Jump to content
Sign in to follow this  
Ktulu789

Monitoring HD read / write activity

Recommended Posts

Ktulu789

Hello, AutoIt forum, it's been a while!

This is my current scenario:

Using nLite I made an unattended WinXP SP2 installation which, on first boot (but before having the user profile completely created) runs a script that, after copying some files from the cd to the hard disk calls another script. As for the profile to be fully created and for Windows to finish booting the first called exe must close.

The second script makes a lot of adjustments to the system and some of them are made in the current user profile so the script fails if files are missing, or not yet created.

For instance, it creates a hot key shortcut for the @ProgramsDir&"Internet explorer.lnk" file.

On slow machines this file takes a while to appear and the function FileCreateShortcut fails if the file doesn't exist.

I originally made a Sleep(60.000) but I want something better, faster and more reliable because, even with such a long sleep it failed sometimes. And as this is run even before the windows taskbar and systray there is no way to pause it sometimes.

My idea is to use WMI and performance monitor looking for the DQ (disk queue) every little while and sleeping(1000) if it is bigger than 0 or 1:

$strComputer = "."
$objWMIService = ObjGet("winmgmts:\\" & $strComputer & "\root\cimv2")
$colClass = $objWMIService.ExecQuery("Select * from Win32_PerfRawData_PerfDisk_PhysicalDisk")

For $objClass in $colClass
MsgBox(0,"","This one: " & $objClass.CurrentDiskQueueLength)
Next

$objWMIService = ""
$colClass = ""
$objClass = ""

After ten consecutive 0 DQs (or 10 secs of calm sea)... Run, Forest, run!!

And my questions are:

1. Does anyone know any other way even more reliable than this one?

2. If I look for ones on the DQ I wonder if the running script will count as part of the queue. But I don't really think so.

3a. As I don't know nothing about anything (and even less about WMI): "Select * from Win32_PerfRawData_PerfDisk_PhysicalDisk"

This line looks up every physical disk (or every instance of w32_perf....) on the machine because of the "*". Or am I wrong?

Edit: No I was wrong: the "*" stands for every dynamic class (Name, CurrentDiskQueueLenght, DiskBytesPersec, etc) inside the given NameSpace (CIMv2). I got this with trial and error, also I found Scriptomatic 2.0 which helped me enumerate all Dynamic classes in my computer.

3b*. How can I change that to only look for the C: or, more properly, the 0 physical drive and not the rest, if any?

I tried to change the For ... In but as objects are not common variables........

Edit: * Forget about 3b. I will monitor all physical HDs, otherwise I will be forced to use HDs only in the Primary master IDE channel.

I will use: $objClass.Name="_Total"

Other work around was to If FileExists() Then but there are many files to check.

While I wait for your generous help I will start implementing this but by now I'm gonna hit the sack as I used most of today's time to search the internetz. At least today I've got a seemingly good solution with WMI!!

There are lots of programs that put a led icon in the systray and if there's any way to get the state of the disk without the "led", say dll, it would be great.

I also came upon some SysInternals Apps but I don't know how to get the output into AutoIt.

Well, thank you in advance, I'm going to sleep now and I will dream about flashing HDD LEDs. :wub::blink::):P

Edited by Ktulu789

AutoIt is a blessing, I don't know how I was able to use my computer before [Auto]It :-S

Share this post


Link to post
Share on other sites
Kerros

I've been thinking about this for a bit now, and I don't know if waiting for the DQ to empty would be the best way. There are going to be times when the DQ in empty, but the files that you are looking for still do not exists, so your back to looking to make sure that the files are there with FileExist().

You could do a post install process, by adding an entry into the registry RunOnceEx. that would run after windows in installed, so you shouldn't need to check for the files.


Kerros===============================================================How to learn scripting: Figure out enough to be dangerous, then ask for assistance.

Share this post


Link to post
Share on other sites
Ktulu789

@Kerros: Thanks for the reply! I thought about that and I'll keep the If FileExists() function as I have it right now (it checks ten files). But the DQ will have to be zero for 10 seconds, for the If FileExists() to be run. Kind of a double check.

I think that ten seconds of inactivity are enough and I'll check every 500ms, but maybe I will make it be 15 secs.

If I don't misunderstand you the RunOnce will be run the next time I reboot the box.

But on fast machines I could forget about the disk queue and even the existence of the target files, with a simple Sleep(15.000) would be more than enough.

On slow machines it means waiting for the system to finish loading (manually), rebooting (manually), waiting for shutdown and bootup (manually :P and longer than everything else :) ) and praying for it to work anyways!

Too many manual steps, wich make it easier to put a MsgBox asking the user to wait for the HDD LED to calm down.

Or even put a two minute Sleep wich should be enough for even the crappiest computer. If that's not enough instead of making it boot I would wear boots and start kicking that XTra slow box (Xnail/Turtle).

Thank you for the reply!!


AutoIt is a blessing, I don't know how I was able to use my computer before [Auto]It :-S

Share this post


Link to post
Share on other sites
Ktulu789

Well, well, this is what I got so far:

HotKeySet("^+q","Quit")
Func Quit()
 MsgBox(0,"Disk idle",TimerDiff($Begin)&" ms");Tell me how much time since last HD activity
 $objWMIService = ""
 $colClass = ""
 $objClass = ""
 Exit
EndFunc
Global $Runs,$TimerStarted,$objWMIService = ObjGet("winmgmts:\\.\root\cimv2");removed $strComputer="." which seems to be "localhost"
While 1
 Sleep(10)
 $colClass = $objWMIService.ExecQuery("Select * from Win32_PerfRawData_PerfDisk_PhysicalDisk")
 If IsObj($colClass) Then
  For $objClass In $colClass;Don't like this
   If $objClass.Name="_Total" Then $TotalQueue=$objClass.CurrentDiskQueueLength
  Next
  Select
   Case $Runs=320;around 5 secs on my PC
    Quit()
   Case $TotalQueue>0
    Global $Runs=0,$TimerStarted=0
    ToolTip("LED")
   Case $TotalQueue=0
    If $TimerStarted=0 Then $Begin=TimerInit()
    Global $Runs+=1,$TimerStarted=1
  EndSelect
 Else;This didn't happen yet
  MsgBox(0,"Error","There was an error with performance monitor, continue when the HDD's LED stops flashing.")
 EndIf
WEnd

On my PC it's run three times: First HD, Second HD and _Total. Average PCs have only one disk but still the script will check that twice. So if I want to ExecQuery as much as posible I lose time checking the disks alone when I only need _Total.

Any way to skip the For loop to go just after _Total?

The sleep has to be around 10 ms or else the processor suffers and remember that this is intended to be run on slow computers which maybe it's Achilles' heel.


AutoIt is a blessing, I don't know how I was able to use my computer before [Auto]It :-S

Share this post


Link to post
Share on other sites
rover

$colClass = $objWMIService.ExecQuery("SELECT * FROM Win32_PerfRawData_PerfDisk_PhysicalDisk WHERE Name= '_Total'", "WQL")


I see fascists...

Share this post


Link to post
Share on other sites
Ktulu789

Monitor HDs "activity" 0.99b:

Global $Runs,$TimerStarted,$WMIObj=ObjGet("WinMgmts:\\.\Root\CIMv2")
While $Runs<>320;320 around 5 secs on my PC
 Sleep(10)
 $colClass=$WMIObj.ExecQuery("Select CurrentDiskQueueLength From Win32_PerfRawData_PerfDisk_PhysicalDisk Where Name='_Total'")
 If IsObj($colClass) Then
  For $objClass In $colClass;Any other way to do this?
   $TotalQueue=$objClass.CurrentDiskQueueLength
  Next
  If $TotalQueue=0 Then
   If $TimerStarted=0 Then Global $Begin=TimerInit(),$TimerStarted=1
   $Runs+=1
  Else
   Global $Runs=0,$TimerStarted=0
   ToolTip("LED")
  EndIf
 Else
  MsgBox(0,"Error","There was an error with performance monitor, continue when the HDD's LED stops flashing.")
  ExitLoop()
 EndIf
WEnd
Run("TheOtherScript.exe");when the HD is more idle, remember: more $Runs means more checks and less probability of false positives
Quit()
Func Quit()
 ConsoleWrite(TimerDiff($Begin)&" ms"&@CRLF);Tell me how much time since last HD activity
 $WMIObj=""
 $colClass=""
 $objClass=""
 Exit
EndFuncoÝ÷ Û÷b½g¥ØZ¶+)­ëmÊkZ´×"ËbW(Zv¢ëmáh¬Í{HÂÝz¸¶«z+²+µêðk$ájy,¶h½êâ¶Z(¦Ê'*'qæî¶Ø^Ç¢¶ÇÊÞvéÝj{m¢g­¢¯zÚ0®+^¶¢²'5ìm)Þ°çßNbq*&zØb·m«aj^ç]º¸§×¬¶)àz¹¦Z§¶+â·+ay¦è½ë®*m®éì~=ói¬-(­ßÛ/z¼¬µæÈ׶'£hÂkíëbì²)ïzË^v)íå!¯&§uû§rب©b·+("Ú(­jYh~+ZºÚ"µÍÛØ[   ÌÍÔ[Ë   ÌÍÕ[YÝYÚ[H ÌÍÔ[ÉÉÝÌÌÌÌÝ[
HÙXÜÈÛ^HÂÛY
L
BY  ÌÍÕ[YÝYL[ÛØ[  ÌÍÐYÚ[U[Y[]

K   ÌÍÕ[YÝYLB   ÌÍÔ[ÊÏLBÑ[ÛÛÛÛUÜ]J[YY    ÌÍÐYÚ[I[É][ÝÈÉ][ÝÉ[ÐÔNÕ[YHÝÈ]XÚ[YHÚ[ÙHÝXÝ]]

The weird thing about this is that this last (shorter, SIMPLER) one takes mostly 4984 ms (sometimes it goes to 5000).

What the heck??? :)

Partly it's because WMI works with pointers but even considering that this doesn't make sense to me, some computation is done anyway: as if I write ObjGet("WinMgmts:\\.\Root\Ktulu") the operation will fail.

Well, if the first little script results useful to you, say hello!!

If you know what the heck is going on with the second script, PLEASE REPLY!!


AutoIt is a blessing, I don't know how I was able to use my computer before [Auto]It :-S

Share this post


Link to post
Share on other sites
rover

was just about to post 'Select CurrentDiskQueueLength' instead of * :)

as for timing from your example:

with sleep(10) - 3570.47613593348 ms

without sleep anywhere from 50ms to 130ms

GUIGetMsg() in place of sleep - 2510.06203302375 ms

of course timing varies as others have commented

on sleep and timer accuracy on the forum

Global $objWMIService = ObjGet("winmgmts:\\.\root\cimv2")
Global $oMyError = ObjEvent("AutoIt.Error","COMErrFunc")

While 1
    Sleep(10)
    $colClass = $objWMIService.ExecQuery("SELECT CurrentDiskQueueLength FROM Win32_PerfRawData_PerfDisk_PhysicalDisk WHERE Name= '_Total'", "WQL", 48)
    If IsObj($colClass) Then
        For $objClass In $colClass
            $TotalQueue = $objClass.CurrentDiskQueueLength
            If $TotalQueue Then Beep(1000,5)
            ConsoleWrite($TotalQueue&@LF)
        Next
    EndIf
WEnd

Func COMErrFunc();COM error handler
  $HexNumber=hex($oMyError.number,8)
  Return
  Msgbox(0,"COM Error Test","We intercepted a COM Error !"       & @CRLF  & @CRLF & _
             "err.description is: "    & @TAB & $oMyError.description    & @CRLF & _
             "err.windescription:"     & @TAB & $oMyError.windescription & @CRLF & _
             "err.number is: "         & @TAB & $HexNumber              & @CRLF & _
             "err.lastdllerror is: "   & @TAB & $oMyError.lastdllerror   & @CRLF & _
             "err.scriptline is: "     & @TAB & $oMyError.scriptline     & @CRLF & _
             "err.source is: "         & @TAB & $oMyError.source         & @CRLF & _
             "err.helpfile is: "       & @TAB & $oMyError.helpfile       & @CRLF & _
             "err.helpcontext is: "    & @TAB & $oMyError.helpcontext _
            )
  SetError(1)  ; to check for after this function returns
Endfunc
Edited by rover

I see fascists...

Share this post


Link to post
Share on other sites
Ktulu789

was just about to post 'Select CurrentDiskQueueLength' instead of * :wub:

Thank you!!

The For loop is redundant as the only object in the collection with that line is CurrentDiskQueueLength

testing with scriptomatic example for 'Win32_PerfRawData_PerfDisk_PhysicalDisk'

shows no other content than CurrentDiskQueueLength

But there is no other way than the For... In...

Well done with the geiger counter, although that the Beep() pauses the script you used it as short as possible! :P

as for timing from your example:

with sleep(10) - 3570.47613593348 ms

without sleep anywhere from 50ms to 130ms

GUIGetMsg() in place of sleep - 2510.06203302375 ms

of course timing varies as others have commented

on sleep and timer accuracy on the forum

Your computer is definitively faster than mine (Athlon64 3500, 1Gb 667), so the script loops faster there, that's why IMO you get shorter times.

Without Sleep() 130 ms???? :) No way! It takes 1,2 seconds here.

GuiGetMsg() the point for Sleep(10) is not to take much processor time. When not using Sleep() Task manager shows 100% use. GUIGetMsg() instead of sleep... Good idea but remember that this has to work on slow machines too.

It's a matter of balance, the longer the sleep() the more the script misses HD activity, the shorter, you need more processor power.

The key aspects to me are:

Accurate= low level of false positives (will be enforced with more loops, 320 is just for the example)

Light= low processor time

Fast= not counting the Sleep() but the rest of the script. Seems that it can't get more improvements now.

When you comment about timer accuracy... do you refer to Timer init?

It starts to make sense, then, the augment of 2 ms on my second simpler script.

About the COMErrFunc(): I thought that with If IsObj($colClass) was enough, anyway I didn't saw that "If" fail yet.

Adding an icon and changing it with TraySetIcon() your example can be turned into a HD tray LED :P which is something I will do for fun in the near future :blink: .


AutoIt is a blessing, I don't know how I was able to use my computer before [Auto]It :-S

Share this post


Link to post
Share on other sites
Ktulu789

Error handler: what kind of errors does it handle?

All of the following make the script crash:

  • The target computer doesn't exists.
  • ObjGet("winmgmts:\\.\root\cimv") (wrong NameSpace)
  • $colClass = $objWMIService.ExecQuery("SELECT CurrentDiskQueueLengths FROM Win32_PerfRawData_PerfDisk_PhysicalDisk WHERE Name= '_Total'", "WQL", 48) (wrong ClassName)
  • $TotalQueue = $objClass.AvgDiskBytesPerTransfer (Not included in the ExecQuery)
The IsObj() after the ExecQuery doesn't work anyway so I will remove that If completely.

This is my first script thingie with COM and I don't know how to perform a basic error checking :D . Any example would be very appreciated! TIA!

Edit: This is gonna be more useful when run on slow computers (at startup) and I wish to check, if possible, things like:

  • Did WMI respond?
  • Is WMI functionality loaded yet (working).
Edited by Ktulu789

AutoIt is a blessing, I don't know how I was able to use my computer before [Auto]It :-S

Share this post


Link to post
Share on other sites
Ktulu789

Well, I still wonder about some way of error checking (Did WMI respond? Is WMI functionality loaded yet?) but this is the latest look of the function:

WaitHDsIdle()
Func WaitHDsIdle()
 Local $Runs,$TimerStarted,$WMIObj=ObjGet("WinMgmts:\\.\Root\CIMv2")
 While $Runs<1000;320 around 5 secs on my PC
  Sleep(10)
  $ObjCollection=$WMIObj.ExecQuery("Select CurrentDiskQueueLength From Win32_PerfRawData_PerfDisk_PhysicalDisk Where Name='_Total'")
  For $EnumObj In $ObjCollection
   $TotalQueue=$EnumObj.CurrentDiskQueueLength
  Next
  If $TotalQueue=0 Then
   If $TimerStarted=0 Then $TimerStarted=TimerInit()
   $Runs+=1
  Else
   Local $Runs=0,$TimerStarted=0
   Beep(1000,5)
   ToolTip("LED")
  EndIf
 WEnd
 Local $EnumObj="",$ObjCollection="",$WMIObj="",$TimerEnd=TimerDiff($TimerStarted)&" ms"
 Beep(880,250)
EndFunc

$TimerEnd= ~15600 ms on my PC.


AutoIt is a blessing, I don't know how I was able to use my computer before [Auto]It :-S

Share this post


Link to post
Share on other sites
Ktulu789

Tested on a freshly installed XP SP2.

As I said before this is intended to run on first system startup after the unattended installation finishes:

I got the following error:

AutoIt Error

Line -1:

Error: Variable used without being declared.

"Line -1" when I saw that, I went ;)

This happened on a VMware virtual machine so I took a snapshot and tested a little.

I run the compiled script many times in the VM with the same error, once on my computer and it worked alright (it was the same file, :P damn it).

After various minutes after the VM booted up the script started working perfectly.

So I think that maybe it's a problem with WMI. Maybe not all WMI functionality is loaded the first time or perhaps the performance monitor is not loaded either.

Remarks on "the first time" because I tested the function with my PC rebooting many times and it never failed.

Now I really need error checking :D .


AutoIt is a blessing, I don't know how I was able to use my computer before [Auto]It :-S

Share this post


Link to post
Share on other sites
Hagaren76

Hello, after more than 2 years of using AutoIt and reading this forum, my first post, better late than never :blink:

So, needed to detect idle time after windows booting up, saw this post, decided to modify code,

hope somebody will find it usefull.

ps.

Yes, it has error checking ;)

Opt("MustDeclareVars", 1)


Global $WHDI_InstallComErrorHandler = True, $WHDI_ComError = False
Global $ObjWHDI_ComError


; --------------- This part for testing purposes only. --------------- 
Global $WHDI_FnResult
$WHDI_FnResult = WaitHDDsIdle(5000, 1000)
MsgBox(0, "WaitHDDsIdle result:", $WHDI_FnResult & " @error: " & @error)
; -------------------------------------------------------------------- 



; Waits $MaxTimeToWaitInMs for all HDDs in machine to be idle for $IdleTimeInMs.
; Minimum value (for both) is 500.
; Returns elapsed time in ms on success.
; On failure, sets @error to:
; 1 - failed to install WaitHDDsIdle_COMErrorHandler, because another function already exists. Returns 0.
; 2 - failed to get WMI object. Returns 0.
; 3 - COM error. Returns hex number of COM error.
; 4 - $MaxTimeToWaitInMs elapsed (HDDs was not idle for $IdleTimeInMs). Returns 0.
; $Precision: value from 1-10, 5 by default. 1 is most precise. 10 is for slower computers (not tested on slower machines, did not detect cpu usage increase, but let it be).
; If $WHDI_InstallComErrorHandler is true, we install error handler to intercept COM error,
; but, this is not mandatory for function to work. If you dont use autoit com error handler function,
; do enable this (it is enabled by default).
Func WaitHDDsIdle($MaxTimeToWaitInMs, $IdleTimeInMs, $Precision = 5)
    Local $PrecisionCounter = 0
    Local $MaxTimeStartInMs, $IdleTimeStartInMs, $IdleTimerStarted = False, $DoStartIdleTimer = False
    Local $WMIObj, $ObjCollection, $EnumObj, $TotalQueue

    If $Precision < 1 Or $Precision > 10 Then $Precision = 5
    If $MaxTimeToWaitInMs < 500 Then $MaxTimeToWaitInMs = 500
    If $IdleTimeInMs < 500 Then $IdleTimeInMs = 500

    If $WHDI_InstallComErrorHandler Then
        If Not Install_WaitHDDsIdle_COMErrorHandler() Then
            SetError(1)
            Return 0
        EndIf
    EndIf

    $MaxTimeStartInMs = TimerInit()
    
    $WMIObj = ObjGet("WinMgmts:\\.\Root\CIMv2")
    If @error Then
        SetError(2)
        Return 0
    EndIf
    
    While 1
        Sleep(10)
        $ObjCollection = $WMIObj.ExecQuery("Select CurrentDiskQueueLength From Win32_PerfRawData_PerfDisk_PhysicalDisk Where Name='_Total'")
        If $WHDI_ComError Then
            $WHDI_ComError = False
            SetError(3)
            Return "0x" & Hex($ObjWHDI_ComError.number, 8)
        EndIf
        For $EnumObj In $ObjCollection
            $TotalQueue = $EnumObj.CurrentDiskQueueLength
            If $WHDI_ComError Then
                $WHDI_ComError = False
                SetError(3)
                Return "0x" & Hex($ObjWHDI_ComError.number, 8)
            EndIf
        Next
        If $TotalQueue = 0 Then
            If Not $IdleTimerStarted Then $DoStartIdleTimer = True
        Else
            $IdleTimerStarted = False
        EndIf
        $PrecisionCounter += 1
        If $Precision = $PrecisionCounter Then
            If $MaxTimeToWaitInMs < TimerDiff($MaxTimeStartInMs) Then
                SetError(4)
                Return 0
            EndIf
            If $DoStartIdleTimer Then
                $IdleTimeStartInMs = TimerInit()
                $IdleTimerStarted = True
                $DoStartIdleTimer = False
            EndIf
            If $IdleTimerStarted And ($IdleTimeInMs < TimerDiff($IdleTimeStartInMs)) Then ExitLoop
            $PrecisionCounter = 0
        EndIf
    WEnd

    ; $IdleTimeInMs reached.
    $WMIObj = 0
    $ObjCollection = 0
    $EnumObj = 0
    $TotalQueue = 0
    $ObjWHDI_ComError = 0
    Return TimerDiff($MaxTimeStartInMs)
EndFunc



; Internal function.
Func WaitHDDsIdle_COMErrorHandler()
    $WHDI_ComError = True
EndFunc



; Internal function.
; Returns 1 if successfully installed error handler,
; or 0 if error handler function already exists.
; From AutoIt help: You can only have ONE Error Event Handler active per AutoIt script.
Func Install_WaitHDDsIdle_COMErrorHandler()
    Local $ErrFuncName = ObjEvent("AutoIt.Error")
    If $ErrFuncName <> "" Then Return 0
    $ObjWHDI_ComError = ObjEvent("AutoIt.Error", "WaitHDDsIdle_COMErrorHandler")
    Return 1
EndFunc
Edited by Hagaren76

Share this post


Link to post
Share on other sites
Ascend4nt

Thanks Hagaren76 for adding that function and making me aware of this thread. I've created a PDH version for my Performance Counters UDF here. :blink:

Share this post


Link to post
Share on other sites
Hagaren76

Bug correction:

- $ObjWHDI_ComError object with AutoIt COM error handler was not destroyed after exiting function when there was error detected.

Opt("MustDeclareVars", 1)


Global $WHDI_InstallComErrorHandler = True, $WHDI_ComError = False
Global $ObjWHDI_ComError


; --------------- This part for testing purposes only. --------------- 
Global $WHDI_FnResult
$WHDI_FnResult = WaitHDDsIdle(120000, 3000)
MsgBox(0, "WaitHDDsIdle result:", $WHDI_FnResult & " @error: " & @error)
; -------------------------------------------------------------------- 




; Waits $MaxTimeToWaitInMs for all HDDs in machine to be idle for $IdleTimeInMs.
; Minimum value (for both) is 500.
; Returns elapsed time in ms on success.
; On failure, sets @error to:
; 1 - failed to install WaitHDDsIdle_COMErrorHandler, because another function already exists. Returns 0.
; 2 - failed to get WMI object. Returns 0.
; 3 - COM error. Returns hex number of COM error.
; 4 - $MaxTimeToWaitInMs elapsed (HDDs was not idle for $IdleTimeInMs). Returns 0.
; $Precision: value from 1-10, 5 by default. 1 is most precise. 10 is for slower computers (not tested on slower machines, did not detect cpu usage increase, but let it be).
; If $WHDI_InstallComErrorHandler is true, we install error handler to intercept COM error,
; but, this is not mandatory for function to work. If you dont use autoit com error handler function,
; do enable this (it is enabled by default).
Func WaitHDDsIdle($MaxTimeToWaitInMs, $IdleTimeInMs, $Precision = 5)
    Local $PrecisionCounter = 0
    Local $MaxTimeStartInMs, $IdleTimeStartInMs, $IdleTimerStarted = False, $DoStartIdleTimer = False
    Local $WMIObj, $ObjCollection, $EnumObj, $TotalQueue, $ErrStr, $ErrNum = 0

    If $Precision < 1 Or $Precision > 10 Then $Precision = 5
    If $MaxTimeToWaitInMs < 500 Then $MaxTimeToWaitInMs = 500
    If $IdleTimeInMs < 500 Then $IdleTimeInMs = 500

    If $WHDI_InstallComErrorHandler Then
        If Not Install_WaitHDDsIdle_COMErrorHandler() Then
            SetError(1)
            Return 0
        EndIf
    EndIf

    $MaxTimeStartInMs = TimerInit()
    
    $WMIObj = ObjGet("WinMgmts:\\.\Root\CIMv2")
    If @error Then
        SetError(2)
        $WHDI_ComError = False
        $ObjWHDI_ComError = 0
        Return 0
    EndIf
    
    While 1
        Sleep(10)
        $ObjCollection = $WMIObj.ExecQuery("Select CurrentDiskQueueLength From Win32_PerfRawData_PerfDisk_PhysicalDisk Where Name='_Total'")
        If $WHDI_ComError Then
            $WHDI_ComError = False
            $ErrNum = 3
            ExitLoop
        EndIf
        For $EnumObj In $ObjCollection
            $TotalQueue = $EnumObj.CurrentDiskQueueLength
            If $WHDI_ComError Then
                $WHDI_ComError = False
                $ErrNum = 3
                ExitLoop
            EndIf
        Next
        If $TotalQueue = 0 Then
            If Not $IdleTimerStarted Then $DoStartIdleTimer = True
        Else
            $IdleTimerStarted = False
        EndIf
        $PrecisionCounter += 1
        If $Precision = $PrecisionCounter Then
            If $MaxTimeToWaitInMs < TimerDiff($MaxTimeStartInMs) Then
                $ErrNum = 4
                ExitLoop
            EndIf
            If $DoStartIdleTimer Then
                $IdleTimeStartInMs = TimerInit()
                $IdleTimerStarted = True
                $DoStartIdleTimer = False
            EndIf
            If $IdleTimerStarted And ($IdleTimeInMs < TimerDiff($IdleTimeStartInMs)) Then ExitLoop
            $PrecisionCounter = 0
        EndIf
    WEnd

    ; Was there errors in the loop?
    Switch $ErrNum
    Case 0
        $ObjWHDI_ComError = 0
        Return TimerDiff($MaxTimeStartInMs)
    Case 3
        SetError(3)
        $ErrStr = "0x" & Hex($ObjWHDI_ComError.number, 8)
        $ObjWHDI_ComError = 0
        Return $ErrStr
    Case 4
        SetError(4)
        $ObjWHDI_ComError = 0
        Return 0
    EndSwitch
EndFunc



; Internal function.
Func WaitHDDsIdle_COMErrorHandler()
    $WHDI_ComError = True
EndFunc



; Internal function.
; Returns 1 if successfully installed error handler,
; or 0 if error handler function already exists.
; From AutoIt help: You can only have ONE Error Event Handler active per AutoIt script.
Func Install_WaitHDDsIdle_COMErrorHandler()
    Local $ErrFuncName = ObjEvent("AutoIt.Error")
    If $ErrFuncName <> "" Then Return 0
    $ObjWHDI_ComError = ObjEvent("AutoIt.Error", "WaitHDDsIdle_COMErrorHandler")
    Return 1
EndFunc

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×