Code: Select all
EnableExplicit
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; App Name: ReadDirectoryChanges
; Author : Sparkie
; Date : April 23, 2005
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Window Enumerations
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Enumeration
#Win_Main
EndEnumeration
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Gadget Enumerations
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Enumeration
#Button_Dir
#Text_Dir
#ListIcon_RDCW
EndEnumeration
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Constants for FILE_NOTIFY_INFORMATION (*fni) actions
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#FILE_ACTION_ADDED= 1
#FILE_ACTION_REMOVED = 2
#FILE_ACTION_MODIFIED = 3
#FILE_ACTION_RENAMED_OLD_NAME = 4
#FILE_ACTION_RENAMED_NEW_NAME = 5
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Constants for FILE_NOTIFY_INFORMATION offsets
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; This is how we access information when more than 1 action
; occurs for a file in our watched directory
#ActionOffset = 4
#CharLenOffset = 8
#FileNameOffset = 12
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; FILE_NOTIFY_INFORMATION structure
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Structure FILE_NOTIFY_INFORMATION
NextEntryOffset.l
action.l
FileNameLength.l
FileName.u[1]
EndStructure
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Globals
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Prototype Prototype_ReadDirectoryChanges(hDirectory.i, *lpBuffer, nBufferLength.l, bWatchSubtree.l, dwNotifyFilter.l, *lpBytesReturned, *lpOverlapped, *lpCompletionRoutine)
Global ReadDirectoryChanges.Prototype_ReadDirectoryChanges
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Open KERNEL32.DLL for ReadDirectoryChangesW function
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
If OpenLibrary(0, "KERNEL32.DLL")
ReadDirectoryChanges = GetFunction(0, "ReadDirectoryChangesW")
If ReadDirectoryChanges = 0
; If function not found, close KERNEL32.DLL and quit
CloseLibrary(0)
MessageRequester("Error", "ReadDirectoryChangesW function not found.")
End
EndIf
Else
; If KERNEL32.DLL not found, quit
MessageRequester("Error", "KERNEL32.DLL not found.")
End
EndIf
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Procedure to retrieve our directory changes
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; fniOffset is used for muliple action on 1 file
Procedure GetRDCWInfo(*fni.FILE_NOTIFY_INFORMATION)
Protected action.i, timeStamp$, uniLen.i, fileName$, action$
; Get a time stamp for this action
timeStamp$ = FormatDate("%mm/%dd/%yyyy %hh:%ii:%ss", Date())
; Get the file name
fileName$ = PeekS(@*fni\FileName[0], *fni\FileNameLength / 2)
; Set the string to be displayed for the action
Select *fni\action
Case #FILE_ACTION_ADDED
action$ = "was added to directory."
Case #FILE_ACTION_REMOVED
action$ = "was removed from directory."
Case #FILE_ACTION_MODIFIED
action$ = "attribute or time stamp modified."
Case #FILE_ACTION_RENAMED_OLD_NAME
action$ = "was renamed."
Case #FILE_ACTION_RENAMED_NEW_NAME
action$ = "is the new file name."
EndSelect
; Add the info to the ListIconGadget
If Not *fni\action = #FILE_ACTION_MODIFIED ; For now, I am content with knowing when an entry was created, deleted or renamed.
AddGadgetItem(#ListIcon_RDCW, -1, fileName$ + #LF$ + action$ + #LF$ + timeStamp$)
EndIf
EndProcedure
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Procedure (Thread) to set the ReadDirectoryChangesW calls
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Procedure RDCW(*FilePath)
Protected volume.i, size.l, nextEntry.i, previousEntry.i, FilePath$, HDVolume$, hDir.i, *fniHelp
Protected *fni.FILE_NOTIFY_INFORMATION
FilePath$ = PeekS(*FilePath)
HDVolume$ = Left(FilePath$, 1)
; Get a handle to our watched directory
hDir = CreateFile_(FilePath$, #FILE_LIST_DIRECTORY|#GENERIC_WRITE, #FILE_SHARE_WRITE | #FILE_SHARE_READ | #FILE_SHARE_DELETE, #Null, #OPEN_EXISTING, #FILE_FLAG_BACKUP_SEMANTICS, #Null)
; Allocate memory for our FILE_NOTIFY_INFORMATION pointer
*fni = AllocateMemory(4096) ; DWORD aligned !!!
If *fni
; Loop calls to ReadDirectoryChangesW
; We're watching for rename, delete, create, write
;Create a file handle to the Volume being watched / not the directory but the main Volume
volume = CreateFile_( "\\.\" + HDVolume$ + ":\0", #GENERIC_WRITE, #FILE_SHARE_WRITE, #Null, #OPEN_EXISTING, 0, #Null)
; FlushFileBuffers before call
FlushFileBuffers_(volume)
; now do a call
While ReadDirectoryChanges(hDir, *fni, 1024, #True, #FILE_NOTIFY_CHANGE_FILE_NAME | #FILE_NOTIFY_CHANGE_DIR_NAME | #FILE_NOTIFY_CHANGE_LAST_WRITE, @size, #Null, #Null)
; Get the info for offset 0
*fniHelp = *fni
GetRDCWInfo(*fniHelp)
; See if there are more entries for this call
nextEntry = *fni\NextEntryOffset
; If so, get the info
While nextEntry > 0
If previousEntry = nextEntry
Break
EndIf
*fniHelp + *fni\NextEntryOffset
GetRDCWInfo(*fniHelp)
; See if there are more entries for this call
nextEntry = PeekL(*fni + nextEntry)
previousEntry = nextEntry
Wend
; No more entries, go to next ReadDirectoryChangesW call after flushing buffers again
FlushFileBuffers_(volume)
Wend
FreeMemory(*fni)
EndIf
CloseHandle_(hDir)
EndProcedure
Define event.i, myPath$, myThread.i, quit.i
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Main window and gadgets
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
If OpenWindow(0, 0, 0, 545, 350, "ReadDirectoryChangesW", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
ButtonGadget(#Button_Dir, 5, 5, 150, 20, "Select Directory to watch")
TextGadget(#Text_Dir, 5, 5, 535, 20, "")
DisableGadget(#Text_Dir, 1)
ListIconGadget(#ListIcon_RDCW, 5, 40, 535, 300, "File", 200, #PB_ListIcon_FullRowSelect | #PB_ListIcon_AlwaysShowSelection | #PB_ListIcon_GridLines)
AddGadgetColumn(#ListIcon_RDCW, 1, "Action", 200)
AddGadgetColumn(#ListIcon_RDCW, 2, "Time", 130)
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Main event loop
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Repeat
event = WaitWindowEvent()
Select event
Case #PB_Event_Gadget
Select EventGadget()
Case #Button_Dir
; Button pressed for getting directory to watch
myPath$ = PathRequester("Select a Folder to watch", "c:\")
; If there is a valid path, start our ReadDirectoryChangesW thread
If myPath$
; check for drive letter
If Mid(myPath$, 2, 1) <> ":"
CloseLibrary(0)
MessageRequester("Error", "Only monitor harddrives whith letters no UNC for now.")
End
EndIf
DisableGadget(#Button_Dir, 1)
; --> Our thread for change notifications with the directory handle
;myThread = CreateThread(@RDCW(), hDir)
myThread = CreateThread(@RDCW(), @myPath$)
DisableGadget(#Text_Dir, 0)
SetGadgetText(#Text_Dir, myPath$ + " is being watched.")
EndIf
EndSelect
Case #PB_Event_CloseWindow
; Kill our ReadDirectoryChangesW thread
KillThread(myThread)
; If KERNEL32.DLL is open, close it
If IsLibrary(0)
CloseLibrary(0)
EndIf
quit = #True
EndSelect
Until quit
EndIf