Jump to content

[NOW WORKING] a (broken) monitor file changes script which uses ReadDirectoryChangeW


Zomp
 Share

Recommended Posts

i've done that...

i'm talking about an array with multiple of this struct....

-- Arck System _ Soon -- Ideas make everything

"La critique est facile, l'art est difficile"

Projects :

[list] [*]Au3Service : Run your exe as service V3 / Updated 29/07/2013 Get it Here [/list]
Link to comment
Share on other sites

  • Replies 63
  • Created
  • Last Reply

Top Posters In This Topic

Top Posters In This Topic

i've done that...

i'm talking about an array with multiple of this struct....

IMVNO (In My Very Newbie Opinion), I thought to use an array of 4*n Uint values, so:

$FNI=DllStructCreate("Uint  FNI[10000]")

Then, with entry (i is a multiple of four):

- the Uint value in position is about NextEntry

- the Uint value in posiiton +1 (4 bytes) is about Action

- the Uint value in position +2 (8 bytes) is about FileName len

- the Uint value in position +3 (12 bytes) is about FileName pointer

I have simply copied this idea from AHK forum scripts.

Edited by Zomp
Link to comment
Share on other sites

1- why thread ? thread not needed here

2- your filenotifystructure are wrong, as the createfile call

3- you need a buffer to get all the changes (i'm blocked here)

4- you don't get what type of event occured (creation, deletion, and so on)

1- I have tried more than one script, so I'm not sure what you relate to

2- Yes, FNI structure is wrong, but the script does not enter in the decode subroutine, so it seems to me that this error is unimportant. Can you tell me why my Createfile call is wrong?

3- See my following post

4- Yes, because I realized that there is an error in my script which prevents to enter the decode subroutine (the read of $nreadlen in first version)

Edited by Zomp
Link to comment
Share on other sites

i've done that...

i'm talking about an array with multiple of this struct....

Perhaps...

#include <memory.au3>
Global $tag_FILE_NOTIFY_INFORMATION = "dword NextEntryOffset;dword Action;dword FileNameLength;str FileName;"

$tFNI = DllStructCreate($tag_FILE_NOTIFY_INFORMATION)
$hBuffer = _MemGlobalAlloc(DllStructSize($tFNI)*1024)
$ptrBuffer = _MemGlobalLock($hBuffer)
$iBufferSize = _MemGlobalSize($hBuffer)
$tFNI = 0
Edited by zorphnog
Link to comment
Share on other sites

I don't have time to play with this right now, but here's some guidelines -

when coding your script, always check the return values from DllCalls with ConsoleWrite (or even MsgBox as you are tring to do now), so you can see if it the called function actually executes properly. Checking @error in this case is only useful for detecting script syntax errors.

DllCallBack udf is not necessary as AutoIt supports this feature internally with DllCallbackRegister etc., unless you are using old version of AutoIt.

Also there are some obvious mistakes, such as:

you create $FNI=DllStructCreate("char FNI[10000]")

and then use

$SizeOfFNI=0x10000

which is not true.

all that code with building strings of "00" and trying to DllStructSetData with them is not necessary even if you would be doing it correctly (which you aren't), because AutoIt structs are filled with zeros by default. What you are doing right now is creating structs of actual strings of "0" (binary 0x30), not binary zeros.

Read helpfile, DllStruct..., and the types it supports.

Edited by Siao

"be smart, drink your wine"

Link to comment
Share on other sites

#include <memory.au3>

Global $tag_FILE_NOTIFY_INFORMATION = "dword NextEntryOffset;dword Action;dword FileNameLength;str FileName;"

$tFNI = DllStructCreate($tag_FILE_NOTIFY_INFORMATION)

$hBuffer = _MemGlobalAlloc(DllStructSize($tFNI)*1024)

$ptrBuffer = _MemGlobalLock($hBuffer)

$iBufferSize = _MemGlobalSize($hBuffer)

$tFNI = 0

Ok will test it when i'll be at work

thx

if you can give me the solution on how to get the array after

@Siao : I've redone the script of Zomb, but i'm still blocked with the buffer

i've seen some other post of you but i didn't understand how to do it at all

i'm thinking about _Mem_movememory but not sure

if you can help me on this...

-- Arck System _ Soon -- Ideas make everything

"La critique est facile, l'art est difficile"

Projects :

[list] [*]Au3Service : Run your exe as service V3 / Updated 29/07/2013 Get it Here [/list]
Link to comment
Share on other sites

Ok will test it when i'll be at work

thx

if you can give me the solution on how to get the array after

@Siao : I've redone the script of Zomb, but i'm still blocked with the buffer

i've seen some other post of you but i didn't understand how to do it at all

i'm thinking about _Mem_movememory but not sure

if you can help me on this...

Any progress in your script?

Link to comment
Share on other sites

yep it works perfectly

thx for Siao for ther solution of the buffer

_MonitorDirAsync("d:\\vmware")
#cs
    $HandleFindFirst = _WINAPI_FindFirstChangeNotification("d:\\vmware",true,$FILE_NOTIFY_CHANGE_LAST_WRITE)
    ConsoleWrite($HandleFindFirst[0] & @crlf)
    ;Exit
    $HandleFindFirst = $HandleFindFirst[0]
    _WINAPI_WaitForSingleObject($HandleFindFirst);
    dim $HandleFileNext
    while 1
    _WinAPI_FindNextChangeNotification($HandleFindFirst)
    _WINAPI_WaitForSingleObject($HandleFindFirst);
    ConsoleWrite("event occured" & @crlf)
    WEnd
    
    
    
    func _WINAPI_FindFirstChangeNotification($lpPathName,$bWatchSubtree,$dwNotifyFilter)
    ;   HANDLE WINAPI FindFirstChangeNotification(
    ;  __in  LPCTSTR lpPathName,
    ;  __in  BOOL bWatchSubtree,
    ;  __in  DWORD dwNotifyFilter
    
    $Handle = dllcall($Kernel32Dll,"hwnd","FindFirstChangeNotification","str",$lpPathName,"int",$bWatchSubTree,"dword",$dwNotifyFilter)
    if $Handle = 0 then ConsoleWrite(_WinAPI_GetLastErrorMessage() & @crlf)
    return $Handle
    
    EndFunc
    
    func _WinAPI_FindNextChangeNotification($filehandle)
    $HandleFileNext = dllcall($Kernel32Dll,"hwnd","FindNextChangeNotification","hwnd",$filehandle)
    return $HandleFileNext
    EndFunc
    
    func _WINAPI_ReadDirectoryChanges()
    ;Original Code from zomp, fixed by Arcker.
    consolewrite("RDC" & @CRLF)
    
    ;   http://msdn2.microsoft.com/en-us/library/aa365465.aspx
    
    $RDC =DllCall( _
    "kernel32.dll","Int","ReadDirectoryChangesW", _
    "UInt" , $hDir, _
    "UInt" , $PointerFNI, _
    "UInt" , $SizeOfFNI, _
    "UInt" , $WatchSubDirs, _
    "UInt" , $FILE_NOTIFY_CHANGE_FILE_NAME _
    +$FILE_NOTIFY_CHANGE_DIR_NAME _
    +$FILE_NOTIFY_CHANGE_ATTRIBUTES _
    +$FILE_NOTIFY_CHANGE_SIZE _
    +$FILE_NOTIFY_CHANGE_LAST_WRITE _
    +$FILE_NOTIFY_CHANGE_LAST_ACCESS _
    +$FILE_NOTIFY_CHANGE_CREATION _
    +$FILE_NOTIFY_CHANGE_SECURITY, _
    "UInt*", $nReadLen, _
    "UInt" , 0, _
    "UInt" , 0  _
    )
    
    if not @error=0 then msgbox(0,"ReadDirectoryChangesW",@error)
    
    Return $RDC
    EndFunc
#ce
Func _MonitorDir($sdir)
    $hDir = DllCall($Kernel32Dll, "hwnd", "CreateFile", _
            "Str", $sdir, _
            "Int", $FILE_LIST_DIRECTORY, _
            "Int", BitOR($FILE_SHARE_READ, $FILE_SHARE_DELETE, $FILE_SHARE_WRITE), _
            "ptr", 0, _
            "int", $OPEN_EXISTING, _
            "int", $FILE_FLAG_BACKUP_SEMANTICS, _
            "int", 0 _
            )
    $hDir = $hDir[0]
    ;ConsoleWrite("+ " & $hDir & @CRLF)
    $tBuffer = DllStructCreate("byte[4096]")
    $pBuffer = DllStructGetPtr($tBuffer)
    $iBufferSize = DllStructGetSize($tBuffer)
    $tFNI = 0
    
    ;_MemGlobalUnlock(
    
    $tReadLen = DllStructCreate("dword ReadLen")
    Dim $BytesReturned
    While 1
        Local $ret = DllCall($Kernel32Dll, "Int", "ReadDirectoryChangesW", _
                "hwnd", $hDir, _
                "ptr", $pBuffer, _
                "dword", $iBufferSize, _
                "int", True, _ ; <== That's so cool !!!!!!
                "dword", BitOR($FILE_NOTIFY_CHANGE_FILE_NAME _
                , $FILE_NOTIFY_CHANGE_DIR_NAME _
                , $FILE_NOTIFY_CHANGE_ATTRIBUTES _
                , $FILE_NOTIFY_CHANGE_SIZE _
                , $FILE_NOTIFY_CHANGE_LAST_WRITE _
                , $FILE_NOTIFY_CHANGE_LAST_ACCESS _
                , $FILE_NOTIFY_CHANGE_CREATION _
                , $FILE_NOTIFY_CHANGE_SECURITY), _
                "dword*", DllStructGetPtr($tReadLen), _
                "ptr", 0, _
                "ptr", 0 _
                )
        ;ConsoleWrite($ret[0] & @CRLF)
        If $ret[0] = 0 Then
            ConsoleWrite(_WinAPI_GetLastErrorMessage() & @CRLF)
            ExitLoop
        EndIf
        Dim $OldName
        $iOffset = 0
        While 1
            $tFNI = DllStructCreate("dword Next;dword Action;dword FilenameLen", $pBuffer + $iOffset)
            ;$iAction = DllStructGetData($tFNI, "Action")
            $tStr = DllStructCreate("wchar[" & DllStructGetData($tFNI, "FilenameLen") / 2 & "]", $pBuffer + $iOffset + 12)
            $Filename = DllStructGetData($tStr, 1)
            ;   ConsoleWrite($sFilename & ", action " & $iAction & @CRLF)
            
            Switch DllStructGetData($tFNI, "Action")
                Case $FILE_ACTION_ADDED
                    ConsoleWrite("+   " & $Filename & @CRLF)
                Case $FILE_ACTION_REMOVED
                    ConsoleWrite("-   " & $Filename & @CRLF)
                Case $FILE_ACTION_MODIFIED
                    ConsoleWrite("<>  " & $Filename & @CRLF)
                Case $FILE_ACTION_RENAMED_OLD_NAME
                    $OldName = $Filename
                    ;ConsoleWrite("The file was renamed and this is the old name." & @CRLF)
                Case $FILE_ACTION_RENAMED_NEW_NAME
                    ConsoleWrite($OldName & "    =>    " & $Filename & @CRLF)
            EndSwitch
            
            
            $iNext = DllStructGetData($tFNI, "Next")
            If $iNext = 0 Then ExitLoop
            $iOffset += $iNext
        WEnd
        
        
    WEnd
EndFunc   ;==>_MonitorDir

As you can see, there is some commented line on the top.

This is another method, but it doesn't give the name of the file.

-- Arck System _ Soon -- Ideas make everything

"La critique est facile, l'art est difficile"

Projects :

[list] [*]Au3Service : Run your exe as service V3 / Updated 29/07/2013 Get it Here [/list]
Link to comment
Share on other sites

Thanks arcker, zomp, and all for this. I had to do a little work to get arcker's latest version working (needed some files opened, and other variables defined), but it monitors files very nicely.

One thing I noticed, though, was while it was monitoring a directory, I couldn't do anything with my taskbar. I couldn't swap tabs, open the start menu, or anything else while it was running. As soon as I killed it (and that was done with CTRL+BREAK in Scite), everything I tried to do all piled through at one time. It might be the way I'm doing it, or just this computer all together, but I was just going to give anyone who fixed it a heads up on how it's been doing to me.

Edit: Just a quick note, I only have the above problem if I try to use the WIN+E shortcut to bring up a new explorer window, otherwise everything runs fine.

Edit2: Also noted, it's not exactly Asynchronous, cause it stops anything else from running while it's going.

Edited by SkinnyWhiteGuy
Link to comment
Share on other sites

hi guy

For me, so far, no problem at all

i've monitored C:\ without any problem, but with asynchronous version (don't ask for it, i'm still working on it, and benchmarking it on a fileserver)

mmm maybe the synchronous version cause this issue.

please tell me :

- what folder do you monitor.

- do you monitor all ? security, size, creation and more ?

i don't understand why this script would stop anything else, because this API "blocks until events"

-- Arck System _ Soon -- Ideas make everything

"La critique est facile, l'art est difficile"

Projects :

[list] [*]Au3Service : Run your exe as service V3 / Updated 29/07/2013 Get it Here [/list]
Link to comment
Share on other sites

I was monitoring a USB drive, just to test it out. It might just have been my work computer, I'll play with it at home later.

I can't wait to see the asynchronous version. I read up on how to do it, but I got lost on the "pick a uniqe event" part. I think I know how to do that, but I'm not sure. I'll wait to see your version, and see if I was correct.

Link to comment
Share on other sites

I was monitoring a USB drive, just to test it out. It might just have been my work computer, I'll play with it at home later.

I can't wait to see the asynchronous version. I read up on how to do it, but I got lost on the "pick a uniqe event" part. I think I know how to do that, but I'm not sure. I'll wait to see your version, and see if I was correct.

Thanks to Arcker for his great script. I have addedd the following lines at the top of his script:

#include <GUIConstants.au3>

Global const $FILE_LIST_DIRECTORY = 0x1, _
   $FILE_FLAG_BACKUP_SEMANTICS = 0x2000000, _
   $FILE_FLAG_OVERLAPPED = 0x40000000

;   $FILE_SHARE_READ = 0x1, _
;   $FILE_SHARE_WRITE = 0x2, _
;   $OPEN_EXISTING = 0x3, _
;   $FILE_SHARE_DELETE = 0x4, _
; are already declared in GUIConstants.au3

Global const _
   $FILE_NOTIFY_CHANGE_FILE_NAME = 0x1, _
   $FILE_NOTIFY_CHANGE_DIR_NAME = 0x2, _
   $FILE_NOTIFY_CHANGE_ATTRIBUTES = 0x4, _
   $FILE_NOTIFY_CHANGE_SIZE = 0x8, _
   $FILE_NOTIFY_CHANGE_LAST_WRITE  = 0x10, _
   $FILE_NOTIFY_CHANGE_LAST_ACCESS = 0x20, _
   $FILE_NOTIFY_CHANGE_CREATION = 0x40, _
   $FILE_NOTIFY_CHANGE_SECURITY = 0x100

Global const _
$FILE_ACTION_ADDED = 0x1, _
$FILE_ACTION_REMOVED = 0x2, _
$FILE_ACTION_MODIFIED = 0x3, _
$FILE_ACTION_RENAMED_OLD_NAME = 0x4, _
$FILE_ACTION_RENAMED_NEW_NAME = 0x5

$kernel32dll="kernel32.dll"
_MonitorDir("c:\temp\")

I'm working on asynchronous version, too. Obviously, since I'm a newbie, it is broken. :D

I have used the following calls, in sequence:

1) CreateFile

2) CreateEvent

3) ReadDirectoryChangeW

4) MsgWaitForMultipleObjectsEx

5) GetOverlappedResult

6) ResetEvent

7) ReadDirectoryChangesW

Calls 4-5-6-7 are inside a loop. The advantage of this approach should be that between two successive calls to ReadDirectoryChangeW one could insert some other operations in the script.

Edited by Zomp
Link to comment
Share on other sites

I post my (working!) asynchronous version of monitor folder script. I hope that can liven this thread.

Every enhancement will be greatly appreciated. For example, a possible improvement is to monitor more than one folder at a time.

But: irrespective of which mode of ReadDirectoryChangeW call is used (asynchronous of synchronous) I realized that a single file modification is notified two or three times. Is any guy able to explain why, and correct such annoying behaviour?

#include <GUIConstants.au3>

Global const $FILE_LIST_DIRECTORY = 0x1, _
   $FILE_FLAG_BACKUP_SEMANTICS = 0x2000000, _
   $FILE_FLAG_OVERLAPPED = 0x40000000

;   $FILE_SHARE_READ = 0x1, _
;   $FILE_SHARE_WRITE = 0x2, _
;   $OPEN_EXISTING = 0x3, _
;   $FILE_SHARE_DELETE = 0x4, _
; are already declared in GUIConstants.au3

Global const _
   $FILE_NOTIFY_CHANGE_FILE_NAME = 0x1, _
   $FILE_NOTIFY_CHANGE_DIR_NAME = 0x2, _
   $FILE_NOTIFY_CHANGE_ATTRIBUTES = 0x4, _
   $FILE_NOTIFY_CHANGE_SIZE = 0x8, _
   $FILE_NOTIFY_CHANGE_LAST_WRITE  = 0x10, _
   $FILE_NOTIFY_CHANGE_LAST_ACCESS = 0x20, _
   $FILE_NOTIFY_CHANGE_CREATION = 0x40, _
   $FILE_NOTIFY_CHANGE_SECURITY = 0x100

Global const _
$FILE_ACTION_ADDED = 0x1, _
$FILE_ACTION_REMOVED = 0x2, _
$FILE_ACTION_MODIFIED = 0x3, _
$FILE_ACTION_RENAMED_OLD_NAME = 0x4, _
$FILE_ACTION_RENAMED_NEW_NAME = 0x5

$kernel32dll="kernel32.dll"

$sdir="c:\temp\temp1\"

    $tBuffer = DllStructCreate("byte[4096]")
    $pBuffer = DllStructGetPtr($tBuffer)
    $iBufferSize = DllStructGetSize($tBuffer)
    $tFNI = 0
   
    ; CreateFile: http://msdn2.microsoft.com/en-us/library/aa914735.aspx
    $hDir = DllCall($Kernel32Dll, "hwnd", "CreateFile", _
            "Str", $sdir, _
            "Int", $FILE_LIST_DIRECTORY, _
            "Int", BitOR($FILE_SHARE_READ, $FILE_SHARE_DELETE, $FILE_SHARE_WRITE), _
            "ptr", 0, _
            "int", $OPEN_EXISTING, _
            "int", $FILE_FLAG_BACKUP_SEMANTICS, _
            "int", 0 _
            )
    $hDir = $hDir[0]
   
    $tReadLen = DllStructCreate("dword ReadLen")
    Dim $BytesReturned
    
    $tOverLapped = DllStructCreate("Uint OL1;Uint OL2; Uint OL3; Uint OL4; hwnd OL5")
    for $i=1 to 5
      DllStructSetData($tOverLapped,$i,0)
      if @error Then
     MsgBox(0,"","Error in DllStructSetData OverLapped " & @error);
     exit
      endif
   Next
    $pOverLapped = DllStructGetPtr($tOverLapped)
    $iOverLappedSize = DllStructGetSize($tOverLapped)
    
    $tDirEvents = DllStructCreate("hwnd DirEvents")
    $pDirEvents = DllStructGetPtr($tDirEvents)
    $iDirEvents = DllStructGetSize($tDirEvents)
    
    $hEvent = DllCall($Kernel32Dll,"hwnd", "CreateEvent", _
           "UInt",0, _
           "Int",true, _
           "Int",false, _
           "UInt",0 )
   dllstructsetdata($tOverLapped,5,$hEvent)
   if @error Then
      MsgBox(0,"","Error in DllStructSetData OverLapped 5" & @error);
      exit
   endif
   DllStructSetData($tDirEvents,1,$hEvent)
   if @error Then
      MsgBox(0,"","Error in DllStructSetData OverLapped 5" & @error);
      exit
   endif

        $ret = DllCall($Kernel32Dll, "Int", "ReadDirectoryChangesW", _
                "hwnd", $hDir, _
                "ptr", $pBuffer, _
                "dword", $iBufferSize, _
                "int", True, _ ; <== That's so cool !!!!!!
                "dword", BitOR($FILE_NOTIFY_CHANGE_FILE_NAME _
                , $FILE_NOTIFY_CHANGE_DIR_NAME _
                , $FILE_NOTIFY_CHANGE_ATTRIBUTES _
                , $FILE_NOTIFY_CHANGE_SIZE _
                , $FILE_NOTIFY_CHANGE_LAST_WRITE _
                , $FILE_NOTIFY_CHANGE_LAST_ACCESS _
                , $FILE_NOTIFY_CHANGE_CREATION _
                , $FILE_NOTIFY_CHANGE_SECURITY), _
                "Uint", 0, _
                "Uint", $pOverLapped, _
                "Uint", 0 _
                )
while 1
    $r = DllCall($Kernel32Dll,"dword","MsgWaitForMultipleObjectsEx", _
     "UInt", 1, _
     "Uint", $pDirEvents, _
     "UInt", -1, _
     "UInt", 0x4FF, _
     "UInt", 0x6)
     
      if $r=0 then 
       
;   consolewrite(" an event occurred " & $r & @CRLF)
     $iOffset = 0
        
     $nReadLen = 0
        
     DllCall($Kernel32Dll,"Uint", "GetOverlappedResult", _
    "hWnd", $hDir, _
    "Uint", $pOverLapped, _
    "UInt*", $nReadLen, _
    "Int", true )

        While 1
            $tFNI = DllStructCreate("dword Next;dword Action;dword FilenameLen", $pBuffer + $iOffset)
            $tStr = DllStructCreate("wchar[" & DllStructGetData($tFNI, "FilenameLen") / 2 & "]", $pBuffer + $iOffset + 12)
            $Filename = DllStructGetData($tStr, 1)
            ;   ConsoleWrite($sFilename & ", action " & $iAction & @CRLF)
           
            Switch DllStructGetData($tFNI, "Action")
                Case $FILE_ACTION_ADDED
                    ConsoleWrite("+   " & $Filename & @CRLF)
                Case $FILE_ACTION_REMOVED
                    ConsoleWrite("-   " & $Filename & @CRLF)
                Case $FILE_ACTION_MODIFIED
                    ConsoleWrite("<>  " & $Filename & @CRLF)
                Case $FILE_ACTION_RENAMED_OLD_NAME
                    $OldName = $Filename
                    ;ConsoleWrite("The file was renamed and this is the old name." & @CRLF)
                Case $FILE_ACTION_RENAMED_NEW_NAME
                    ConsoleWrite($OldName & "    =>    " & $Filename & @CRLF)
            EndSwitch
           
            $iNext = DllStructGetData($tFNI, "Next")
            If $iNext = 0 Then ExitLoop
            $iOffset += $iNext
        WEnd
       
            $ff = DllStructGetData($tOverLapped, 5)
         if @error Then
           MsgBox(0,"","Error in DllStructGetData OverLapped 5" & @error);
           exit
        endif
  
        DllCall($Kernel32dll, "Uint","ResetEvent", _
    "UInt",$ff )
 
$ret = DllCall($Kernel32Dll, "Int", "ReadDirectoryChangesW", _
                "hwnd", $hDir, _
                "ptr", $pBuffer, _
                "dword", $iBufferSize, _
                "int", True, _ ; <== That's so cool !!!!!!
                "dword", BitOR($FILE_NOTIFY_CHANGE_FILE_NAME _
                , $FILE_NOTIFY_CHANGE_DIR_NAME _
                , $FILE_NOTIFY_CHANGE_ATTRIBUTES _
                , $FILE_NOTIFY_CHANGE_SIZE _
                , $FILE_NOTIFY_CHANGE_LAST_WRITE _
                , $FILE_NOTIFY_CHANGE_LAST_ACCESS _
                , $FILE_NOTIFY_CHANGE_CREATION _
                , $FILE_NOTIFY_CHANGE_SECURITY), _
                "Uint", 0, _
                "Uint", $pOverLapped, _
                "Uint", 0 _
                )

 endif
wend
Edited by Zomp
Link to comment
Share on other sites

Thanks for this, I have been playing with it a lot. I have a reason for you seeing multiple file changes: the file is updated for multiple things. One change is the actual content change, another is the time stamp, and a third could be the attributes, because all can trigger an event like that. As far as discovering which event that was, I'm not too sure right now.

Edit: Upon further testing, I discovered something: Your example is actually broken. It only works by coincidence. Your call to MsgWaitForMultipleObjectsEX was failing (you forgot to set the structures to using $hEvent[0] instead of $hEvent), and you weren't looking at it's true return value ($r[0]), but since it was erroring, it was producing 0 for $r, so it kept going till it hit GetOverlappedResults, which you put as not returning until something came through (hence why it was locking me up), so it waited till it returned, which it would when it saw something changed. Also, MsgWaitForMultipleObjectsEX is defined in User32.dll, not Kernel32.dll.

I fixed it for my tests, and I can now say it works without issue on my end. I put a timeout value on the Wait for multiple Objects call, so I could do something else if no file was found. You can do as you please, of course. Here's my version:

#include <GUIConstants.au3>

Global Const $FILE_LIST_DIRECTORY = 0x1, _
        $FILE_FLAG_BACKUP_SEMANTICS = 0x2000000, _
        $FILE_FLAG_OVERLAPPED = 0x40000000

;   $FILE_SHARE_READ = 0x1, _
;   $FILE_SHARE_WRITE = 0x2, _
;   $OPEN_EXISTING = 0x3, _
;   $FILE_SHARE_DELETE = 0x4, _
; are already declared in GUIConstants.au3

Global Const _
        $FILE_NOTIFY_CHANGE_FILE_NAME = 0x1, _
        $FILE_NOTIFY_CHANGE_DIR_NAME = 0x2, _
        $FILE_NOTIFY_CHANGE_ATTRIBUTES = 0x4, _
        $FILE_NOTIFY_CHANGE_SIZE = 0x8, _
        $FILE_NOTIFY_CHANGE_LAST_WRITE = 0x10, _
        $FILE_NOTIFY_CHANGE_LAST_ACCESS = 0x20, _
        $FILE_NOTIFY_CHANGE_CREATION = 0x40, _
        $FILE_NOTIFY_CHANGE_SECURITY = 0x100

Global Const _
        $FILE_ACTION_ADDED = 0x1, _
        $FILE_ACTION_REMOVED = 0x2, _
        $FILE_ACTION_MODIFIED = 0x3, _
        $FILE_ACTION_RENAMED_OLD_NAME = 0x4, _
        $FILE_ACTION_RENAMED_NEW_NAME = 0x5

$kernel32dll = "kernel32.dll"

$sdir = "I:\"

$tBuffer = DllStructCreate("byte[4096]")
$pBuffer = DllStructGetPtr($tBuffer)
$iBufferSize = DllStructGetSize($tBuffer)
$tFNI = 0

; CreateFile: http://msdn2.microsoft.com/en-us/library/aa914735.aspx
$hDir = DllCall($kernel32dll, "hwnd", "CreateFile", _
        "Str", $sdir, _
        "Int", $FILE_LIST_DIRECTORY, _
        "Int", BitOR($FILE_SHARE_READ, $FILE_SHARE_DELETE, $FILE_SHARE_WRITE), _
        "ptr", 0, _
        "int", $OPEN_EXISTING, _
        "int", BitOR($FILE_FLAG_BACKUP_SEMANTICS, $FILE_FLAG_OVERLAPPED), _
        "int", 0 _
        )
$hDir = $hDir[0]

$tReadLen = DllStructCreate("dword ReadLen")
Dim $BytesReturned

$tOverLapped = DllStructCreate("Uint OL1;Uint OL2; Uint OL3; Uint OL4; hwnd OL5")
For $i = 1 To 5
    DllStructSetData($tOverLapped, $i, 0)
    If @error Then
        MsgBox(0, "", "Error in DllStructSetData OverLapped " & @error);
        Exit
    EndIf
Next
$pOverLapped = DllStructGetPtr($tOverLapped)
$iOverLappedSize = DllStructGetSize($tOverLapped)

$tDirEvents = DllStructCreate("hwnd DirEvents")
$pDirEvents = DllStructGetPtr($tDirEvents)
$iDirEvents = DllStructGetSize($tDirEvents)

$hEvent = DllCall($kernel32dll, "hwnd", "CreateEvent", _
        "UInt", 0, _
        "Int", True, _
        "Int", False, _
        "UInt", 0)
DllStructSetData($tOverLapped, 5, $hEvent[0])
If @error Then
    MsgBox(0, "", "Error in DllStructSetData OverLapped 5" & @error);
    Exit
EndIf
DllStructSetData($tDirEvents, 1, $hEvent[0])
If @error Then
    MsgBox(0, "", "Error in DllStructSetData OverLapped 5" & @error);
    Exit
EndIf

$ret = DllCall($kernel32dll, "Int", "ReadDirectoryChangesW", _
        "hwnd", $hDir, _
        "ptr", $pBuffer, _
        "dword", $iBufferSize, _
        "int", True, _ ; <== That's so cool !!!!!!
        "dword", BitOR($FILE_NOTIFY_CHANGE_FILE_NAME _
        , $FILE_NOTIFY_CHANGE_DIR_NAME _
        , $FILE_NOTIFY_CHANGE_ATTRIBUTES _
        , $FILE_NOTIFY_CHANGE_SIZE _
        , $FILE_NOTIFY_CHANGE_LAST_WRITE _
        , $FILE_NOTIFY_CHANGE_LAST_ACCESS _
        , $FILE_NOTIFY_CHANGE_CREATION _
        , $FILE_NOTIFY_CHANGE_SECURITY), _
        "Uint", 0, _
        "Uint", $pOverLapped, _
        "Uint", 0 _
        )
While 1
    $r = DllCall("User32.dll", "dword", "MsgWaitForMultipleObjectsEx", _
            "dword", 1, _
            "ptr", $pDirEvents, _
            "dword", 100, _
            "dword", 0x4FF, _
            "dword", 0x6)

    If $r[0] = 0 Then

        ;   consolewrite(" an event occurred " & $r & @CRLF)
        $iOffset = 0

        $nReadLen = 0

        DllCall($kernel32dll, "Uint", "GetOverlappedResult", _
                "hWnd", $hDir, _
                "Uint", $pOverLapped, _
                "UInt*", $nReadLen, _
                "Int", True)

        While 1
            $tFNI = DllStructCreate("dword Next;dword Action;dword FilenameLen", $pBuffer + $iOffset)
            $tStr = DllStructCreate("wchar[" & DllStructGetData($tFNI, "FilenameLen") / 2 & "]", $pBuffer + $iOffset + 12)
            $Filename = DllStructGetData($tStr, 1)
            ;   ConsoleWrite($sFilename & ", action " & $iAction & @CRLF)

            Switch DllStructGetData($tFNI, "Action")
                Case $FILE_ACTION_ADDED
                    ConsoleWrite("+   " & $Filename & @CRLF)
                Case $FILE_ACTION_REMOVED
                    ConsoleWrite("-   " & $Filename & @CRLF)
                Case $FILE_ACTION_MODIFIED
                    ConsoleWrite("<>  " & $Filename & @CRLF)
                Case $FILE_ACTION_RENAMED_OLD_NAME
                    $OldName = $Filename
                    ;ConsoleWrite("The file was renamed and this is the old name." & @CRLF)
                Case $FILE_ACTION_RENAMED_NEW_NAME
                    ConsoleWrite($OldName & "    =>    " & $Filename & @CRLF)
                Case Else
                    ConsoleWrite("Undefined Action caught: " & DllStructGetData($tFNI, "Action") & " on file " & $Filename & @CRLF)
            EndSwitch

            $iNext = DllStructGetData($tFNI, "Next")
            If $iNext = 0 Then ExitLoop
            $iOffset += $iNext
        WEnd

        $ff = DllStructGetData($tOverLapped, 5)
        If @error Then
            MsgBox(0, "", "Error in DllStructGetData OverLapped 5" & @error);
            Exit
        EndIf

        DllCall($kernel32dll, "Uint", "ResetEvent", _
                "UInt", $ff)

        $ret = DllCall($kernel32dll, "Int", "ReadDirectoryChangesW", _
                "hwnd", $hDir, _
                "ptr", $pBuffer, _
                "dword", $iBufferSize, _
                "int", True, _ ; <== That's so cool !!!!!!
                "dword", BitOR($FILE_NOTIFY_CHANGE_FILE_NAME _
                , $FILE_NOTIFY_CHANGE_DIR_NAME _
                , $FILE_NOTIFY_CHANGE_ATTRIBUTES _
                , $FILE_NOTIFY_CHANGE_SIZE _
                , $FILE_NOTIFY_CHANGE_LAST_WRITE _
                , $FILE_NOTIFY_CHANGE_LAST_ACCESS _
                , $FILE_NOTIFY_CHANGE_CREATION _
                , $FILE_NOTIFY_CHANGE_SECURITY), _
                "Uint", 0, _
                "Uint", $pOverLapped, _
                "Uint", 0 _
                )

    ElseIf $r[0] = 4294967295 Then
        ConsoleWrite(_GetLastErrorMessage() & @CRLF)
    EndIf
WEnd

Func _GetLastErrorMessage()
    Local $ret,$s
    $ret    = DllCall("Kernel32.dll","int","GetLastError")
    Return _FormatMessage($ret[0])
EndFunc

Func _FormatMessage($err)
    Local Const $FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000
    Local $ret, $s
    $ret = DllCall("kernel32.dll","int","FormatMessage", _
                    "int", $FORMAT_MESSAGE_FROM_SYSTEM, _
                    "ptr", 0, _
                    "int", $err, _
                    "int", 0, _
                    "str", $s, _
                    "int", 4096, _
                    "ptr", 0)
    $s = $ret[5]
    Return $s
EndFunc

I also added Functions to test for and report errors for you. Again, thanks for your work.

Edited by SkinnyWhiteGuy
Link to comment
Share on other sites

First of all, many thanks for all your contribution.

I have few comments in reply to what you said:

Thanks for this, I have been playing with it a lot. I have a reason for you seeing multiple file changes: the file is updated for multiple things. One change is the actual content change, another is the time stamp, and a third could be the attributes, because all can trigger an event like that. As far as discovering which event that was, I'm not too sure right now.

I'm afraid it is not so.

I have left out the inessential events in the code line which calls ReadDirectoryChangeW, writing as follows:

$ret = DllCall($kernel32dll, "Int", "ReadDirectoryChangesW", _
        "hwnd", $hDir, _
        "ptr", $pBuffer, _
        "dword", $iBufferSize, _
        "int", True, _; <== That's so cool !!!!!!
        "dword", BitOR($FILE_NOTIFY_CHANGE_FILE_NAME _
        , $FILE_NOTIFY_CHANGE_LAST_WRITE _
        , $FILE_NOTIFY_CHANGE_CREATION _
        ), _
        "Uint", 0, _
        "Uint", $pOverLapped, _
        "Uint", 0 _
        )

but I obtain yet many notifications for a single file modification.

Edit: Upon further testing, I discovered something: Your example is actually broken. It only works by coincidence. Your call to MsgWaitForMultipleObjectsEX was failing (you forgot to set the structures to using $hEvent[0] instead of $hEvent), and you weren't looking at it's true return value ($r[0]), but since it was erroring, it was producing 0 for $r, so it kept going till it hit GetOverlappedResults, which you put as not returning until something came through (hence why it was locking me up), so it waited till it returned, which it would when it saw something changed. Also, MsgWaitForMultipleObjectsEX is defined in User32.dll, not Kernel32.dll.

I regret very much for such inconvenients. :) As you probably have realized, my knowledge of winapi functions is practically zero. I should have paid attention to previous CreateFile call which analogously returns a "hwnd" variable $hDir and that immediately after the instruction $hDir = $hDir[0] is used.

By the way, I need an explanation, if you want. What $r is? An Array? How many elements it has? What does r[0] contain? Is such structure typical of "hwnd" variable type?

I fixed it for my tests, and I can now say it works without issue on my end. I put a timeout value on the Wait for multiple Objects call, so I could do something else if no file was found. You can do as you please, of course. Here's my version:

I'm not sure to have understood what you mean. The script uses a while-wend loop but you can put many other instructions (your "something else") without be worried about file changes, which are not lost, since they are signaled just after the first call to MsgWaitForMultipleObjectsEx.

In other words: you can write

While 1
    msgbox(0,"","wait for the user touch")
    $r = DllCall("User32.dll", "dword", "MsgWaitForMultipleObjectsEx", _
            "dword", 1, _
            "ptr", $pDirEvents, _
            "dword", -1, _
            "dword", 0x4FF, _
            "dword", 0x6)

and even if you press "OK" only after one minute at a time, the script remembers all the events happened in the while. (Sorry for the very rough description of my thinking). Or did you want to say something else? In other words, which is the practical difference between using -1 and using 100?

Many thanks again for your patience and your helpfulness.

Edited by Zomp
Link to comment
Share on other sites

First of all, many thanks for all your contribution.

I have few comments in reply to what you said:

I'm afraid it is not so.

I have left out the inessential events in the code line which calls ReadDirectoryChangeW, writing as follows:

$ret = DllCall($kernel32dll, "Int", "ReadDirectoryChangesW", _
        "hwnd", $hDir, _
        "ptr", $pBuffer, _
        "dword", $iBufferSize, _
        "int", True, _; <== That's so cool !!!!!!
        "dword", BitOR($FILE_NOTIFY_CHANGE_FILE_NAME _
        , $FILE_NOTIFY_CHANGE_LAST_WRITE _
        , $FILE_NOTIFY_CHANGE_CREATION _
        ), _
        "Uint", 0, _
        "Uint", $pOverLapped, _
        "Uint", 0 _
        )

but I obtain yet many notifications for a single file modification.

I noticed that sometimes too, but only with certain applications. Notepad only triggers one time for a file save, but Scite usually triggers 3, and Wordpad triggers 2. It might just be the way the program saves, not sure.

I regret very much for such inconvenients. :) As you probably have realized, my knowledge of winapi functions is practically zero. I should have paid attention to previous CreateFile call which analogously returns a "hwnd" variable $hDir and that immediately after the instruction $hDir = $hDir[0] is used.

By the way, I need an explanation, if you want. What $r is? An Array? How many elements it has? What does r[0] contain? Is such structure typical of "hwnd" variable type?

It's ok, it was a challenge, and a rewarding one at that. I learned more about those API calls myself. $r is the return from DllCall, which is an array if the call succeeds. $r[0] is the return value from the called Dll, $r[1] is the 1st argument (which the DLL can set sometimes depending on what kind of variable it asked to be put in) and so on. Read the help file on DllCall for further info.

I'm not sure to have understood what you mean. The script uses a while-wend loop but you can put many other instructions (your "something else") without be worried about file changes, which are not lost, since they are signaled just after the first call to MsgWaitForMultipleObjectsEx.

In other words: you can write

While 1
    msgbox(0,"","wait for the user touch")
    $r = DllCall("User32.dll", "dword", "MsgWaitForMultipleObjectsEx", _
            "dword", 1, _
            "ptr", $pDirEvents, _
            "dword", -1, _
            "dword", 0x4FF, _
            "dword", 0x6)

and even if you press "OK" only after one minute at a time, the script remembers all the events happened in the while. (Sorry for the very rough description of my thinking). Or did you want to say something else? In other words, which is the practical difference between using -1 and using 100?

Many thanks again for your patience and your helpfulness.

Well, with that -1 in there (which should be equal to 0xFFFFFFFF), that is the value for WAIT_INFINITE, meaning it will never stop waiting for the event to occur. I changed it to 100, so with using testing, I could have it not pause the script while waiting for a file event to take place on the drive in question, as well as to give it some pauses so as not to eat up the CPU usage.

I played with it some more last night, and I almost have a working version that can monitor multiple folders. With you showing me that you can "wait" for calls to that, and it still show you everything that happened before, I might just get it working as an AdLib function, which would be great for me. If you want to see the version I have now, just let me know, and I can post it for you.

Link to comment
Share on other sites

I played with it some more last night, and I almost have a working version that can monitor multiple folders. With you showing me that you can "wait" for calls to that, and it still show you everything that happened before, I might just get it working as an AdLib function, which would be great for me. If you want to see the version I have now, just let me know, and I can post it for you.

Surely yes! A thousand of thanks!

Link to comment
Share on other sites

I figured that'd be your response, but wasn't sure if you wanted a stab at it first.

#include <GUIConstants.au3>

Global Const $FILE_LIST_DIRECTORY = 0x1, _
        $FILE_FLAG_BACKUP_SEMANTICS = 0x2000000, _
        $FILE_FLAG_OVERLAPPED = 0x40000000

;   $FILE_SHARE_READ = 0x1, _
;   $FILE_SHARE_WRITE = 0x2, _
;   $OPEN_EXISTING = 0x3, _
;   $FILE_SHARE_DELETE = 0x4, _
; are already declared in GUIConstants.au3

Global Const _
        $FILE_NOTIFY_CHANGE_FILE_NAME = 0x1, _
        $FILE_NOTIFY_CHANGE_DIR_NAME = 0x2, _
        $FILE_NOTIFY_CHANGE_ATTRIBUTES = 0x4, _
        $FILE_NOTIFY_CHANGE_SIZE = 0x8, _
        $FILE_NOTIFY_CHANGE_LAST_WRITE = 0x10, _
        $FILE_NOTIFY_CHANGE_LAST_ACCESS = 0x20, _
        $FILE_NOTIFY_CHANGE_CREATION = 0x40, _
        $FILE_NOTIFY_CHANGE_SECURITY = 0x100

Global Const _
        $FILE_ACTION_ADDED = 0x1, _
        $FILE_ACTION_REMOVED = 0x2, _
        $FILE_ACTION_MODIFIED = 0x3, _
        $FILE_ACTION_RENAMED_OLD_NAME = 0x4, _
        $FILE_ACTION_RENAMED_NEW_NAME = 0x5

$kernel32dll = "kernel32.dll"

Dim $dirs = 2
$sdir[0] = "I:\"
$sdir[1] = "C:\"

Dim $sdir[$dirs], $hDir[$dirs], $tOverLapped[$dirs], $pOverLapped[$dirs], $iOverLappedSize[$dirs], $hEvent[$dirs]
$tBuffer = DllStructCreate("byte[4096]")
$pBuffer = DllStructGetPtr($tBuffer)
$iBufferSize = DllStructGetSize($tBuffer)
$tFNI = 0

For $i = 0 To UBound($sdir) - 1
    ; CreateFile: http://msdn2.microsoft.com/en-us/library/aa914735.aspx
    $tDir = DllCall($kernel32dll, "hwnd", "CreateFile", _
            "Str", $sdir[$i], _
            "Int", $FILE_LIST_DIRECTORY, _
            "Int", BitOR($FILE_SHARE_READ, $FILE_SHARE_DELETE, $FILE_SHARE_WRITE), _
            "ptr", 0, _
            "int", $OPEN_EXISTING, _
            "int", BitOR($FILE_FLAG_BACKUP_SEMANTICS, $FILE_FLAG_OVERLAPPED), _
            "int", 0 _
            )
    $hDir[$i] = $tDir[0]
Next

$tReadLen = DllStructCreate("dword ReadLen")
Dim $BytesReturned

For $i = 0 To UBound($sdir) - 1
    $tOverLapped[$i] = DllStructCreate("Uint OL1;Uint OL2; Uint OL3; Uint OL4; hwnd OL5")
    For $j = 1 To 5
        DllStructSetData($tOverLapped[$i], $j, 0)
        If @error Then
            MsgBox(0, "", "Error in DllStructSetData OverLapped " & @error);
            Exit
        EndIf
    Next
    $pOverLapped[$i] = DllStructGetPtr($tOverLapped[$i])
    $iOverLappedSize[$i] = DllStructGetSize($tOverLapped[$i])
Next

$tDirEvents = DllStructCreate("hwnd DirEvents[" & $dirs & "]")
$pDirEvents = DllStructGetPtr($tDirEvents)
$iDirEvents = DllStructGetSize($tDirEvents)

For $i = 1 To UBound($sdir)
    $hEvent = DllCall($kernel32dll, "hwnd", "CreateEvent", _
            "UInt", 0, _
            "Int", True, _
            "Int", False, _
            "UInt", 0)
    DllStructSetData($tOverLapped[$i-1], 5, $hEvent[0])
    If @error Then
        MsgBox(0, "", "Error in DllStructSetData OverLapped 5" & @error);
        Exit
    EndIf
    DllStructSetData($tDirEvents, 1, $hEvent[0], $i)
    If @error Then
        MsgBox(0, "", "Error in DllStructSetData OverLapped 5" & @error);
        Exit
    EndIf
Next

For $i = 0 To UBound($sdir) - 1
    $ret = DllCall($kernel32dll, "Int", "ReadDirectoryChangesW", _
            "hwnd", $hDir[$i], _
            "ptr", $pBuffer, _
            "dword", $iBufferSize, _
            "int", True, _ ; <== That's so cool !!!!!!
            "dword", BitOR($FILE_NOTIFY_CHANGE_FILE_NAME _
            , $FILE_NOTIFY_CHANGE_DIR_NAME _
            , $FILE_NOTIFY_CHANGE_ATTRIBUTES _
            , $FILE_NOTIFY_CHANGE_SIZE _
            , $FILE_NOTIFY_CHANGE_LAST_WRITE _
            , $FILE_NOTIFY_CHANGE_LAST_ACCESS _
            , $FILE_NOTIFY_CHANGE_CREATION _
            , $FILE_NOTIFY_CHANGE_SECURITY), _
            "Uint", 0, _
            "Uint", $pOverLapped[$i], _
            "Uint", 0 _
            )
Next

While 1
    $r = DllCall("User32.dll", "dword", "MsgWaitForMultipleObjectsEx", _
            "dword", $dirs, _
            "ptr", $pDirEvents, _
            "dword", 100, _
            "dword", 0, _
            "dword", 0x6)
    Switch $r[0]
        Case 0 To UBound($sdir) - 1

        ;   consolewrite(" an event occurred " & $r & @CRLF)
        $iOffset = 0

        $nReadLen = 0
        
        DllCall($kernel32dll, "Uint", "GetOverlappedResult", _
                "hWnd", $hDir[$r[0]], _
                "Uint", $pOverLapped[$r[0]], _
                "UInt*", $nReadLen, _
                "Int", True)
                
        While 1
            $tFNI = DllStructCreate("dword Next;dword Action;dword FilenameLen", $pBuffer + $iOffset)
            $tStr = DllStructCreate("wchar[" & DllStructGetData($tFNI, "FilenameLen") / 2 & "]", $pBuffer + $iOffset + 12)
            $Filename = $sdir[$r[0]] & DllStructGetData($tStr, 1)
            ;   ConsoleWrite($sFilename & ", action " & $iAction & @CRLF)

            Switch DllStructGetData($tFNI, "Action")
                Case $FILE_ACTION_ADDED
                    ConsoleWrite("+   " & $Filename & @CRLF)
                Case $FILE_ACTION_REMOVED
                    ConsoleWrite("-   " & $Filename & @CRLF)
                Case $FILE_ACTION_MODIFIED
                    ConsoleWrite("<>  " & $Filename & @CRLF)
                Case $FILE_ACTION_RENAMED_OLD_NAME
                    $OldName = $Filename
                    ;ConsoleWrite("The file was renamed and this is the old name." & @CRLF)
                Case $FILE_ACTION_RENAMED_NEW_NAME
                    ConsoleWrite($OldName & "    =>    " & $Filename & @CRLF)
                Case Else
                    ConsoleWrite("Undefined Action caught: " & DllStructGetData($tFNI, "Action") & " on file " & $Filename & @CRLF)
            EndSwitch

            $iNext = DllStructGetData($tFNI, "Next")
            If $iNext = 0 Then ExitLoop
            $iOffset += $iNext
        WEnd

        $ff = DllStructGetData($tOverLapped[$r[0]], 5)
        If @error Then
            MsgBox(0, "", "Error in DllStructGetData OverLapped 5" & @error);
            Exit
        EndIf

        DllCall($kernel32dll, "Uint", "ResetEvent", _
                "UInt", $ff)

        $ret = DllCall($kernel32dll, "Int", "ReadDirectoryChangesW", _
                "hwnd", $hDir[$r[0]], _
                "ptr", $pBuffer, _
                "dword", $iBufferSize, _
                "int", True, _ ; <== That's so cool !!!!!!
                "dword", BitOR($FILE_NOTIFY_CHANGE_FILE_NAME _
                , $FILE_NOTIFY_CHANGE_DIR_NAME _
                , $FILE_NOTIFY_CHANGE_ATTRIBUTES _
                , $FILE_NOTIFY_CHANGE_SIZE _
                , $FILE_NOTIFY_CHANGE_LAST_WRITE _
                , $FILE_NOTIFY_CHANGE_LAST_ACCESS _
                , $FILE_NOTIFY_CHANGE_CREATION _
                , $FILE_NOTIFY_CHANGE_SECURITY), _
                "Uint", 0, _
                "Uint", $pOverLapped[$r[0]], _
                "Uint", 0 _
                )

        Case 4294967295
            ConsoleWrite(_GetLastErrorMessage() & @CRLF)
    EndSwitch
WEnd

Func _GetLastErrorMessage()
    Local $ret,$s
    $ret    = DllCall("Kernel32.dll","int","GetLastError")
    Return _FormatMessage($ret[0])
EndFunc

Func _FormatMessage($err)
    Local Const $FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000
    Local $ret, $s
    $ret = DllCall("kernel32.dll","int","FormatMessage", _
                    "int", $FORMAT_MESSAGE_FROM_SYSTEM, _
                    "ptr", 0, _
                    "int", $err, _
                    "int", 0, _
                    "str", $s, _
                    "int", 4096, _
                    "ptr", 0)
    $s = $ret[5]
    Return $s
EndFunc

Just remember to set the $dirs variable to how many your going to watch, and then fill in the array from 0 to $dirs - 1 with the names of the directories (ending with a \ I believe).

Edited by SkinnyWhiteGuy
Link to comment
Share on other sites

Just remember to set the $dirs variable to how many your going to watch, and then fill in the array from 0 to $dirs - 1 with the names of the directories (ending with a \ I believe).

Yes, you are right. I'm sure about the need of the final "\". :)

Now I'm going to enjoy myself with your work. Many many thanks.

Link to comment
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
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...