KaFu Posted April 22, 2012 Posted April 22, 2012 HiHo Forum ,I was playing around with the _WinAPI_ShellChangeNotifyRegister() function from (required for the example) and hit a wall, would be great if this one could be answered.Looking around the net, I found a definition on how to register the notifications for all filesystem objects and not just specified directories (see my adjusted _WinAPI_ShellChangeNotifyRegisterEx() below).This works fine (commented part in the WM_SHELLCHANGENOTIFY function below), but I wanted it to take a step further. I've found this article:http://koti.mbnet.fi/vaultec/files/miscellaneous/undocw95/notify.html[font="Times New Roman"][size="3"]This all seems simple enough, but on NT there is a bit of a problem. You can't just send a message to a window in a different process, and still expect the structure pointer contained in that message to be accessible. To get around this, NT actually dumps all the data into a memory-mapped file, and then sends the memory-map handle and a process id as the parameters to the message. In order to remain compatible with Windows 95, some- body obviously has to extract the information from that memory-map on the other side. The way this works is that NT creates a hidden 'proxy' window whenever you call SHChangeNotifyRegister. It is the proxy window that receives the notification message containing the memory- map. Its message handler then extracts all the information, before passing on the expected data structure to your win- dow. Of course, this is not exactly efficient, which is where the [size="-1"]SHCNF_NO_PROXY[/size] flag comes in. By specifying that flag, you are telling NT not to create the proxy window, so the memory-map handle gets passed directly to your win- dow. It's then up to you to extract the relevant information from the memory-map. Fortunately there are two functions that do all the work for you:[/size][/font]The SHCNF_NO_PROXY flag mentioned equals the SHCNRF_NEWDELIVERY flag in the MSDN documentation.... and there's my problem. According to the article the notification is sent via a memory mapped file, which can be accessed via a call to SHChangeNotification_Lock(), and this part currently fails for me, I don't get the pointer to the PIDL.expandcollapse popup#region ;**** Directives created by AutoIt3Wrapper_GUI **** #AutoIt3Wrapper_UseUpx=n #AutoIt3Wrapper_Res_requestedExecutionLevel=asInvoker #endregion ;**** Directives created by AutoIt3Wrapper_GUI **** #include <APIConstants.au3> #include <WinAPIEx.au3> #include <GUIConstantsEx.au3> HotKeySet("{ESC}", "_Exit") Global $hWnd, $iMsg, $ID ; http://koti.mbnet.fi/vaultec/files/miscellaneous/undocw95/notify.html $hWnd = GUICreate('') $iMsg = _WinAPI_RegisterWindowMessage('SHELLCHANGENOTIFY') GUIRegisterMsg($iMsg, 'WM_SHELLCHANGENOTIFY') $ID = _WinAPI_ShellChangeNotifyRegisterEx($hWnd, $iMsg, $SHCNE_ALLEVENTS, BitOR($SHCNRF_INTERRUPTLEVEL, $SHCNRF_SHELLLEVEL, $SHCNRF_RECURSIVEINTERRUPT, $SHCNRF_NEWDELIVERY), "", 1) If @error Then MsgBox(16, 'Error', 'Window does not registered.') Exit EndIf OnAutoItExitRegister('OnAutoItExit_ShellChangeNotifyRegister') While 1 Sleep(1000) WEnd Func _Exit() Exit EndFunc ;==>_Exit Func WM_SHELLCHANGENOTIFY($hWnd, $iMsg, $wParam, $lParam) #cs Local $Path = _WinAPI_ShellGetPathFromIDList(DllStructGetData(DllStructCreate('dword Item1; dword Item2', $wParam), 'Item1')) If $Path Then ConsoleWrite(TimerInit() & @TAB & 'Event: 0x' & Hex($lParam) & ' | Path: ' & $Path & @CR) Else ConsoleWrite(TimerInit() & @TAB & 'Event: 0x' & Hex($lParam) & @CR) EndIf #ce ConsoleWrite("+ " & $hWnd & @TAB & $iMsg & @TAB & $wParam & @TAB & $lParam & @CRLF) Local $iRes = DllCall("shell32.dll", "handle", "SHChangeNotification_Lock", "handle", $wParam, "dword", $lParam, "ptr", "", "ulong", "") Local $PIDLIST_ABSOLUTE = $iRes[3] Local $plEvent = $iRes[4] ConsoleWrite("! 1: " & $iRes[0] & @TAB & "2: " & $PIDLIST_ABSOLUTE & @TAB & "3: " & $plEvent & @CRLF) Local $Path = _WinAPI_ShellGetPathFromIDList(DllStructGetData(DllStructCreate('dword Item1; dword Item2', $PIDLIST_ABSOLUTE), 'Item1')) ; Local $Path = _WinAPI_ShellGetPathFromIDList($PIDLIST_ABSOLUTE) ConsoleWrite("- Path: " & $Path & @CRLF) $iRes = DllCall("shell32.dll", "int", "SHChangeNotification_Unlock", "HANDLE", $iRes[0]) ConsoleWrite($iRes[0] & @CRLF & "============" & @CRLF & @CRLF) EndFunc ;==>WM_SHELLCHANGENOTIFY Func OnAutoItExit_ShellChangeNotifyRegister() _WinAPI_ShellChangeNotifyDeregister($ID) EndFunc ;==>OnAutoItExit_ShellChangeNotifyRegister Func _WinAPI_ShellChangeNotifyRegisterEx($hWnd, $iMsg, $iEvents, $iSources, $aPaths, $fRecursive = 0) Local $tEntry, $Path = $aPaths, $Struct = '', $iCount If $aPaths Then If IsArray($aPaths) Then If UBound($aPaths, 2) Then Return SetError(1, 0, 0) EndIf Else Dim $aPaths[1] = [$Path] EndIf For $i = 0 To UBound($aPaths) - 1 If Not _WinAPI_PathIsDirectory($aPaths[$i]) Then Return SetError(1, 0, 0) EndIf Next For $i = 0 To UBound($aPaths) - 1 $Struct &= 'ptr;int;' Next $tEntry = DllStructCreate($Struct) For $i = 0 To UBound($aPaths) - 1 $aPaths[$i] = _WinAPI_ShellILCreateFromPath(_WinAPI_PathSearchAndQualify($aPaths[$i])) DllStructSetData($tEntry, 2 * $i + 1, $aPaths[$i]) DllStructSetData($tEntry, 2 * $i + 2, $fRecursive) Next Local $Ret = DllCall('shell32.dll', 'ulong', 'SHChangeNotifyRegister', 'hwnd', $hWnd, 'int', $iSources, 'long', $iEvents, 'uint', $iMsg, 'int', UBound($aPaths), 'ptr', DllStructGetPtr($tEntry)) Else $tEntry = DllStructCreate('ptr;int;') DllStructSetData($tEntry, 1, "") DllStructSetData($tEntry, 2, 1) Local $Ret = DllCall('shell32.dll', 'ulong', 'SHChangeNotifyRegister', 'hwnd', $hWnd, 'int', $iSources, 'long', $iEvents, 'uint', $iMsg, 'int', 1, 'ptr', DllStructGetPtr($tEntry)) EndIf If (Not @error) And ($Ret[0]) Then $Ret = $Ret[0] Else $Ret = 0 EndIf For $i = 0 To UBound($aPaths) - 1 _WinAPI_CoTaskMemFree($aPaths[$i]) Next Return SetError(Number($Ret = 0), 0, $Ret) EndFunc ;==>_WinAPI_ShellChangeNotifyRegisterEx 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 (2025-May-18) - THE file info and duplicates search tool SSD - Set Sound Device (2017-Sep-16)
KaFu Posted April 23, 2012 Author Posted April 23, 2012 Works now for me. And as mentioned in the MSDN documentation for SHChangeNotifyRegister, "SHCNRF_NewDelivery: We recommend this flag because it provides a more robust delivery method. All clients should specify this flag."expandcollapse popup#region ;**** Directives created by AutoIt3Wrapper_GUI **** #AutoIt3Wrapper_UseUpx=n #AutoIt3Wrapper_Res_requestedExecutionLevel=asInvoker #endregion ;**** Directives created by AutoIt3Wrapper_GUI **** #include <APIConstants.au3> #include <WinAPIEx.au3> #include <GUIConstantsEx.au3> HotKeySet("{ESC}", "_Exit") Global $hWnd, $iMsg, $ID ; [url="http://koti.mbnet.fi/vaultec/files/miscellaneous/undocw95/notify.html"]http://koti.mbnet.fi/vaultec/files/miscellaneous/undocw95/notify.html[/url] $hWnd = GUICreate('') $iMsg = _WinAPI_RegisterWindowMessage('SHELLCHANGENOTIFY') GUIRegisterMsg($iMsg, 'WM_SHELLCHANGENOTIFY') $ID = _WinAPI_ShellChangeNotifyRegisterEx($hWnd, $iMsg, $SHCNE_ALLEVENTS, BitOR($SHCNRF_INTERRUPTLEVEL, $SHCNRF_SHELLLEVEL, $SHCNRF_RECURSIVEINTERRUPT, $SHCNRF_NEWDELIVERY), "", 1) If @error Then MsgBox(16, 'Error', 'Window does not registered.') Exit EndIf OnAutoItExitRegister('OnAutoItExit_ShellChangeNotifyRegister') While 1 Sleep(1000) WEnd Func _Exit() Exit EndFunc ;==>_Exit Func WM_SHELLCHANGENOTIFY($hWnd, $iMsg, $wParam, $lParam) #cs Local $Path = _WinAPI_ShellGetPathFromIDList(DllStructGetData(DllStructCreate('dword Item1; dword Item2', $wParam), 'Item1')) If $Path Then ConsoleWrite(TimerInit() & @TAB & 'Event: 0x' & Hex($lParam) & ' | Path: ' & $Path & @CR) Else ConsoleWrite(TimerInit() & @TAB & 'Event: 0x' & Hex($lParam) & @CR) EndIf #ce ; ConsoleWrite("+ " & $hWnd & @TAB & $iMsg & @TAB & $wParam & @TAB & $lParam & @CRLF) ;Local $PIDLIST_ABSOLUTE, $plEvent Local $iRes = DllCall("shell32.dll", "hwnd", "SHChangeNotification_Lock", "wparam", $wParam, "lparam", $lParam, "ptr*", "", "ulong*", "") Local $PIDLIST_ABSOLUTE = $iRes[3] Local $plEvent = $iRes[4] ; ConsoleWrite("! 1: " & $iRes[0] & @TAB & "2: " & $PIDLIST_ABSOLUTE & @TAB & "3: " & $plEvent & @CRLF) Local $Path = _WinAPI_ShellGetPathFromIDList(DllStructGetData(DllStructCreate('dword Item1; dword Item2', $PIDLIST_ABSOLUTE), 'Item1')) ; Local $Path = _WinAPI_ShellGetPathFromIDList($PIDLIST_ABSOLUTE) ConsoleWrite("+ Event: 0x" & Hex($plEvent) & @tab & "Path: " & $Path & @CRLF) $iRes = DllCall("shell32.dll", "int", "SHChangeNotification_Unlock", "HANDLE", $iRes[0]) ; ConsoleWrite($iRes[0] & @CRLF & "============" & @CRLF & @CRLF) EndFunc ;==>WM_SHELLCHANGENOTIFY Func OnAutoItExit_ShellChangeNotifyRegister() _WinAPI_ShellChangeNotifyDeregister($ID) EndFunc ;==>OnAutoItExit_ShellChangeNotifyRegister Func _WinAPI_ShellChangeNotifyRegisterEx($hWnd, $iMsg, $iEvents, $iSources, $aPaths, $fRecursive = 0) Local $tEntry, $Path = $aPaths, $Struct = '', $iCount If $aPaths Then If IsArray($aPaths) Then If UBound($aPaths, 2) Then Return SetError(1, 0, 0) EndIf Else Dim $aPaths[1] = [$Path] EndIf For $i = 0 To UBound($aPaths) - 1 If Not _WinAPI_PathIsDirectory($aPaths[$i]) Then Return SetError(1, 0, 0) EndIf Next For $i = 0 To UBound($aPaths) - 1 $Struct &= 'ptr;int;' Next $tEntry = DllStructCreate($Struct) For $i = 0 To UBound($aPaths) - 1 $aPaths[$i] = _WinAPI_ShellILCreateFromPath(_WinAPI_PathSearchAndQualify($aPaths[$i])) DllStructSetData($tEntry, 2 * $i + 1, $aPaths[$i]) DllStructSetData($tEntry, 2 * $i + 2, $fRecursive) Next Local $Ret = DllCall('shell32.dll', 'ulong', 'SHChangeNotifyRegister', 'hwnd', $hWnd, 'int', $iSources, 'long', $iEvents, 'uint', $iMsg, 'int', UBound($aPaths), 'ptr', DllStructGetPtr($tEntry)) Else $tEntry = DllStructCreate('ptr;int;') DllStructSetData($tEntry, 1, "") DllStructSetData($tEntry, 2, 1) Local $Ret = DllCall('shell32.dll', 'ulong', 'SHChangeNotifyRegister', 'hwnd', $hWnd, 'int', $iSources, 'long', $iEvents, 'uint', $iMsg, 'int', 1, 'ptr', DllStructGetPtr($tEntry)) EndIf If (Not @error) And ($Ret[0]) Then $Ret = $Ret[0] Else $Ret = 0 EndIf For $i = 0 To UBound($aPaths) - 1 _WinAPI_CoTaskMemFree($aPaths[$i]) Next Return SetError(Number($Ret = 0), 0, $Ret) EndFunc ;==>_WinAPI_ShellChangeNotifyRegisterEx 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 (2025-May-18) - THE file info and duplicates search tool SSD - Set Sound Device (2017-Sep-16)
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