wraithdu Posted September 26, 2008 Posted September 26, 2008 (edited) I've been messing with this all night, and finally got it working. The SHChangeNotifyRegister() function allows your GUI window to receive any of the notifications available from the SHChangeNotify() function - http://msdn.microsoft.com/en-us/library/bb762118(VS.85).aspxIn my example, I get notified of all file and folder deletes for the folder C:\test. The folder must exist before calling the function. Set the fRecursive parameter of the shnotifystruct to 1 to be notified of changes recursively from the root folder. Set the pidl parameter to 0 to receive notifications from the entire filesystem.You must call CoTaskMemFree() for each PIDL created with ILCreateFromPath(), and SHChangeNotifyDeregister() for each path registered with SHChangeNotifyRegister(). You can register more than one path to monitor multiple folders.In the notification, lParam holds the notifications message, and wParam holds a pointer to a SHNOTIFY struct which holds 2 dword values. dwItem1 and dwItem2 contain information according to the message received, based on the SHChangeNotify() function (see web page above for different values).To see it work, run the script, then create a new file in C:\test and delete it.expandcollapse popup#include <WinAPI.au3> Opt("TrayAutoPause", 0) Global Const $SHCNE_RENAMEITEM = 0x0001 Global Const $SHCNE_CREATE = 0x0002 Global Const $SHCNE_DELETE = 0x0004 Global Const $SHCNE_MKDIR = 0x0008 Global Const $SHCNE_RMDIR = 0x0010 Global Const $SHCNE_UPDATEDIR = 0x1000 Global Const $SHCNE_UPDATEITEM = 0x2000 Global Const $SHCNE_UPDATEIMAGE = 0x8000 Global Const $SHCNE_RENAMEFOLDER = 0x20000 Global Const $SHCNE_ALLEVENTS = 0x7FFFFFFF OnAutoItExitRegister("OnAutoItExit") $SHNOTIFY = _WinAPI_RegisterWindowMessage("shchangenotifymsg") $gui = GUICreate("") GUIRegisterMsg($SHNOTIFY, "MY_SHNOTIFY") If Not FileExists("C:\test") Then DirCreate("C:\test") $ppidl = DllCall("shell32.dll", "ptr", "ILCreateFromPathW", "wstr", "C:\test") ConsoleWrite($ppidl[0] & @CRLF) $shnotifystruct = DllStructCreate("ptr pidl; int fRecursive") DllStructSetData($shnotifystruct, "pidl", $ppidl[0]) DllStructSetData($shnotifystruct, "fRecursive", 1) $register = DllCall("shell32.dll", "int", "SHChangeNotifyRegister", "hwnd", $gui, _ "int", 0x0003, _ "long", BitOR($SHCNE_DELETE, $SHCNE_RMDIR), _ "uint", $SHNOTIFY, _ "int", 1, _ "ptr", DllStructGetPtr($shnotifystruct)) DllCall("ole32.dll", "none", "CoTaskMemFree", "ptr", $ppidl[0]) ConsoleWrite("register: " & $register[0] & @CRLF) While 1 Sleep(1000) WEnd Func OnAutoItExit() $ret = DllCall("shell32.dll", "int", "SHChangeNotifyDeregister", "ulong", $register[0]) ConsoleWrite("deregister: " & $ret[0] & @CRLF) EndFunc Func MY_SHNOTIFY($hWnd, $Msg, $wParam, $lParam) Local $path ConsoleWrite("notify: " & $lParam & @CRLF) $path = DllStructCreate("dword dwItem1; dword dwItem2", $wParam) $ret = DllCall("shell32.dll", "int", "SHGetPathFromIDListW", "ptr", DllStructGetData($path, "dwItem1"), "wstr", "") ConsoleWrite($ret[2] & @CRLF) EndFunc Edited April 27, 2010 by wraithdu
wraithdu Posted September 26, 2008 Author Posted September 26, 2008 (edited) Interesting note - AutoIt's FileMove() func does not trigger the SHCNE_CREATE notification in the destination dir. DirMove(), on the other hand, does trigger the SHCNE_MKDIR notification in the destination dir. AutoIt bug maybe? Edited September 26, 2008 by wraithdu
wraithdu Posted September 26, 2008 Author Posted September 26, 2008 (edited) Here's an example of registering more than one folder at a time. It was tricky to figure out how exactly the function wanted the array, but I finally got it. expandcollapse popup#include <WinAPI.au3> Opt("TrayAutoPause", 0) Global Const $SHCNE_RENAMEITEM = 0x0001 Global Const $SHCNE_CREATE = 0x0002 Global Const $SHCNE_DELETE = 0x0004 Global Const $SHCNE_MKDIR = 0x0008 Global Const $SHCNE_RMDIR = 0x0010 Global Const $SHCNE_UPDATEDIR = 0x1000 Global Const $SHCNE_UPDATEITEM = 0x2000 Global Const $SHCNE_UPDATEIMAGE = 0x8000 Global Const $SHCNE_RENAMEFOLDER = 0x20000 Global Const $SHCNE_ALLEVENTS = 0x7FFFFFFF OnAutoItExitRegister("OnAutoItExit") $SHNOTIFY = _WinAPI_RegisterWindowMessage("shchangenotifymsg") $gui = GUICreate("") GUIRegisterMsg($SHNOTIFY, "MY_SHNOTIFY") If Not FileExists("C:\test") Then DirCreate("C:\test") If Not FileExists("C:\test2") Then DirCreate("C:\test2") $ppidl = DllCall("shell32.dll", "ptr", "ILCreateFromPathW", "wstr", "C:\test") ConsoleWrite($ppidl[0] & @CRLF) $ppidl2 = DllCall("shell32.dll", "ptr", "ILCreateFromPathW", "wstr", "C:\test2") ConsoleWrite($ppidl2[0] & @CRLF) $shArray = DllStructCreate("double; double"); 16 bytes = two 8 byte shnotifystructs in an array ; == this could also be done as DllStructCreate("double[2]"), $pshArray = DllStructGetPtr($shArray) ; == then below would be ; == $shnotifystruct = DllStructCreate("ptr pidl; int fRecursive", $pshArray) ; == $shnotifystruct2 = DllStructCreate("ptr pidl; int fRecursive", $pshArray + 8) -> ptr to array plus 8 bytes ; == for many folders, either method could be achieved by use of loops $shnotifystruct = DllStructCreate("ptr pidl; int fRecursive", DllStructGetPtr($shArray, 1)); first struct, first element ConsoleWrite("error1: " & @error & @CRLF) DllStructSetData($shnotifystruct, "pidl", $ppidl[0]) DllStructSetData($shnotifystruct, "fRecursive", 0) $shnotifystruct2 = DllStructCreate("ptr pidl; int fRecursive", DllStructGetPtr($shArray, 2)); second struct, second element ConsoleWrite("error2: " & @error & @CRLF) DllStructSetData($shnotifystruct2, "pidl", $ppidl2[0]) DllStructSetData($shnotifystruct2, "fRecursive", 0) $register = DllCall("shell32.dll", "int", "SHChangeNotifyRegister", "hwnd", $gui, _ "int", 0x0003, _ "long", BitOR($SHCNE_CREATE, $SHCNE_DELETE, $SHCNE_MKDIR, $SHCNE_RMDIR), _ "uint", $SHNOTIFY, _ "int", 2, _; number of shnotifystructs in the array "ptr", DllStructGetPtr($shArray)); ptr to the array ; free each created PIDL DllCall("ole32.dll", "none", "CoTaskMemFree", "ptr", $ppidl[0]) DllCall("ole32.dll", "none", "CoTaskMemFree", "ptr", $ppidl2[0]) $shArray = 0 $shnotifystruct = 0 $shnotifystruct2 = 0 ConsoleWrite("register: " & $register[0] & @CRLF) While 1 Sleep(1000) WEnd Func OnAutoItExit() ; deregister the window $ret = DllCall("shell32.dll", "int", "SHChangeNotifyDeregister", "ulong", $register[0]) ConsoleWrite("deregister: " & $ret[0] & @CRLF) EndFunc Func MY_SHNOTIFY($hWnd, $Msg, $wParam, $lParam) Local $path ConsoleWrite("notify: " & $lParam & @CRLF) $path = DllStructCreate("dword dwItem1; dword dwItem2", $wParam) $ret = DllCall("shell32.dll", "int", "SHGetPathFromIDListW", "ptr", DllStructGetData($path, "dwItem1"), "wstr", "") ConsoleWrite($ret[2] & @CRLF) EndFunc Edited April 27, 2010 by wraithdu
cloim Posted November 13, 2008 Posted November 13, 2008 (edited) good job! it's really cooooooooooool!! Edited November 13, 2008 by cloim
JRSmile Posted April 27, 2010 Posted April 27, 2010 (edited) a perfect code so far, but i can't get it to register file changes even $SHCNE_ALLEVENTS does not the trick, did i miss something? or isn't it possible to track file internal changes ( like a modified logfile) with it? Edited April 27, 2010 by JRSmile $a=StringSplit("547275737420796F757220546563686E6F6C75737421","") For $b=1 To UBound($a)+(-1*-1*-1)step(2^4/8);&$b+=1*2/40*µ&Asc(4) Assign("c",Eval("c")&Chr(Dec($a[$b]&$a[$b+1])));''Chr("a")&"HI" Next ;time_U&r34d,ths,U-may=get$the&c.l.u.e;b3st-regards,JRSmile; MsgBox(0x000000,"",Eval("c"));PiEs:d0nt+*b3.s4d.4ft3r.1st-try:-)
wraithdu Posted April 27, 2010 Author Posted April 27, 2010 No, you didn't miss anything. I can't get it to notify either. This seems like a very unreliable function for file/folder level stuff. Shame...
wraithdu Posted April 27, 2010 Author Posted April 27, 2010 Edit... aha! Change the second param of the DllCall to '"int", 0x0003' and things start to make more sense. You'll have to work out how to interpret the different messages from the SHChangeNotify documentation, as my example doesn't handle all the possibilities properly.
JRSmile Posted April 27, 2010 Posted April 27, 2010 i can't see what 0x0003 would do, since: SHCNRF_InterruptLevel 0x0001. Interrupt level notifications from the file system. SHCNRF_ShellLevel 0x0002. Shell-level notifications from the shell. Interrupts are file system and shell are the same we got already, are interrupts fired when modifying a file?i ask because i did not get any when testing any idea? $a=StringSplit("547275737420796F757220546563686E6F6C75737421","") For $b=1 To UBound($a)+(-1*-1*-1)step(2^4/8);&$b+=1*2/40*µ&Asc(4) Assign("c",Eval("c")&Chr(Dec($a[$b]&$a[$b+1])));''Chr("a")&"HI" Next ;time_U&r34d,ths,U-may=get$the&c.l.u.e;b3st-regards,JRSmile; MsgBox(0x000000,"",Eval("c"));PiEs:d0nt+*b3.s4d.4ft3r.1st-try:-)
KaFu Posted April 27, 2010 Posted April 27, 2010 I guess seangriffin gave the answer here...Shell NotificationsHere's an excerpt:"...The Origin of EventsSo now, you know how to receive any of these shell notifications that are floating around, but who is actually generating them? According to the documentation, 'An application should use this function (SHChangeNotify) if it performs an action that may affect the shell'. But that seems to be a bit of wishful thinking. I can't imagine there are many applica tion developers that really give a damn whether the shell is kept informed of their actions......The result is that these notifications tend to be a little bit unreliable. The likelyhood of you getting an event for something, may depend on what explorer windows happen to be open at the time. The shell also only has a 10 item event buffer, and may replace some events with a generic SHCNE_UPDATEDIR in case of an overflow. In short: don't depend on these notifications for any mission-critical applications..." Â OS: Win10-22H2 - 64bit - German, AutoIt Version: 3.3.16.1, AutoIt Editor: SciTE, Website: https://funk.eu AMT - Auto-Movie-Thumbnailer (2024-Oct-13)Â BIC - Batch-Image-Cropper (2023-Apr-01) COP - Color Picker (2009-May-21) DCS - Dynamic Cursor Selector (2024-Oct-13) HMW - Hide my Windows (2024-Oct-19) HRC - HotKey Resolution Changer (2012-May-16)Â ICU - Icon Configuration Utility (2018-Sep-16) SMF - Search my Files (2024-Oct-20) - THE file info and duplicates search tool SSD - Set Sound Device (2017-Sep-16)
JRSmile Posted April 27, 2010 Posted April 27, 2010 I guess seangriffin gave the answer here...Thats soooo typical for a non perfect developer world ... $a=StringSplit("547275737420796F757220546563686E6F6C75737421","") For $b=1 To UBound($a)+(-1*-1*-1)step(2^4/8);&$b+=1*2/40*µ&Asc(4) Assign("c",Eval("c")&Chr(Dec($a[$b]&$a[$b+1])));''Chr("a")&"HI" Next ;time_U&r34d,ths,U-may=get$the&c.l.u.e;b3st-regards,JRSmile; MsgBox(0x000000,"",Eval("c"));PiEs:d0nt+*b3.s4d.4ft3r.1st-try:-)
wraithdu Posted April 28, 2010 Author Posted April 28, 2010 Once I changed that value from 0x0002 to 0x0003, I started getting the notifications I was missing while modifying a file (text file -> change -> save), and notifications for renames, etc. I tested with Explorer and Notepad++. I dont know what the InterruptLevel really means either, but it gave me my missing notifications.
JRSmile Posted April 28, 2010 Posted April 28, 2010 Ok i tested with the windows notepad... And there were no notifies ... So bad programs like micdosoft own tools do not notify :-) so i will have to test with the app i use in production :-) good news thank you very much. $a=StringSplit("547275737420796F757220546563686E6F6C75737421","") For $b=1 To UBound($a)+(-1*-1*-1)step(2^4/8);&$b+=1*2/40*µ&Asc(4) Assign("c",Eval("c")&Chr(Dec($a[$b]&$a[$b+1])));''Chr("a")&"HI" Next ;time_U&r34d,ths,U-may=get$the&c.l.u.e;b3st-regards,JRSmile; MsgBox(0x000000,"",Eval("c"));PiEs:d0nt+*b3.s4d.4ft3r.1st-try:-)
wraithdu Posted April 28, 2010 Author Posted April 28, 2010 When I switch to SHCNE_ALLEVENTS, I'm getting SHCNE_UPDATEITEM events for changing text file contents in Notepad. I'm on Win7 Ult 32-bit.
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now