Jump to content

RDC UDF (ReadDirectoryChanges Wrapper)


Yashied
 Share

Recommended Posts

questions  , i have created this  script , but  why  if  i change  variable 

$iGResult = 0  , it  changes  in 1   autoamatically??

#include <APIConstants.au3>
#include <GDIPlus.au3>
#include <MsgBoxConstants.au3>
#include <WinAPIFiles.au3>

#include "RDC.au3"

Opt('MustDeclareVars', 1)
Opt('TrayAutoPause', 0)

Global $hWnd = GUICreate(''), $aDir[3], $iGResult, $sGPath_Monitor
$sGPath_Monitor = StringReplace(@ScriptDir, "\INCLUDE", "") & (IniRead(@ScriptDir & "\presync_conf.ini", "Path_to_sync", "key1", "Error"))

_RDC_OpenDll()
If @error Then
    ConsoleWrite('Error: _RDC_OpenDll() - ' & @error & @CR)
    MsgBox(16, 'Error !!!', 'Error: _RDC_OpenDll() - ' & @error & @CR)
    Exit
EndIf

GUIRegisterMsg($WM_RDC, 'WM_RDC')

_RDC_Create($sGPath_Monitor, 1, BitOR($FILE_NOTIFY_CHANGE_FILE_NAME, $FILE_NOTIFY_CHANGE_DIR_NAME, $FILE_NOTIFY_CHANGE_SIZE), 0, $hWnd)
If @error Then
    ConsoleWrite('Error: _RDC_Create() - ' & @error & ', ' & @extended & @CR)
    MsgBox(16, 'Error !!!', 'Error: _RDC_Create() - ' & @error & ' ' & @extended & @CR)
    ;Exit
EndIf


Func WM_RDC($hWnd, $iMsg, $wParam, $lParam)

    #forceref $hWnd, $iMsg, $wParam
    Local $aData = _RDC_GetData($lParam)

    If @error Then
        ; Do something because notifications will not come from this thread!
        ConsoleWrite('Error: _RDC_GetData() - ' & @error & ', ' & @extended & ', ' & _RDC_GetDirectory($lParam) & @CR)
        MsgBox(16, 'Error !!!', 'Error: _RDC_GetData() - ' & @error & ' ' & @extended & ' ' & _RDC_GetDirectory($lParam) & @CR)
        _RDC_Delete($lParam)
        Return 0
    EndIf
    For $i = 1 To $aData[0][0]
        $iGResult = $aData[$i][0]
    Next
    ;ConsoleWrite($iGResult)
    Return 0

EndFunc   ;==>WM_RDC

While 1

    Switch $iGResult
        Case $iGResult = 1 ; crea file
            MsgBox(0, '1', 'crea file')
            $iGResult = 0
        Case $iGResult = 2 ; cancella file
            MsgBox(0, '2', 'cancella file')
            $iGResult = 0
        Case $iGResult = 3 ; modifica file
            MsgBox(0, '3', 'modifica file')
            $iGResult = 0
        Case $iGResult = 4 ; nome vecchio
            MsgBox(0, '4', 'nome vecchio')
            $iGResult = 0
        Case $iGResult = 5 ; nome nuovo
            MsgBox(0, '5', 'nome nuovo')
            $iGResult = 0
    EndSwitch

WEnd

 

 

Link to comment
Share on other sites

On 11/27/2016 at 9:51 AM, argumentum said:
Local $n
Local $t = @MIN&@SEC
For $n = 1 To 100
;~  Sleep($n * 10)
    FileWriteLine(@ScriptDir&'\~TEST~\testFastWrite'&$t&'_'&$n&'.txt',"delete, this just a test."&@CRLF)
Next

@Mbee , I've runned this and checked the original code's result. Could not find anything out of sequence.

Hello, @argumentum -- Sorry about the delay in responding.  I somehow missed the notification...

As for the code snippet you posted, I'm afraid I don't understand what you're testing -- at least in regards to my tiny change in the UDF.  The ONLY change I made was ONLY within the _RDC_GetData() function, which your snippet doesn't even call.  The documentation of the original version of that function claimed that the returned array from that call would hold the type of change in element [n][0] and the filename in element [n][1].  However, the original version (v1.0) actually returned the filename in element [n][0] and the type of change code in element [n][1], which was in error.  All I did was make the teeny tiny change so that the documentation would be correct!

So, since I don't see anywhere that you actually call _RDC_GetData(), my tiny change has had absolutely no effect on what you're trying to accomplish.

If I've misunderstood, please let me know.

Link to comment
Share on other sites

i try to modify  the script  with this  but put always up 100% cpu usage  :(

#include <APIConstants.au3>
#include <GDIPlus.au3>
#include <MsgBoxConstants.au3>
#include <WinAPIFiles.au3>

#include "RDC.au3"

Opt('MustDeclareVars', 1)
Opt('TrayAutoPause', 0)

Global $hWnd = GUICreate(''), $aDir[3], $iGResult, $sGPath_Monitor, $de = 0

$sGPath_Monitor = StringReplace(@ScriptDir, "\INCLUDE", "") & (IniRead(@ScriptDir & "\presync_conf.ini", "Path_to_sync", "key1", "Error"))

_RDC_OpenDll()
If @error Then
    ConsoleWrite('Error: _RDC_OpenDll() - ' & @error & @CR)
    MsgBox(16, 'Error !!!', 'Error: _RDC_OpenDll() - ' & @error & @CR)
    Exit
EndIf

GUIRegisterMsg($WM_RDC, 'WM_RDC')

_RDC_Create($sGPath_Monitor, 1, BitOR($FILE_NOTIFY_CHANGE_FILE_NAME, $FILE_NOTIFY_CHANGE_DIR_NAME, $FILE_NOTIFY_CHANGE_SIZE), 0, $hWnd)
If @error Then
    ConsoleWrite('Error: _RDC_Create() - ' & @error & ', ' & @extended & @CR)
    MsgBox(16, 'Error !!!', 'Error: _RDC_Create() - ' & @error & ' ' & @extended & @CR)
    ;Exit
EndIf


Func WM_RDC($hWnd, $iMsg, $wParam, $lParam)

    #forceref $hWnd, $iMsg, $wParam
    Local $aData = _RDC_GetData($lParam)

    If @error Then
        ; Do something because notifications will not come from this thread!
        ConsoleWrite('Error: _RDC_GetData() - ' & @error & ', ' & @extended & ', ' & _RDC_GetDirectory($lParam) & @CR)
        MsgBox(16, 'Error !!!', 'Error: _RDC_GetData() - ' & @error & ' ' & @extended & ' ' & _RDC_GetDirectory($lParam) & @CR)
        _RDC_Delete($lParam)
        Return 0
    EndIf
    For $i = 1 To $aData[0][0]
        $iGResult = $aData[$i][0]
        $de = 1
    Next

    ;ConsoleWrite($iGResult)
    Return 0

EndFunc   ;==>WM_RDC

While 1

    If $de = 1 Then
        Switch $iGResult
            Case $iGResult = 1 ; crea file
                MsgBox(0, '1', 'crea file')
                $de = 0
            Case $iGResult = 2 ; cancella file
                MsgBox(0, '2', 'cancella file')
                $de = 0
            Case $iGResult = 3 ; modifica file
                MsgBox(0, '3', 'modifica file')
                $de = 0
            Case $iGResult = 4 ; nome vecchio
                MsgBox(0, '4', 'nome vecchio')
                $de = 0
            Case $iGResult = 5 ; nome nuovo
                MsgBox(0, '5', 'nome nuovo')
                $de = 0
        EndSwitch
    EndIf


WEnd

but  how is  possible modify ???

 

Link to comment
Share on other sites

@faustf, I haven't tried to analyze your code very much, but why the infinite loop?  In any case, there's a chance that your WM_RDC() handler is simply being invoked (via re-entrance) by the system more than once, so that the $iGResult variable is being reset each time.  As I noted at the end of my post on the first page ( here), you MUST expect to get each notification at least twice.

I would set a counter at the top of your WM_RDC() handler to record how many times it is being invoked.

 

Link to comment
Share on other sites

@faustf, the %100 CPU usage might be due to your infinite loop, but the problem may lie elsewhere.  When I run the code you've posted on the previous page where you saw extreme CPU usage, I see at most %35 CPU usage...

Also, in your WM_RDC handler, you combine the data from multiple changes in a single variable! Huh??  And you set $de to 1 every pass through the loop, which also doesn't make sense.

Why don't you tell us exactly what you're trying to do/test in your code, please?

 

Edited by Mbee
Link to comment
Share on other sites

@faustf, as promised, I've coded up an example test of the RDC functions as per what I understood about your needs.

You can download the attached source file and try it yourself, but I'll also post the code here.  Your attempts included a few serious mistakes, but the following works fine on my machine...

#include <APIConstants.au3>
#include <GDIPlus.au3>
#include <MsgBoxConstants.au3>
#include <WinAPIFiles.au3>

#include "RDC.au3"

Opt('MustDeclareVars', 1)
Opt('GUICloseOnESC', 1)
Opt('GUIOnEventMode', 1)    ; Enable Event Mode
Opt('TrayAutoPause', 0)

Global $GiRDCid, $GhWnd, $GbActionsOccurred, $GsPath_Monitor, $GsEventDesc[50], $GiEventDescIndex = 0, $GiTotalRDCmsgCount = 0
Global Const $CsAppTitle = "RDC Test Script"

Local $iStat, $sPart1, $sPart2, $sOutMsg

$GbActionsOccurred = False

$sPart1 = StringReplace(@ScriptDir, "\INCLUDE", "")
$sPart2 = IniRead(@ScriptDir & "\presync_conf.ini", "Path_to_sync", "key1", "Error")
If $sPart2 = "Error" Then
    MsgBox($MB_ICONERROR, $CsAppTitle, "File 'presync_conf.ini' contains invalid data - Exiting")
    Exit
EndIf

$GsPath_Monitor = $sPart1 & $sPart2
If Not FileExists($GsPath_Monitor) Then
    MsgBox($MB_ICONERROR, $CsAppTitle, "Folder to be monitored does NOT exist! - Exiting")
    Exit
EndIf

_RDC_OpenDll()
If @error <> 0 Then
    MsgBox($MB_ICONERROR, $CsAppTitle, 'Error: _RDC_OpenDll() - ' & @error & ' - Exiting' & @CR)
    Exit
EndIf

HotKeySet("{ESC}", "_ExitApp")

$GhWnd = GUICreate($CsAppTitle)
If $GhWnd = 0 Then
    MsgBox($MB_ICONERROR, $CsAppTitle, 'Error: GUICreate() Failed! - Exiting')
    Exit
EndIf


$iStat = GUIRegisterMsg($WM_RDC, 'WM_RDC')
If $iStat <> 1 Then
    MsgBox($MB_ICONERROR, $CsAppTitle, "Error: GUIRegisterMsg() failed - Exiting")
    Exit
EndIf

$GiRDCid = _RDC_Create($GsPath_Monitor, True, _
    BitOR($FILE_NOTIFY_CHANGE_FILE_NAME, $FILE_NOTIFY_CHANGE_DIR_NAME, $FILE_NOTIFY_CHANGE_SIZE), False, $GhWnd, 47)
If $GiRDCid  = -1 Then
    MsgBox($MB_ICONERROR, $CsAppTitle, 'Error: _RDC_Create() - @error = ' & @error & '  @extended = ' & @extended & ' -- Exiting' & @CR)
    Exit
EndIf


While True

    If $GbActionsOccurred Then
        $iStat = GUIRegisterMsg($WM_RDC, "")    ; Unregister this event handler to avoid being interrupted until done
        If $iStat <> 1 Then
            MsgBox($MB_ICONERROR, $CsAppTitle, "Error from GUIRegisterMsg() call to disable WM_RDC failed - Exiting")
            Exit
        EndIf

        $sOutMsg = ""

        For $i = 0 To $GiEventDescIndex -1
            $sOutMsg &= "Event # " & $i & " : " & $GsEventDesc[$i] & @CRLF
        Next

        MsgBox($MB_OK, $CsAppTitle, "Total number of WM_RDC invocations thus far = " & $GiTotalRDCmsgCount & @CRLF & @CRLF _
            & "Number of Action Events this pass = " & $GiEventDescIndex & "  -- Here's the list..." & @CRLF & $sOutMsg)

        $GbActionsOccurred = False              ; Reset this flag so we won't be triggered until there are more events
        $iStat = GUIRegisterMsg($WM_RDC, "WM_RDC") ; Re-register this event handler
        If $iStat <> 1 Then
            MsgBox($MB_ICONERROR, $CsAppTitle, "Error from GUIRegisterMsg() call to re-enable WM_RDC failed - Exiting")
            Exit
        EndIf

    Else

        Sleep(500)

    EndIf

WEnd


Func WM_RDC($hWnd, $iMsg, $wParam, $lParam)

    #forceref $hWnd, $iMsg, $wParam, $lParam

    Local $sName, $iActionCount, $iEventType


    $GiTotalRDCmsgCount += 1

    Local $aData = _RDC_GetData($GiRDCid)
    If @error <> 0 Then
        $sName = _RDC_GetDirectory($GiRDCid)
        If @error <> 0 Then
            MsgBox($MB_ICONERROR, $CsAppTitle, 'Error: _RDC_GetDirectory() failed. @error = ' & @error & ' @extended = ' _
                    & @extended & ' -- Exiting' & @CR)
            Exit
        EndIf
        MsgBox($MB_ICONERROR, $CsAppTitle, 'Error: _RDC_GetData() - @error = ' & @error & ' @extended = ' & @extended _
            & @CR & " Directory = '" & $sName & @CR & "... Exiting")
        _RDC_Delete($GiRDCid)
        Exit
    EndIf

    $iActionCount = $aData[0][0]
    If $iActionCount < 1 Then Return 0

    $GiEventDescIndex = 0
    For $i = 1 To $iActionCount
        $iEventType = $aData[$i][0]
        $sName = $aData[$i][1]

        Switch $iEventType
            Case $FILE_ACTION_ADDED
                $GsEventDesc[$GiEventDescIndex] = "Item Added = " & $sName
            Case $FILE_ACTION_REMOVED
                $GsEventDesc[$GiEventDescIndex] = "Item Removed = " & $sName
            Case $FILE_ACTION_MODIFIED
                $GsEventDesc[$GiEventDescIndex] = "Item Modified = " & $sName
            Case $FILE_ACTION_RENAMED_OLD_NAME
                $GsEventDesc[$GiEventDescIndex] = "Old item named " & $sName & " will be renamed"
            Case $FILE_ACTION_RENAMED_NEW_NAME
                $GsEventDesc[$GiEventDescIndex] = "Item renamed to " & $sName
            Case Else
                $GsEventDesc[$GiEventDescIndex] = "INVALID ACTION!"
        EndSwitch

        $GiEventDescIndex += 1
        If $GiEventDescIndex >= 50 Then
            MsgBox($MB_ICONERROR, $CsAppTitle, 'Error in WM_RDC: Too many events! - Exiting')
            Exit
        EndIf

    Next

    $GbActionsOccurred = True
    Return 0

EndFunc   ;==>WM_RDC


Func _ExitApp()
    Exit
EndFunc

 

 

 

 

RDC Test Script.au3

Edited by Mbee
typo & minor code change
Link to comment
Share on other sites

Here is an explanation of the code I posted above, along with some comments (not in any particular order, but just as they occur to me)...

Note that you need to actually build this code and then run the resulting executable. It is not intended for running within SciTE.  Also, your code must NOT have the line: #include <RDC.au3>.  Instead, you need to have my version 1.1 of RDC.au3 in your development directory, because that's the correct code.  That might well be why others such as @argumentum have not seen the effects of my changes!  Replace #include <RDC.au3> with #include "RDC.au3" !

As others have quite correctly pointed out, one critical thing is that it's essential that the WM_RDC() event handler do it's job and return as quickly as possible (while still performing the necessary stuff).  Thus, in the code above, there are no time-consuming calls in the event handler except for the MsgBox() calls that will only be invoked upon an error and then will exit immediately anyway.

What I've done is to structure the main code so that it will do the time-consuming work such as issuing MsgBox calls (I stripped out the ConsoleWrite stuff since it was redundant and I don't like doing things that way anyway).  And it will only do that when there's something to report, as indicated by the "$GbActionsOccurred" flag.

Also, I enabled "On Event" mode, since your trials never even polled events anyway.  The original author of the examples did a bare minimum job and thus his/her examples are not a good basis to build on in the first place.  They appear to have been intended merely to show what the RDC calls looked like, rather than do anything rational.

The main part of your example code blindly fell right into the WM_RDC event handler, which was a mistake that was probably due to the poor example code provided.

Also, the earlier code's usage of the "$lParam" argument in the WM_RDC handler is very puzzling indeed (unless there's something I don't understand about the UDF)! The value of the parameter to be passed to functions such as "_RDC_GetData()" is the "RDC ID" (aka Thread ID) that was returned by the _RDC_Create() call. The UDF's documentation makes no mention that the $lParam value will contain that ID, but that could be a misunderstanding on my part...  In the case of multiple _RDC_Create() calls, each with different directories to be monitored, there might well need to be a way of allowing the WM_RDC handler to know which "RDC ID" the event came from, although getting the directory name might suffice...  If @Yashied coded the DLLs such as to provide that ID via either $lParam or in one of the words of the $wParam, that would do it.  I simply don't know if the DLLs are coded to do that or not...

I can't think of anything to add at the moment, so if you have any questions about the code I posted above, let me know!

 

Edited by Mbee
Added additional info
Link to comment
Share on other sites

Regarding the $lParam argument passed to the WM_RDC message handler, I've now more carefully reviewed the example script "RDC_Ex_Advanced.au3" and have seen the way the author (presumably Yashied) clearly assumed that it contained the RDC/Thread ID of the relevant directory.  I sure wish that had been documented!

But I also know that this was not strictly necessary.  In my code above, notice that the call to _RDC_Create() passed the "arbitrary" value of 47 (as a significant number of Americans -- either Ponoma College graduates or those who knew one (I had a friend who matriculated there), or just those who watched any number of Star Trek spin-off series' episodes -- understand as "the most common number in the universe"), a working alternative to using the $lParam value would be to pass a unique value for each separate call to _RDC_Create().  For instance...

Global $GaRDCID[2]

$GaRDCID[0] = _RDC_Create("C:\FolderA", True, _
    BitOR($FILE_NOTIFY_CHANGE_FILE_NAME, $FILE_NOTIFY_CHANGE_DIR_NAME, $FILE_NOTIFY_CHANGE_SIZE), False, $GhWnd, 1)
If $GaRDCID[0]  = -1 Then
    MsgBox($MB_ICONERROR, $CsAppTitle, 'Error: _RDC_Create() - @error = ' & @error & '  @extended = ' & @extended & ' -- Exiting' & @CR)
    Exit
EndIf

$GaRDCID[1] = _RDC_Create("C:\FolderB", True, _
    BitOR($FILE_NOTIFY_CHANGE_FILE_NAME, $FILE_NOTIFY_CHANGE_DIR_NAME, $FILE_NOTIFY_CHANGE_SIZE), False, $GhWnd, 2)
If $GaRDCID[1]  = -1 Then
    MsgBox($MB_ICONERROR, $CsAppTitle, 'Error: _RDC_Create() - @error = ' & @error & '  @extended = ' & @extended & ' -- Exiting' & @CR)
    Exit
EndIf


;~ . . . .

Func WM_RDC($hWnd, $iMsg, $wParam, $lParam)

    #forceref $hWnd, $iMsg, $wParam, $lParam

    Local $iLowWord, $iRDCid, $sName, $iActionCount, $iEventType


    $iLowWord = BitAND($wParam, 0xFFFF) ; The low-order word contains the value of the final parameter to _RDC_Create()
    $iRDCid = $GaRDCID[$iLowWord -1]

    Local $aData = _RDC_GetData($iRDCid)


;~ . . . .

EndFunc

 

Granted, using $lParam is easier.  This is just FYI..

 

Link to comment
Share on other sites

There were problems in the script I posted above for faustf, so I have posted the revised script below. I have also posted a better general example script with additional commentary for an event-mode GUI both in Post # 3 and below.  Enjoy.

 

RDC Test Script for faustf.au3

 

General RDC v1.1 Test Script with GUI.au3

Edited by Mbee
Link to comment
Share on other sites

  • 4 months later...

..so I was happily using this UDF/DLL and with a Edit control for debug in my project  ...
meanwhile downloading stuff with chrome browser ...
hey !, where is the new file?, it shows the "Unconfirmed 581257.crdownload", but, where is the newly renamed file ???, well, it does not know.
So use [NOW WORKING] a (broken) monitor file changes script which uses ReadDirectoryChangeW  , it does show every event.

Edit: Then again, I made a test writing 10000 files at 2 file per millisecond to a RAM disk and the above linked did not catch all the events but this UDF/DLL did.
...what to do, what to do. No clue yet.

Edited by argumentum

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

Link to comment
Share on other sites

  • 1 year later...

Thanks for the excellent UDF @Yashied, but I seem to have spotted some mistakes:

  1. The global constants are commented out
  2. _RDC_GetData documentation has reversed the columns for the returned array. It should be 0 for filename and 1 for type of change

Surely I am not the only one who noticed this? :think:

EasyCodeIt - A cross-platform AutoIt implementation - Fund the development! (GitHub will double your donations for a limited time)

DcodingTheWeb Forum - Follow for updates and Join for discussion

Link to comment
Share on other sites

@argumentum Not a lot to be corrected, you just need to remove the multi-line comment blocks to "enable" the constants inside the Constants region in RDC.au3. And remember that _RDC_GetData returns the filename in the first column and change in the second column as opposed to the documentation.

EasyCodeIt - A cross-platform AutoIt implementation - Fund the development! (GitHub will double your donations for a limited time)

DcodingTheWeb Forum - Follow for updates and Join for discussion

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...