Announcement

Collapse
No announcement yet.

FindFirstChangeNotification double

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • FindFirstChangeNotification double

    If I use FindFirstChangeNotification on a local directory ("C:\Temp" in example), it works fine.
    If I use it on a networked directory ("Z:\Projects" in example, on a NAS), I always get 2 notifications.
    A bit confusing; is there a way to prevent this?

    Code:
    #Compiler PBWin 10
    #Compile Exe
    #Dim All
    #Include "win32api.inc"
    '==================================================================================================
    Function PBMain()
      ReDim hServiceEvent(2) As Local Dword
      Local Result As Dword
    
      hServiceEvent(0) = CreateEvent (ByVal %Null, ByVal 0, ByVal 0, "Cleanup")    'Create Event
      hServiceEvent(1) = FindFirstChangeNotification("Z:\projects\", 0, %FILE_NOTIFY_CHANGE_LAST_WRITE)  ' <- NAS
    '  hServiceEvent(1) = FindFirstChangeNotification("C:\Temp\", 0, %FILE_NOTIFY_CHANGE_LAST_WRITE)    ' <- LOCAL
      '...............................................................................................................
      While %True                                                                            'Endless loop
         Result  = WaitForMultipleObjects (ByVal 2, ByVal VarPtr (hServiceEvent(0)), 0, 100)  'Wait on event
         Select Case Result
          Case %WAIT_TIMEOUT                                                                 'Timeout
          Case %WAIT_OBJECT_0 +1
            MsgBox "File change event"
            FindNextChangeNotification hServiceEvent(1)
            MsgBox "File change event 2"
        End Select
      Wend
    End Function
    Regards,
    Peter

  • #2
    You may actually be getting changes to more than one file. Maybe there is an indexing service or something else running which is recording changes and when that file is updated you get a notification for that as well as the notification for the "file of interest." But why you would not get that on the local drive, I don't know.

    I had this same kind of thing to deal with when I wrote Win32(SDK): Internet Cookie Monitor April 25, 2001. You will see I dealt with this by keeping my own directory for comparison to see WHICH file had changed.

    I will assume your hServiceEvent(0) (name "Cleanup") is a placeholder for something you will be adding to this application later, as it serves no purpose in this snippet since it is never signaled.

    BTW the MSGBOX in this code is out of place:
    Code:
     FindNextChangeNotification hServiceEvent(1)  
          MsgBox "File change event 2"
    You will always get this second (of the While/wend loop) messagebox.

    The FindNextChangeNotification() call simply notifies the O/S that a signal is requested on that handle on the next change so you can Wait (WFSO or WFMO) for it. It does not itself cause a wait here.

    MCM
    Michael Mattias
    Tal Systems (retired)
    Port Washington WI USA
    [email protected]
    http://www.talsystems.com

    Comment


    • #3
      Yes, Cleanup isn't used yet. A pity that findfirstchangenotification doesn't give the object that causes the event.
      Could be that one event is change of the file, and one change of the directory entries for the file?
      (I'm only interested in one file in that directory)
      Regards,
      Peter

      Comment


      • #4
        A pity that findfirstchangenotification doesn't give the object that causes the event.
        Someone posted some real nice code here just this week which uses the 'ShChangeNotifyRegister' family of functions. That does get you "direct" to the "file of interest."

        No reason you could not use a message-only window with that to get your notifications. Also you won't need to WaitFor....() anything. Requires Win/2000+ which is why I did not use it back in 2001 when I was running Windows/98 (as were many members here!)

        MCM
        Michael Mattias
        Tal Systems (retired)
        Port Washington WI USA
        [email protected]
        http://www.talsystems.com

        Comment


        • #5
          Used a quick and dirty way out (using the archive bit)...

          Code:
          #Compiler PBWin 10
          #Compile Exe
          #Dim All
          #Include "win32api.inc"
          '==================================================================================================
          Function PBMain()
            ReDim hServiceEvent(2) As Local Dword
            Local Result, hFile As Dword, zFileName As AsciiZ * 256
          
            zFileName = "Z:\projects\meldcentrale\config\test.log"
          
            hServiceEvent(0) = CreateEvent (ByVal %Null, ByVal 0, ByVal 0, "Cleanup" + Chr$(0))    'Create Event
            hServiceEvent(1) = FindFirstChangeNotification("Z:\projects\meldcentrale\config\", 0, %FILE_NOTIFY_CHANGE_LAST_WRITE)
            '....................................................................................................................
            While %True                                                                            'Endless loop
               Result  = WaitForMultipleObjects (ByVal 2, ByVal VarPtr (hServiceEvent(0)), 0, 100)  'Wait on event
               Select Case Result
                Case %WAIT_TIMEOUT                                                                 'Timeout
                Case %WAIT_OBJECT_0 +1
                  If  (GetAttr(zFileName) And %ARCHIVE) = %ARCHIVE Then
                    MsgBox "File change event"
                    SetAttr zFileName, %Normal
                    FindNextChangeNotification(hServiceEvent(1))
                  End If
              End Select
            Wend
          End Function
          Regards,
          Peter

          Comment


          • #6
            >Used a quick and dirty way out (using the archive bit)

            Assumes you are not using the archive bit for "normal" purposes.. like backing up. Then again, you are only watching one file. That being the case, why don't you save the file's last write time and compare that against "now?" That way you don't have to mess with the archive bit at all. (See cookie monitor application.. that's what I did!)

            Code:
            LOCAL w32Before AS WIN32_FIND_DATA , W32Now  ' can use DIR$() and "DirectoryVar"  if you want
               hFind = FindFirstFile (targetFile, W32before)
               FindClose hFind
            
            DO
            
               iWait =  WFMO ...
                SELECT CASE AS LONG iWait
            
                  CASE   WAIT_OBJECT_0 + 1&
                       hFind = FindFirstFile  (target File, W32Now)   ' get current info re target file
                       FindC;ose hFile                                            ' close this find handle
                    IF ISTRUE (CompareFileTime (W32Before.lastwriteTime, W32Now.lastwriteTime)) THEN   ' did target file's last write change?
                      target File's last write time has changed   ' yes
                      do whatever it is you do when this happens
                     W32Before = W32Now   ' the new last write time
                   else
                        it hasn't and we don't care
                   endif
                   FindNextChangeNotfication  hServiceEvent (1)
              END SELECT
            
            LOOP
            Also - timeout=100? Like, someone is going to change your target file ten times per second? I think you will be looping a lot on WAIT_TIMEOUT, wasting valuable computer resources.

            Real Men wait %INFINITE.

            MCM
            Michael Mattias
            Tal Systems (retired)
            Port Washington WI USA
            [email protected]
            http://www.talsystems.com

            Comment


            • #7
              Thanks,

              I'll try that. This code will run in a separate thread, so it won't use up much system time. The WAIT_TIMEOUT will be used for other system functions.
              Like periodically cleanup and other stuff. (backing up the datafiles once every day)

              I just tried some more. It seems that (only on a Network file) I get 2 WAIT_OBJECT messages, one with the original filetime,
              and one with the new filetime...
              This works:

              Code:
              #Compiler PBWin 10
              #Compile Exe
              #Dim All
              #Include "win32api.inc"
              '==================================================================================================
              Function PBMain()
                ReDim hServiceEvent(2) As Local Dword
                Local Result As Dword, DateBefore, DateAfter As DirData, sFileName As String
              
                hServiceEvent(0) = FindFirstChangeNotification("Z:\projects\meldcentrale\config\", 0, %FILE_NOTIFY_CHANGE_LAST_WRITE)
                sFileName = Dir$ ("Z:\projects\meldcentrale\config\test.log",%Normal Or %ARCHIVE, To DateBefore)
                While %True                                                                            'Endless loop
                   Result  = WaitForMultipleObjects (ByVal 1, ByVal VarPtr (hServiceEvent(0)), 0, 100)  'Wait on event
                   Select Case Result
                    Case %WAIT_TIMEOUT                                                                 'Timeout
                    Case %WAIT_OBJECT_0
                      If IsFile("Z:\projects\meldcentrale\config\test.log") Then
                        sFileName = Dir$ ("Z:\projects\meldcentrale\config\test.log", %Normal Or %ARCHIVE, To DateAfter)
                        If (DateBefore.LastWriteTime <> DateAfter.LastWriteTime ) And sFileName <> "" Then
                          MsgBox "File change event" & $Cr & _
                                     "Old time: " & Format$(DateBefore.LastWriteTime) & $Cr & _
                                     "New time: " & Format$(DateAfter.LastWriteTime),, sFileName
                          DateBefore.LastWriteTime = DateAfter.LastWriteTime
                        End If
                      End If
                      FindNextChangeNotification(hServiceEvent(0))
                  End Select
                Wend
                FindCloseChangeNotification(hServiceEvent(0))
              End Function
              Last edited by Peter Lameijn; 10 Nov 2016, 05:16 AM.
              Regards,
              Peter

              Comment


              • #8
                >This code will run in a separate thread, so it won't use up much system time.

                Call me naive, but I am pretty sure doing anything, anywhere, in any fashion uses system time.

                Doing nothing, now there's a way to not use up much system time.

                BTW, instead of piggybacking on Wait_TIMEOUT, why not make that another object to wait on, like a Waitable Timer Object Demo June 2005? Or maybe a user-defined event you can signal by user-action as demonstrated at Terminate Worker Threads Using Windows Events (and Using Thread Local Storage) Demo Dec 23 2005?

                Now you do things on WAIT_OBJECT_0 and WAIT_OBJECT_0+1 and if you get WAIT_TIMEOUT it's an error condition. No reason you can't WaitForMultipleDIFFERENTObjects.

                Using Windows Synchronization can be fun!

                MCM
                Michael Mattias
                Tal Systems (retired)
                Port Washington WI USA
                [email protected]
                http://www.talsystems.com

                Comment


                • #9
                  Hey Peter,

                  Sems that having multiple notifications from FindFirstChangeNotification & co is normal.
                  In the network file case, could a supplemental one be related to the attribute FILE_ATTRIBUTE_OFFLINE?

                  If any interest, here is some about ReadDirectoryChangesW.
                  Also, you may look at SHChangeNotifyRegister

                  Pierre

                  Code:
                  #COMPILE EXE
                  #INCLUDE "win32Api.inc"
                  
                  DECLARE FUNCTION ReadDirectoryChangesW LIB "Kernel32.dll" ALIAS "ReadDirectoryChangesW" _
                  (BYVAL hDirectory AS DWORD, BYVAL lpBuffer AS DWORD, BYVAL nBufferLength AS DWORD, _
                   BYVAL bWatchSubtree AS LONG, BYVAL dwNotifyFilter AS DWORD, lpBytesReturned AS DWORD, _
                   lpOverlapped AS OVERLAPPED, BYVAL lpCompletionRoutine AS DWORD) AS LONG
                  
                  GLOBAL hDlg AS DWORD
                  
                  %Edit01 = 101
                  '__________________________________________________________________________
                  
                  SUB TextAdd(BYVAL sText AS STRING)
                  
                   'Move the caret to the end of text.
                   SendDlgItemMessage(hDlg, %Edit01, %EM_SETSEL, -1, -1)
                  
                   sText = sText & $CRLF 'Add a CRLF if needed
                  
                   'Insert the string at caret position.
                   SendDlgItemMessage(hDlg, %Edit01, %EM_REPLACESEL, %TRUE, BYVAL STRPTR(sText))
                  
                  END SUB
                  '______________________________________________________________________________
                  
                  THREAD FUNCTION GetFolderChangeNotification(BYVAL pFolder AS STRING POINTER) AS LONG
                    LOCAL pChangeInfo        AS FILE_NOTIFY_INFORMATION POINTER
                    LOCAL sAnsiFileName      AS STRING
                    LOCAL sLog               AS STRING
                    LOCAL hFile              AS DWORD
                    LOCAL pChangeInfoBuffer  AS DWORD
                    LOCAL BufferSize         AS DWORD
                    LOCAL BytesReturnedCount AS DWORD
                    LOCAL SubFolder          AS LONG
                  
                    hFile = CreateFile(BYVAL pFolder, %FILE_LIST_DIRECTORY, _
                                       %FILE_SHARE_READ OR %FILE_SHARE_WRITE OR %FILE_SHARE_DELETE, _
                                       BYVAL %NULL, %OPEN_EXISTING, %FILE_FLAG_BACKUP_SEMANTICS, %NULL)
                    IF hFile = %INVALID_HANDLE_VALUE THEN
                      MessageBox(%HWND_DESKTOP, "CreateFile error!", "ReadDirectoryChangesW", %MB_TOPMOST)
                    ELSE
                      BufferSize        = 1023
                      pChangeInfoBuffer = GlobalAlloc(%GMEM_FIXED OR %GMEM_ZEROINIT, BufferSize)
                      SubFolder         = %FALSE
                      DO 'Wait for a change
                        ReadDirectoryChangesW(hFile, pChangeInfoBuffer, GlobalSize(pChangeInfoBuffer), SubFolder, _
                                              %FILE_NOTIFY_CHANGE_FILE_NAME      OR _
                                              %FILE_NOTIFY_CHANGE_LAST_WRITE     OR _
                                              _ %FILE_NOTIFY_CHANGE_DIR_NAME     OR _
                                              _ %FILE_NOTIFY_CHANGE_ATTRIBUTES   OR _
                                              _ %FILE_NOTIFY_CHANGE_SIZE         OR _
                                              _ %FILE_NOTIFY_CHANGE_LAST_ACCESS  OR _
                                              _ %FILE_NOTIFY_CHANGE_CREATION     OR _
                                              _ %FILE_NOTIFY_CHANGE_SECURITY     OR _
                                              0, BytesReturnedCount, BYVAL 0, 0)
                        IF BytesReturnedCount THEN
                          sLog = "BytesReturnedCount " & STR$(BytesReturnedCount) & $CRLF & $CRLF
                          pChangeInfo = pChangeInfoBuffer
                          DO
                            sAnsiFileName = NUL$(@pChangeInfo.FileNameLength)
                            WideCharToMultiByte(%CP_ACP, %NULL, BYVAL pChangeInfo + 12, @pChangeInfo.FileNameLength / 2, _
                                                BYVAL STRPTR(sAnsiFileName), @pChangeInfo.FileNameLength / 2, BYVAL %NULL, BYVAL %NULL)
                            sAnsiFileName = RTRIM$(sAnsiFileName, $NUL)
                  
                            sLog = sLog & "NextEntryOffset" & STR$(@pChangeInfo.NextEntryOffset) & $CRLF & _
                                          "Action " & CHOOSE$(@pChangeInfo.Action, _
                                                              "%FILE_ACTION_ADDED", "%FILE_ACTION_REMOVED", "%FILE_ACTION_MODIFIED", _
                                                              "%FILE_ACTION_RENAMED_OLD_NAME", "%FILE_ACTION_RENAMED_NEW_NAME") & $CRLF & _
                                          "FileNameLength " & STR$(@pChangeInfo.FileNameLength) & $CRLF & _
                                          "FileName " & sAnsiFileName & $CRLF & $CRLF
                            IF @pChangeInfo.NextEntryOffset = 0 THEN EXIT LOOP
                            pChangeInfo = pChangeInfo + @pChangeInfo.NextEntryOffset
                          LOOP
                          TextAdd(sLog & STRING$(80, 95) & $CRLF)
                        END IF
                      LOOP
                      GlobalFree(pChangeInfoBuffer)
                      CloseHandle(hFile)
                    END IF
                  
                  END FUNCTION
                  '______________________________________________________________________________
                  
                  CALLBACK FUNCTION ShowDIALOG1Proc()
                   LOCAL sFolder AS STRING
                   LOCAL hThread AS DWORD
                  
                   SELECT CASE AS LONG CBMSG
                  
                     CASE %WM_INITDIALOG
                       sFolder = "D:\Tmp\1"
                       TextAdd("Monitoring folder " & sFolder & $CRLF & STRING$(80, 95) & $CRLF)
                       PostMessage(GetDlgItem(hDlg, %Edit01), %EM_SETSEL, -1, -1)
                       THREAD CREATE GetFolderChangeNotification(BYVAL STRPTR(sFolder)) TO hThread
                       THREAD CLOSE hThread TO hThread
                  
                     CASE %WM_SIZE 'Resize Edit01 when dialog is resized
                       IF CBWPARAM <> %SIZE_MINIMIZED THEN
                         SetWindowPos(GetDlgItem(hDlg, %Edit01), 0, 5, 5, LO(WORD, CBLPARAM) - 10, HI(WORD, CBLPARAM) - 10, %SWP_NOZORDER)
                       END IF
                  
                   END SELECT
                  
                  END FUNCTION
                  '______________________________________________________________________________
                  
                  FUNCTION PBMAIN()
                   LOCAL hIcon AS DWORD
                  
                   DIALOG NEW %HWND_DESKTOP, "SHChangeNotifyRegister for disk events", , , 400, 250, %WS_POPUP OR %WS_BORDER OR _
                   %WS_DLGFRAME OR %WS_THICKFRAME OR %WS_CAPTION OR %WS_SYSMENU OR %WS_MINIMIZEBOX OR %WS_MAXIMIZEBOX OR %WS_CLIPSIBLINGS OR _
                   %WS_VISIBLE OR %DS_MODALFRAME OR %DS_3DLOOK OR %DS_NOFAILCREATE OR %DS_SETFONT, %WS_EX_CONTROLPARENT OR %WS_EX_LEFT OR _
                   %WS_EX_LTRREADING OR %WS_EX_RIGHTSCROLLBAR, TO hDlg
                  
                   hIcon = ExtractIcon(GetModuleHandle(""), "%SystemRoot%\system32\powrprof.dll", 1)
                   SetClassLong(hDlg, %GCL_HICON, hIcon)
                  
                   CONTROL ADD TEXTBOX, hDlg, %Edit01, _
                   "Create, copy, rename, edit, delete files etc. in folder...", _
                   5, 5, 940, 225, %WS_CHILD OR %WS_VISIBLE OR %WS_TABSTOP OR %WS_HSCROLL OR %WS_VSCROLL OR _
                   %ES_LEFT OR %ES_MULTILINE OR %ES_AUTOHSCROLL OR %ES_AUTOVSCROLL OR %ES_WANTRETURN OR _
                   %ES_NOHIDESEL, %WS_EX_CLIENTEDGE OR %WS_EX_LEFT OR %WS_EX_RIGHTSCROLLBAR OR %WS_EX_LTRREADING
                  
                   DIALOG SHOW MODAL hDlg, CALL ShowDIALOG1Proc
                   DestroyIcon(hIcon)
                  
                  END FUNCTION
                  '______________________________________________________________________________
                  '
                  Last edited by Pierre Bellisle; 8 May 2021, 01:53 AM.

                  Comment

                  Working...
                  X