KaFu Posted April 22, 2012 Share 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 (2022-Nov-26) BIC - Batch-Image-Cropper (2023-Apr-01) COP - Color Picker (2009-May-21) DCS - Dynamic Cursor Selector (2024-Feb-16) HMW - Hide my Windows (2018-Sep-16) HRC - HotKey Resolution Changer (2012-May-16) ICU - Icon Configuration Utility (2018-Sep-16) SMF - Search my Files (2023-Jun-03) - THE file info and duplicates search tool SSD - Set Sound Device (2017-Sep-16) Link to comment Share on other sites More sharing options...
KaFu Posted April 23, 2012 Author Share 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 (2022-Nov-26) BIC - Batch-Image-Cropper (2023-Apr-01) COP - Color Picker (2009-May-21) DCS - Dynamic Cursor Selector (2024-Feb-16) HMW - Hide my Windows (2018-Sep-16) HRC - HotKey Resolution Changer (2012-May-16) ICU - Icon Configuration Utility (2018-Sep-16) SMF - Search my Files (2023-Jun-03) - THE file info and duplicates search tool SSD - Set Sound Device (2017-Sep-16) Link to comment Share on other sites More sharing options...
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