Jump to content

jackchen

Active Members
  • Posts

    23
  • Joined

  • Last visited

Everything posted by jackchen

  1. OK,I've made my "Chrome_KeepLastTab.au3" work.This script adds some features to Chrome browser: 1. Double click on a tab to close the tab. 2. Keep last tab:This script monitors your mouse clicks and hotkeys,if you are about to close the last tab within Chrome(click close button on the last tab, middle click/double click on the last tab or press Ctrl + w or Ctrl + {F4}), a new tab will be open and then the old tab be closed. #include <WindowsConstants.au3> #include <WinAPI.au3> #include "CUIAutomation2.au3" #AutoIt3Wrapper_UseX64=Y ;Should be used for stuff like tagpoint having right struct etc. when running on a 64 bits os ConsoleWrite("@OSArch: " & @OSArch & ", @AutoItX64: " & @AutoItX64 & @CRLF) Global $DoubleClickTime = 500 Global $UIA_oUIAutomation ; The main library core CUI automation reference Global $hMouseEvent, $hMouseHook Global $aMouseEvent[2] Global $KeepLastTab = True ; settings from ini file If $KeepLastTab Then ;The main object with acces to the windows automation api 3.0 $UIA_oUIAutomation = ObjCreateInterface($sCLSID_CUIAutomation, $sIID_IUIAutomation, $dtagIUIAutomation) If IsObj($UIA_oUIAutomation) Then HotKeySet("^w", "Hotkey_Event") ; Ctrl + w to close tab HotKeySet("^{F4}", "Hotkey_Event") ; Ctrl + {F4} to close tab $DoubleClickTime = DllCall("user32.dll", "uint", "GetDoubleClickTime")[0] OnAutoItExitRegister("UnhookMouse") ; Register mouse events callback $hMouseEvent = DllCallbackRegister("Mouse_Event", "int", "int;ptr;ptr") $hMouseHook = _WinAPI_SetWindowsHookEx($WH_MOUSE_LL, DllCallbackGetPtr($hMouseEvent), _WinAPI_GetModuleHandle(0)) EndIf EndIf While 1 Sleep(100) WEnd ToolTip("") ; https://www.autoitscript.com/forum/topic/103362-monitoring-mouse-events/ Func Mouse_Event($nCode, $wParam, $lParam) Local $info, $mouseData, $time, $timeDiff If $nCode < 0 Then Return _WinAPI_CallNextHookEx($hMouseHook, $nCode, $wParam, $lParam) ; Continue processing EndIf $tPoint = _WinAPI_GetMousePos() $hWnd = _WinAPI_WindowFromPoint($tPoint) ; if mouse is on the widget window(class: Chrome_RenderWidgetHostHWND), ; use $hWnd = _WinAPI_GetParent($hWnd) to get the parent Chrome window If Not StringInStr(_WinAPI_GetClassName($hWnd), "Chrome_WidgetWin_") Then ; Chrome_WidgetWin_1: Chrome window ; Chrome_WidgetWin_2: Chrome menu ; ignore non Chrome window Return _WinAPI_CallNextHookEx($hMouseHook, $nCode, $wParam, $lParam) EndIf ToolTip($hWnd & " - " & _WinAPI_GetClassName($hWnd)) ;$tagPOINT = "struct;long X;long Y;endstruct" Local Const $MSLLHOOKSTRUCT = $tagPOINT & ";dword mouseData;dword flags;dword time;ulong_ptr dwExtraInfo" $info = DllStructCreate($MSLLHOOKSTRUCT, $lParam) $mouseData = DllStructGetData($info, 3) $time = DllStructGetData($info, 5) $timeDiff = $time - $aMouseEvent[1] Local $block Switch $wParam Case $WM_LBUTTONUP $aMouseEvent[1] = $time If $aMouseEvent[0] = "LClick" And ($timeDiff) < $DoubleClickTime Then $aMouseEvent[0] = "LDClick" Else $aMouseEvent[0] = "LClick" EndIf $block = KeepLastTab($hWnd, $aMouseEvent[0]) Case $WM_MBUTTONUP $aMouseEvent[1] = $time If $aMouseEvent[0] = "MClick" And ($timeDiff) < $DoubleClickTime Then $aMouseEvent[0] = "MDClick" Else $aMouseEvent[0] = "MClick" $block = KeepLastTab($hWnd, $aMouseEvent[0]) EndIf EndSwitch If Not $block Then _WinAPI_CallNextHookEx($hMouseHook, $nCode, $wParam, $lParam) ; Continue processing EndIf EndFunc ;==>Mouse_Event Func UnhookMouse() _WinAPI_UnhookWindowsHookEx($hMouseHook) $hMouseHook = 0 DllCallbackFree($hMouseEvent) $hMouseEvent = 0 EndFunc ;==>UnhookMouse Func Hotkey_Event() Local $block Local $hWnd = WinGetHandle(WinGetTitle("[ACTIVE]")) If StringInStr(_WinAPI_GetClassName($hWnd), "Chrome_WidgetWin_1") Then ; Chrome_WidgetWin_1, Chrome window $block = KeepLastTab($hWnd, "Hotkey") EndIf If Not $block Then HotKeySet("^w") Send(@HotKeyPressed) HotKeySet("^w", "Hotkey_Event") EndIf EndFunc Func KeepLastTab($hWnd, $action = "LClick") ; Possible $action value: LClick, LDClick, MClick, Hotkey ConsoleWrite(@CRLF & $action & " on a Chrome window: " & $hWnd & @CRLF) Local $aMousePos = MouseGetPos() Local $pChrome, $oChrome $UIA_oUIAutomation.ElementFromHandle($hWnd, $pChrome) ; Window $oChrome = ObjCreateInterface($pChrome, $sIID_IUIAutomationElement, $dtagIUIAutomationElement) If Not IsObj($oChrome) Then ConsoleWrite("Failed to get Chrome object from hWnd." & @CRLF) Return EndIf ;$UIA_ControlTypePropertyId = 30003 $oChromeTabs = UIA_getFirstElement($oChrome, $UIA_ControlTypePropertyId, $UIA_TabControlTypeId, $treescope_subtree) If Not IsObj($oChromeTabs) Then ConsoleWrite("Failed to get Chrome tab bar object." & @CRLF) Return EndIf Local $t $oChromeTabs.GetCurrentPropertyValue($UIA_BoundingRectanglePropertyId, $t) If UBound($t) < 4 Then Return If $action <> "Hotkey" And ($aMousePos[0] < $t[0] Or $aMousePos[0] > $t[2]+$t[0] Or $aMousePos[1] < $t[1] Or $aMousePos[1] > $t[3]+$t[1]) Then ; Mouse not on tabs bar ConsoleWrite("Mouse is not on the tab bar. Ignore and return..." & @CRLF) Return EndIf Local $pTrueCondition, $pElements, $iLength, $oAutomationElementArray $UIA_oUIAutomation.CreateTrueCondition($pTrueCondition) $oCondition = ObjCreateInterface($pTrueCondition, $sIID_IUIAutomationCondition, $dtagIUIAutomationCondition) If Not IsObj($oCondition) Then Return $oChromeTabs.FindAll($treescope_children, $oCondition, $pElements) $oAutomationElementArray = ObjCreateInterface($pElements, $sIID_IUIAutomationElementArray, $dtagIUIAutomationElementArray) If Not IsObj($oAutomationElementArray) Then ConsoleWrite("Failed to find all Chrome tabs. " & @CRLF) Return EndIf $oAutomationElementArray.Length($iLength) Local $UIA_pUIElement, $oTab2 Local $iTabs = $iLength - 1 If $iTabs > 1 Then ; more than one tab If $action = "LDClick" Then ConsoleWrite("There are " & $iTabs & " tabs within Chrome window. " & @CRLF) For $i = 1 To $iTabs $oAutomationElementArray.GetElement($i, $UIA_pUIElement) $oTab2 = ObjCreateInterface($UIA_pUIElement, $sIID_IUIAutomationElement, $dtagIUIAutomationElement) If Not IsObj($oTab2) Then ContinueLoop $oTab2.GetCurrentPropertyValue($UIA_BoundingRectanglePropertyId, $t) If UBound($t) < 4 Then ContinueLoop If $aMousePos[0] >= $t[0] And $aMousePos[0] <= $t[2]+$t[0] And $aMousePos[1] >= $t[1] And $aMousePos[1] <= $t[3]+$t[1] Then ConsoleWrite("You double clicked on one of " & $iTabs & " tabs. Close the tab and return..." & @CRLF) HotKeySet("^w") Send("^w") HotKeySet("^w", "Hotkey_Event") Return True EndIf Next EndIf Return EndIf ConsoleWrite("There is ONLY one tab within Chrome window. " & @CRLF) $oAutomationElementArray.GetElement(1, $UIA_pUIElement) $oTab = ObjCreateInterface($UIA_pUIElement, $sIID_IUIAutomationElement, $dtagIUIAutomationElement) If Not IsObj($oTab) Then ConsoleWrite("Failed to get the last tab object." & @CRLF) Return EndIf Local $rtTab $oTab.GetCurrentPropertyValue($UIA_BoundingRectanglePropertyId, $rtTab) If UBound($rtTab) < 4 Then Return If $action <> "Hotkey" And ($aMousePos[0] < $rtTab[0] Or $aMousePos[0] > $rtTab[2]+$rtTab[0] Or $aMousePos[1] < $rtTab[1] Or $aMousePos[1] > $rtTab[3]+$rtTab[1]) Then ; Mouse not on the last tab ConsoleWrite("Mouse is not on the last tab. Ignore and return..." & @CRLF) Return EndIf If $action = "LClick" Then $oTabClose = UIA_getFirstElement($oTab, $UIA_ControlTypePropertyId, $UIA_ButtonControlTypeId, $treescope_subtree) If Not IsObj($oTabClose) Then ConsoleWrite("Failed to get the last tab close object." & @CRLF) Return EndIf $oTabClose.GetCurrentPropertyValue($UIA_BoundingRectanglePropertyId, $t) If UBound($t) < 4 Then Return If $aMousePos[0] < $t[0] Or $aMousePos[0] > $t[2]+$t[0] Or $aMousePos[1] < $t[1] Or $aMousePos[1] > $t[3]+$t[1] Then ; Mouse not on the tab close button Return EndIf EndIf ; open a new tab within chrome ConsoleWrite("The last tab is about to be closed, so we have to open a new tab to prevent Chrome window from closing." & @CRLF) Send("^t") ConsoleWrite("A new tab created!" & @CRLF) Sleep(100) ConsoleWrite("Close the old tab and return..." & @CRLF) ;~ Local $pPattern ;~ $oTabClose.GetCurrentPattern($UIA_InvokePatternId, $pPattern) ;~ $oPattern = ObjCreateInterface($pPattern, $sIID_IUIAutomationInvokePattern, $dtagIUIAutomationInvokePattern) ;~ If IsObj($oPattern) Then ;~ ConsoleWrite("Invoke to close the tab..." & @CRLF) ;~ $oTabClose.SetFocus() ;~ $oPattern.Invoke() ;~ EndIf _WinAPI_UnhookWindowsHookEx($hMouseHook) $aMousePos = MouseGetPos() If $aMousePos[0] >= $rtTab[0] And $aMousePos[0] <= $rtTab[2]+$rtTab[0] And $aMousePos[1] >= $rtTab[1] And $aMousePos[1] <= $rtTab[3]+$rtTab[1] Then MouseClick("middle", $aMousePos[0], $aMousePos[1], 1, 0) Else MouseClick("middle", $rtTab[0]+$rtTab[2]/2, $rtTab[1]+$rtTab[3]/2, 1, 0) ; close the tab MouseMove($aMousePos[0], $aMousePos[1], 0) ; move mouse back to previous position EndIf ;~ _WinAPI_Mouse_Event($MOUSEEVENTF_MIDDLEDOWN, $rtTab[0]+$rtTab[2]/2, $rtTab[1]+$rtTab[3]/2) ;~ Sleep(100) ;~ _WinAPI_Mouse_Event($MOUSEEVENTF_MIDDLEUP, $rtTab[0]+$rtTab[2]/2, $rtTab[1]+$rtTab[3]/2) $hMouseHook = _WinAPI_SetWindowsHookEx($WH_MOUSE_LL, DllCallbackGetPtr($hMouseEvent), _WinAPI_GetModuleHandle(0)) Return True ; to block mouse click/hot key EndFunc Func UIA_getFirstElement($obj, $propertyID, $tval, $treeScope) Local $pCondition, $oCondition $UIA_oUIAutomation.CreatePropertyCondition($propertyID, $tval, $pCondition) $oCondition = ObjCreateInterface($pCondition, $sIID_IUIAutomationPropertyCondition, $dtagIUIAutomationPropertyCondition) Local $UIA_oUIElement, $UIA_pUIElement $t = $obj.Findfirst($treeScope, $oCondition, $UIA_pUIElement) $UIA_oUIElement = ObjCreateInterface($UIA_pUIElement, $sIID_IUIAutomationElement, $dtagIUIAutomationElement) If IsObj($UIA_oUIElement) Then Return $UIA_oUIElement Return SetError(1, 0, "") EndFunc ;==>UIA_getFirstElement My question is related to #AutoIt3Wrapper_UseX64 when run this script on 64-bit Win 7. No mater I set #AutoIt3Wrapper_UseX64=Y or #AutoIt3Wrapper_UseX64=N, this script works very well on hotkey event, while mouse clicks sometimes works if #AutoIt3Wrapper_UseX64=N and sometimes works on Y. Can some one test this and finger out what's wrong? Info from SciTe if #AutoIt3Wrapper_UseX64=Y : @OSArch: X64, @AutoItX64: 1 Hotkey on a Chrome window: 0x0000000000140330 There is ONLY one tab within Chrome window. The last tab is about to be closed, so we have to open a new tab to prevent Chrome window from closing. A new tab created! Close the old tab and return... LClick on a Chrome window: 0x0000000000140330 There is ONLY one tab within Chrome window. The last tab is about to be closed, so we have to open a new tab to prevent Chrome window from closing. A new tab created! Close the old tab and return... Info if #AutoIt3Wrapper_UseX64=N or comment out this line: @OSArch: X64, @AutoItX64: 0 Hotkey on a Chrome window: 0x00140330 There is ONLY one tab within Chrome window. The last tab is about to be closed, so we have to open a new tab to prevent Chrome window from closing. A new tab created! Close the old tab and return... LClick on a Chrome window: 0x00140330 Failed to get Chrome tab bar object.Hotkey events and mouse click events share the same function KeepLastTab($hWnd, $action = "LClick"),Why this function triggered by hotkey works on both 32-bit and 64-bit while that triggered by mouse events failed on 32-bit autoit?
  2. I've tried to make a script "Chrome_KeepLastTab.au3" to prevent Chrome from exit if the last tab is closed. This script monitors the mouse click.If the mouse clicks on the close button on the last tab, it opens a new tab and then close the previous tab. This script works but not stable.Can somebody point me out what I am wrong? #Region ;**** Directives created by AutoIt3Wrapper_GUI **** #AutoIt3Wrapper_Compile_Both=y #AutoIt3Wrapper_UseX64=Y #AutoIt3Wrapper_Run_Au3Stripper=y #EndRegion ;**** Directives created by AutoIt3Wrapper_GUI **** #include <WindowsConstants.au3> #include <WinAPI.au3> #include "UIAWrappers.au3" Global $doubleClickTime = DllCall("user32.dll", "uint", "GetDoubleClickTime") $doubleClickTime = $doubleClickTime[0] Global $aMouseEvent[2] ;Register callback $hMouseEvent = DllCallbackRegister("_Mouse_Event", "int", "int;ptr;ptr") $hMouseHook = _WinAPI_SetWindowsHookEx($WH_MOUSE_LL, DllCallbackGetPtr($hMouseEvent), _WinAPI_GetModuleHandle(0)) OnAutoItExitRegister("UnhookMouse_OnExit") While 1 Sleep(100) WEnd ToolTip("") ; https://www.autoitscript.com/forum/topic/103362-monitoring-mouse-events/ Func _Mouse_Event($nCode, $wParam, $lParam) Local $info, $mouseData, $time, $timeDiff If $nCode < 0 Then Return _WinAPI_CallNextHookEx($hMouseHook, $nCode, $wParam, $lParam) ; Continue processing EndIf $tPoint = _WinAPI_GetMousePos() $hWnd = _WinAPI_WindowFromPoint($tPoint) If Not StringInStr(_WinAPI_GetClassName($hWnd), "Chrome") Then Return _WinAPI_CallNextHookEx($hMouseHook, $nCode, $wParam, $lParam) EndIf Local $aMousePos[2] $aMousePos[0] = DllStructGetData($tPoint, "X") $aMousePos[1] = DllStructGetData($tPoint, "Y") ToolTip($aMousePos[0] & @CRLF & $aMousePos[1]) Local Const $MSLLHOOKSTRUCT = $tagPOINT & ";dword mouseData;dword flags;dword time;ulong_ptr dwExtraInfo" $info = DllStructCreate($MSLLHOOKSTRUCT, $lParam) $mouseData = DllStructGetData($info, 3) $time = DllStructGetData($info, 5) $timeDiff = $time - $aMouseEvent[1] Switch $wParam Case $WM_LBUTTONUP If $aMouseEvent[0] = "LClick" And ($timeDiff) < $doubleClickTime Then $aMouseEvent[0] = "LDClick" $aMouseEvent[1] = $time EndIf ;Set LClick if other events haven't fired If $aMouseEvent[1] <> $time Then $aMouseEvent[0] = "LClick" $aMouseEvent[1] = $time ConsoleWrite("X = " & DllStructGetData($tPoint, "X") & ", " & "Y = " & DllStructGetData($tPoint, "Y") & @CRLF) KeepLastTab($hWnd, $aMousePos) EndIf ;~ Case $WM_RBUTTONUP ;~ If $aMouseEvent[0] = "RClick" And ($timeDiff) < $doubleClickTime Then ;~ $aMouseEvent[0] = "RDClick" ;~ $aMouseEvent[1] = $time ;~ EndIf ;~ ;Set RClick if other events haven't fired ;~ If $aMouseEvent[1] <> $time Then ;~ $aMouseEvent[0] = "RClick" ;~ $aMouseEvent[1] = $time ;~ EndIf EndSwitch Return _WinAPI_CallNextHookEx($hMouseHook, $nCode, $wParam, $lParam) ; Continue processing EndFunc ;==>_Mouse_Event Func UnhookMouse_OnExit() _WinAPI_UnhookWindowsHookEx($hMouseHook) $hMouseHook = 0 DllCallbackFree($hMouseEvent) $hMouseEvent = 0 EndFunc ;==>UnhookMouse_OnExit Func KeepLastTab($hWnd, $aMousePos = "") If Not IsObj($UIA_oDesktop) Then Return ;$oChrome = _UIA_getFirstObjectOfElement($UIA_oDesktop, "handle:=" & $hWnd, $treescope_children) ; <== Why this line not work? $oChrome = _UIA_getFirstObjectOfElement($UIA_oDesktop, "class:=Chrome_WidgetWin_1", $treescope_children) If Not IsObj($oChrome) Then Return ConsoleWrite("Got Chrome object." & @CRLF) $oChromeTabs = _UIA_getFirstObjectOfElement($oChrome, "controltype:=" & $UIA_TabControlTypeId, $treescope_subtree) If Not IsObj($oChromeTabs) Then Return ConsoleWrite("Got Chrome Tabs object." & @CRLF) Local $pTrueCondition, $pElements, $iLength, $oAutomationElementArray $UIA_oUIAutomation.CreateTrueCondition($pTrueCondition) $oCondition = ObjCreateInterface($pTrueCondition, $sIID_IUIAutomationCondition, $dtagIUIAutomationCondition) If Not IsObj($oCondition) Then Return $oChromeTabs.FindAll($treescope_children, $oCondition, $pElements) $oAutomationElementArray = ObjCreateInterface($pElements, $sIID_IUIAutomationElementArray, $dtagIUIAutomationElementArray) If Not IsObj($oAutomationElementArray) Then Return $oAutomationElementArray.Length($iLength) ConsoleWrite("Tabs count: " & $iLength-1 & @CRLF) If $iLength > 2 Then Return ; ignore if more than one tab ; try to get tab close button object Local $UIA_pUIElement $oAutomationElementArray.GetElement(1, $UIA_pUIElement) $oTab = ObjCreateInterface($UIA_pUIElement, $sIID_IUIAutomationElement, $dtagIUIAutomationElement) If Not IsObj($oTab) Then Return $oTabClose = _UIA_getFirstObjectOfElement($oTab, "controltype:=" & $UIA_ButtonControlTypeId, $treescope_subtree) $t = StringSplit(_UIA_getPropertyValue($oTabClose, $UIA_BoundingRectanglePropertyId), ";", 2) If UBound($t) < 4 Then Return ConsoleWrite("Close button on the last tab: " & $t[0] & ";" & ($t[2] + $t[0]) & ";" & $t[1]& ";" & ($t[3] + $t[1]) & @CRLF) If $aMousePos[0] < $t[0] Or $aMousePos[0] > $t[2]+$t[0] Or $aMousePos[1] < $t[1] Or $aMousePos[1] > $t[3]+ $t[1] Then ; Mouse is not on the tab close button Return EndIf ConsoleWrite("Mouse is on the Tab close button, so we have to open a new tab to prevent Chrome from exit." & @CRLF) ; send Ctrl + t to open a new tab within chrome Send("^t") Sleep(100) EndFunc
  3. mLipok,it's related but I think it's impossible to combine your script's icon/window with the icon/window of Notepad or other app only using WinAPI_SetCurrentProcessExplicitAppUserModelID()
  4. I'v made a UDF based on some codes of binhnx and solved my problem, Group different Apps on Win 7+ taskbar. Thanks to binhnx, LarsJ and others.
  5. After some days effort I've solved my problem. I made some udfs to get / set appid for windows or shortcut files.So I can group any windows/taskbar icons as desired. Some Funcs are from this thread.Thanks to binhnx, LarsJ and others. Here is the UDF: #include-once ; file name: AppUserModelId.au3 ; #### Title #### ; ================================================================================ ; Get / Set AppUserModelId for a window or a shortcut file(*.lnk) ; Thanks to binhnx, LarsJ and others ; - jackchen ; ================================================================================ ; #### FUNCTIONS #### ; ================================================================================ ; _WindowAppId ; _ShortcutAppId ; ================================================================================ #include <WinAPI.au3> #Region ====================== #### CONSTANTS #### ====================== ; https://www.autoitscript.com/forum/topic/161067-get-clsid-from-iid/ Global Const $CLSID_ShellLink = "{00021401-0000-0000-C000-000000000046}" Global Const $sIID_IShellLinkW = "{000214F9-0000-0000-C000-000000000046}" Global Const $tag_IShellLinkW = _ "GetPath hresult(long;long;long;long);" & _ "GetIDList hresult(long);" & _ "SetIDList hresult(long);" & _ "GetDescription hresult(long;long);" & _ "SetDescription hresult(wstr);" & _ "GetWorkingDirectory hresult(long;long);" & _ "SetWorkingDirectory hresult(long;long);" & _ "GetArguments hresult(long;long);" & _ "SetArguments hresult(ptr);" & _ "GetHotkey hresult(long);" & _ "SetHotkey hresult(word);" & _ "GetShowCmd hresult(long);" & _ "SetShowCmd hresult(int);" & _ "GetIconLocation hresult(long;long;long);" & _ "SetIconLocation hresult(wstr;int);" & _ "SetRelativePath hresult(long;long);" & _ "Resolve hresult(long;long);" & _ "SetPath hresult(wstr);" Global Const $tag_IPersist = "GetClassID hresult(long);" Global Const $sIID_IPersistFile = "{0000010b-0000-0000-C000-000000000046}" Global Const $tag_IPersistFile = $tag_IPersist & _ ; Inherits from IPersist "IsDirty hresult();" & _ "Load hresult(wstr;dword);" & _ "Save hresult(wstr;bool);" & _ "SaveCompleted hresult(long);" & _ "GetCurFile hresult(long);" ; https://msdn.microsoft.com/en-us/library/windows/desktop/aa380337(v=vs.85).aspx Global Const $STGM_READ = 0x00000000 Global Const $STGM_READWRITE = 0x00000002 Global Const $STGM_SHARE_DENY_NONE = 0x00000040 ; Global Const $tagPROPERTYKEY = $tagGUID & ';DWORD pid' Global Const $tagPROPERTYKEY = 'struct;ulong Data1;ushort Data2;ushort Data3;byte Data4[8];DWORD pid;endstruct' Global $tagPROPVARIANT = _ 'USHORT vt;' & _ ;typedef unsigned short VARTYPE; - in WTypes.h 'WORD wReserved1;' & _ 'WORD wReserved2;' & _ 'WORD wReserved3;' & _ 'LONG;PTR' ;union, use the largest member (BSTRBLOB, which is 96-bit in x64) Global Const $sIID_IPropertyStore = '{886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99}' Global Const $VT_EMPTY = 0, $VT_LPWSTR = 31 #EndRegion ====================== #### CONSTANTS #### ====================== ; #FUNCTION# ;=============================================================================== ; Name...........: _WindowAppId ; Description ...: Get / Set AppUerModelId(AppId) of a window ; Syntax.........: _WindowAppId($hWnd, $appid = Default) ; Parameters ....: $hWnd - Handle of a window. ; $appid - [optional] AppId to set. ; Return values .: Success - Returns current AppId ; Failure - Returns "" and sets @error: ; Author ........: binhnx, jackchen ; Link ..........: https://www.autoitscript.com/forum/topic/168099-how-to-prevent-multiple-guis-from-combining-in-the-taskbar/ ;============================================================================================ Func _WindowAppId($hWnd, $appid = Default) Local $tpIPropertyStore = DllStructCreate('ptr') _WinAPI_SHGetPropertyStoreForWindow($hWnd, $sIID_IPropertyStore, $tpIPropertyStore) Local $pPropertyStore = DllStructGetData($tpIPropertyStore, 1) Local $oPropertyStore = ObjCreateInterface($pPropertyStore, $sIID_IPropertyStore, _ 'GetCount HRESULT(PTR);GetAt HRESULT(DWORD; PTR);GetValue HRESULT(PTR;PTR);' & _ 'SetValue HRESULT(PTR;PTR);Commit HRESULT()') If Not IsObj($oPropertyStore) Then Return SetError(1, 0, '') Local $tPKEY = _PKEY_AppUserModel_ID() Local $tPROPVARIANT = DllStructCreate($tagPROPVARIANT) Local $sAppId If $appid = Default Then $oPropertyStore.GetValue(DllStructGetPtr($tPKEY), DllStructGetPtr($tPROPVARIANT)) ; Extracts a string value from a PROPVARIANT structure ; http://deletethis.net/dave/dev/setappusermodelid/ ; https://msdn.microsoft.com/en-us/library/windows/desktop/bb776559(v=vs.85).aspx If DllStructGetData($tPROPVARIANT, 'vt') <> $VT_EMPTY Then Local $buf = DllStructCreate('wchar[128]') DllCall('Propsys.dll', 'long', 'PropVariantToString', _ 'ptr', DllStructGetPtr($tPROPVARIANT), _ 'ptr', DllStructGetPtr($buf), _ 'uint', DllStructGetSize($buf)) If Not @error Then $sAppId = DllStructGetData($buf, 1) EndIf EndIf Else _WinAPI_InitPropVariantFromString($appId, $tPROPVARIANT) $oPropertyStore.SetValue(DllStructGetPtr($tPKEY), DllStructGetPtr($tPROPVARIANT)) $oPropertyStore.Commit() $sAppId = $appid EndIf ;$oPropertyStore.Release() ; this line crashes Autoit Return SetError(($sAppId == '')*2, 0, $sAppId) EndFunc ; #FUNCTION# ;=============================================================================== ; Name...........: _ShortcutAppId ; Description ...: Get AppUerModelId(AppId) from a .lnk shortcut file or ; set the shortcut's AppId if $appid is provided. ; Syntax.........: _ShortcutAppId($lnkfile, $appid = Default) ; Parameters ....: $lnkfile - path of shortcut file. ; $appid - [optional] AppId to set. ; Return values .: Success - Returns current AppId ; Failure - Returns "" and sets @error: ; Author ........: jackchen ; Linlk .........: https://code.google.com/p/win7appid/ ;============================================================================================ Func _ShortcutAppId($lnkfile, $appid = Default) Local $oIShellLinkW = ObjCreateInterface($CLSID_ShellLink , $sIID_IShellLinkW, $tag_IShellLinkW ) If Not IsObj( $oIShellLinkW ) Then Return SetError(1, 0, '') Local $pIPersistFile, $oIPersistFile, $ret, $sAppId Local $tRIID_IPersistFile = _WinAPI_GUIDFromString( $sIID_IPersistFile ) $oIShellLinkW.QueryInterface( $tRIID_IPersistFile, $pIPersistFile ) $oIPersistFile = ObjCreateInterface( $pIPersistFile, $sIID_IPersistFile, $tag_IPersistFile ) If IsObj( $oIPersistFile ) Then If $appid == Default Then ; read only $ret = $oIPersistFile.Load($lnkfile, BitOR($STGM_READ, $STGM_SHARE_DENY_NONE)) Else $ret = $oIPersistFile.Load($lnkfile, $STGM_READWRITE) EndIf If $ret = 0 Then Local $tPKEY = _PKEY_AppUserModel_ID() Local $tPROPVARIANT = DllStructCreate($tagPROPVARIANT) $tRIID_IPropertyStore = _WinAPI_GUIDFromString($sIID_IPropertyStore) Local $pPropertyStore $oIShellLinkW.QueryInterface($tRIID_IPropertyStore, $pPropertyStore) Local $oPropertyStore = ObjCreateInterface($pPropertyStore, $sIID_IPropertyStore, _ 'GetCount HRESULT(PTR);GetAt HRESULT(DWORD;PTR);GetValue HRESULT(PTR;PTR);' & _ 'SetValue HRESULT(PTR;PTR);Commit HRESULT()') If IsObj($oPropertyStore) Then If $appid == Default Then ; get appid $oPropertyStore.GetValue(DllStructGetPtr($tPKEY), DllStructGetPtr($tPROPVARIANT)) If DllStructGetData($tPROPVARIANT, 'vt') <> $VT_EMPTY Then ; Extracts a string value from a PROPVARIANT structure ; http://deletethis.net/dave/dev/setappusermodelid/ ; https://msdn.microsoft.com/en-us/library/windows/desktop/bb776559(v=vs.85).aspx Local $buf = DllStructCreate('wchar[128]') DllCall('Propsys.dll', 'long', 'PropVariantToString', _ 'ptr', DllStructGetPtr($tPROPVARIANT), _ 'ptr', DllStructGetPtr($buf), _ 'uint', DllStructGetSize($buf)) $sAppId = DllStructGetData($buf, 1) EndIf Else ; set appid _WinAPI_InitPropVariantFromString($appid, $tPROPVARIANT) $oPropertyStore.SetValue(DllStructGetPtr($tPKEY), DllStructGetPtr($tPROPVARIANT)) $oPropertyStore.Commit() $oIPersistFile.Save($lnkfile, True) $sAppId = $appid EndIf EndIf EndIf EndIf If IsObj($oPropertyStore) Then $oPropertyStore.Release() If IsObj($oIPersistFile) Then $oIPersistFile.Release() If IsObj($oIShellLinkW) Then $oIShellLinkW.Release() Return SetError(($sAppId == '')*2, 0, $sAppId) EndFunc ; https://www.autoitscript.com/forum/topic/168099-how-to-prevent-multiple-guis-from-combining-in-the-taskbar/ ; This function is not exposed in any dll, but inlined in propvarutil.h so we need to rewrite it entirely in AutoIt Func _WinAPI_InitPropVariantFromString($sUnicodeString, ByRef $tPROPVARIANT) DllStructSetData($tPROPVARIANT, 'vt', $VT_LPWSTR) Local $aRet = DllCall('Shlwapi.dll', 'LONG', 'SHStrDupW', _ 'WSTR', $sUnicodeString, 'PTR', DllStructGetPtr($tPROPVARIANT) + 8) If @error Then Return SetError(@error, @extended, False) Local $bSuccess = $aRet[0] == 0 ; If fails, zero memory of the current PROPVARIANT struct If (Not $bSuccess) Then $tPROPVARIANT = DllStructCreate($tagPROPVARIANT) Return SetExtended($aRet[0], $bSuccess) EndFunc ; https://www.autoitscript.com/forum/topic/168099-how-to-prevent-multiple-guis-from-combining-in-the-taskbar/ Func _PKEY_AppUserModel_ID() Local $tPKEY = DllStructCreate($tagPROPERTYKEY) ;PKEY_AppUserModel_ID = { {0x9F4C2855, 0x9F79, 0x4B39, ; {0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3}}, 5 } _WinAPI_GUIDFromStringEx('{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}', _ DllStructGetPtr($tPKEY)) DllStructSetData($tPKEY, 'pid', 5) Return $tPKEY EndFunc ; https://www.autoitscript.com/forum/topic/168099-how-to-prevent-multiple-guis-from-combining-in-the-taskbar/ Func _WinAPI_SHGetPropertyStoreForWindow($hWnd, $sIID, ByRef $tPointer) Local $tIID = _WinAPI_GUIDFromString($sIID) Local $pp = IsPtr($tPointer)? $tPointer : DllStructGetPtr($tPointer) Local $aRet = DllCall('Shell32.dll', 'LONG', 'SHGetPropertyStoreForWindow', _ 'HWND', $hWnd, 'STRUCT*', $tIID, 'PTR', $pp) If @error Then Return SetError(@error, @extended, False) Return SetExtended($aRet[0], ($aRet[0] = 0)) EndFuncAnd here are some examples: #include "AppUserModelId.au3" ; Example 1 ; search .lnk files in user pinned taskbar dir and show appid info $TaskbarDir = @AppDataDir & "\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar" $hSearch = FileFindFirstFile($TaskbarDir & "\*.lnk") If $hSearch = -1 Then Exit While 1 $file = FileFindNextFile($hSearch) If @error Then ExitLoop $ShellObj = ObjCreate("WScript.Shell") $objShortcut = $ShellObj.CreateShortCut($TaskbarDir & "\" & $file) $path = $objShortcut.TargetPath $ShellObj = "" $objShortcut = "" ConsoleWrite($file &':' & @crlf & _ $path & @CRLF & _ _ShortcutAppId($TaskbarDir & "\" & $file)) ConsoleWrite(@CRLF & @CRLF) WEnd FileClose($hSearch) MsgBox(0, "Example 1", "Take a look at console for AppId info of user pinned Apps!") ; Example 2 ; group 2 of 3 windows $hWnd1 = GUICreate('Test1', 400, 200, 0, 100) $hWnd2 = GUICreate('Test2', 400, 200, 500, 100) $hWnd3 = GUICreate('Test3', 400, 200, 1000, 100) ; The application id should be unique so it not effect any other application. ; Better use a format like MyCompany(orMyName).MyApplication.WindowId _WindowAppId($hWnd1, 'MyGreatCompany.MyAwesomeApplication.MyElegantWindow1') _WindowAppId($hWnd2, 'MyGreatCompany.MyAwesomeApplication.OhImNotAWindowImAnAlien') _WindowAppId($hWnd3, 'MyGreatCompany.MyAwesomeApplication.MyElegantWindow1') GUISetState(@SW_SHOW, $hWnd1) GUISetState(@SW_SHOW, $hWnd2) GUISetState(@SW_SHOW, $hWnd3) $appid1 = _WindowAppId($hWnd1) $appid3 = _WindowAppId($hWnd3) MsgBox(0, 'Example 2', 'AppId of Test1 Window: ' & @CRLF & $appid1 & @CRLF & @CRLF & _ 'AppId of Test3 Window: ' & $appid3 & @CRLF & @CRLF & _ 'Their AppIds are the same, so their icons should be grouped together.', 0, $hWnd1) Do Until GUIGetMsg() = -3 GUIDelete($hWnd1) GUIDelete($hWnd2) GUIDelete($hWnd3)Example 3: Script of example 3 should be compiled to exe and pinned to taskbar before test. #include "AppUserModelId.au3" ; Example 3 ; --> Compile this script to compiled_script.exe before test ; --> Pin compiled_script.exe to taskbar ; --> click compiled_script.exe icon on taskbar to start notepad ; --> notepad's icon should be grouped with compiled_script.exe icon $appid_to_set = "MyNotepad.123456" ; DllCall("Shell32.dll", "long", "SetCurrentProcessExplicitAppUserModelID", "wstr", $appid_to_set) $TaskbarDir = @AppDataDir & "\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar" $lnkfile = $TaskbarDir & "\" & StringTrimRight(@ScriptName, 4) & ".lnk" If Not FileExists($lnkfile) Then MsgBox(0, "Example 3", "Please compile this script to exe" & @CRLF & "and pin to taskbar before test Example 3!") Exit EndIf Run("Notepad.exe") WinWait("[REGEXPCLASS:(?i)Notepad]", "", 15) $hWnd = WinGetHandle('[REGEXPCLASS:(?i)Notepad]') MsgBox(0, "Example 3", "Notepad should be launched." & @CRLF & _ "Press OK to combine Notepad's icon!", 0, $hWnd) ; Get the window's appid, normaly Notepad's window has no appid. ; so we set a predefined appid for it. $window_appid = _WindowAppId($hWnd) If Not $window_appid Then $window_appid = _WindowAppId($hWnd, $appid_to_set) EndIf ; We set the same appid for the lnk file $shortcut_appid = _ShortcutAppId($lnkfile) If $shortcut_appid <> $window_appid Then $shortcut_appid = _ShortcutAppId($lnkfile, $window_appid) EndIf MsgBox(0, "Example 3", "Notepad's taskbar icon should be combined with this script's icon now!", 0, $hWnd)
  6. UEZ,Thanks for your example which is exactly what the OP needs. Actually my request is maybe the reverse, I need combine other window's icon with my loader icon. I made a chrome_portable_loader.exe in autoit.If I pin the loader to taskbar and click the icon to launch Google Chrome browser,the browser's icon will on the right side of loader. I hope the browser's icon can combine with my loader's icon on taskbar.It's absolutley possible.I got the appid of chrome window via _Win7Helper_GetWindowAppId($hWnd) in Autoit, set the same appid for chrome_portable_loader.lnk in @AppDataDir & "\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar", then both icons were combined.I set .lnk file's appid using Win7AppId which is coded in c++.I should convert those code to Autoit,but that's not easy for me.
  7. I've added a Func _Win7Helper_GetWindowAppId($hWnd) to binhnx's codes to get a window's AppId. Func _Win7Helper_GetWindowAppId($hWnd) Local Static $tPROPERTYKEY = _Win7Helper_PKEY_AppUserModel_ID() Local $tPROPVARIANT = DllStructCreate($tagPROPVARIANT) Local $tpIPropertyStore = DllStructCreate('ptr') _WinAPI_SHGetPropertyStoreForWindow($hWnd, '{886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99}', $tpIPropertyStore) Local $pPropertyStoreObject = DllStructGetData($tpIPropertyStore, 1) Local $oPropertyStoreObject = ObjCreateInterface($pPropertyStoreObject, _ '{886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99}', _ 'GetCount HRESULT(PTR);GetAt HRESULT(DWORD; PTR);GetValue HRESULT(PTR; PTR);' & _ 'SetValue HRESULT(PTR; PTR);Commit HRESULT()') If Not IsObj($oPropertyStoreObject) Then Return SetError(1, 0, '') $oPropertyStoreObject.GetValue(DllStructGetPtr($tPROPERTYKEY), DllStructGetPtr($tPROPVARIANT)) ; Extracts a string value from a PROPVARIANT structure ; http://deletethis.net/dave/dev/setappusermodelid/ ; https://msdn.microsoft.com/en-us/library/windows/desktop/bb776559(v=vs.85).aspx Local $buf = DllStructCreate('wchar[128]') DllCall('Propsys.dll', 'long', 'PropVariantToString', _ 'ptr', DllStructGetPtr($tPROPVARIANT), _ 'ptr', DllStructGetPtr($buf), _ 'uint', DllStructGetSize($buf)) If @error Then Return SetError(@error, @extended, "") Else Return DllStructGetData($buf, 1) EndIf EndFunc And below is the full codes with examples: ; https://www.autoitscript.com/forum/topic/168099-how-to-prevent-multiple-guis-from-combining-in-the-taskbar/ ; http://stackoverflow.com/questions/28228042/shgetpropertystoreforwindow-how-to-set-properties-on-existing-system-appusermo #include <StructureConstants.au3> #include <WinAPI.au3> Global Enum $VT_EMPTY, $VT_NULL, $VT_I2, $VT_I4, $VT_R4, $VT_R8, _ $VT_CY, $VT_DATE, $VT_BSTR, $VT_DISPATCH, $VT_ERROR, _ $VT_BOOL, $VT_VARIANT, $VT_UNKNOWN, $VT_DECIMAL, _ $VT_I1 = 16, $VT_UI1, $VT_UI2, $VT_UI4, $VT_I8, $VT_UI8, $VT_INT, $VT_UINT, _ $VT_VOID, $VT_HRESULT, $VT_PTR, $VT_SAFEARRAY, $VT_CARRAY, $VT_USERDEFINED, _ $VT_LPSTR, $VT_LPWSTR, _ $VT_RECORD = 36, $VT_INT_PTR, $VT_UINT_PTR, _ $VT_FILETIME = 64, $VT_BLOB, $VT_STREAM, $VT_STORAGE, _ $VT_STREAMED_OBJECT, $VT_STORED_OBJECT, $VT_BLOB_OBJECT, _ $VT_CF, $VT_CLSID, $VT_VERSIONED_STREAM, $VT_BSTR_BLOB = 0XFFF, _ $VT_VECTOR = 0X1000, $VT_ARRAY = 0X2000, $VT_BYREF = 0X4000, $VT_RESERVED = 0X8000, _ $VT_ILLEGAL = 0XFFFF, $VT_ILLEGALMASKED = 0XFFF, $VT_TYPEMASK = 0XFFF Global $tagPROPVARIANT = 'USHORT vt;' & _ ;typedef unsigned short VARTYPE; - in WTypes.h 'WORD wReserved1;' & _ 'WORD wReserved2;' & _ 'WORD wReserved3;' & _ 'LONG;PTR' ;union, use the largest member (BSTRBLOB, which is 96-bit in x64) Global Const $tagPROPERTYKEY = $tagGUID & ';DWORD pid' ; Optinal setting. Default to True (use newer, experimental method). Set to False to use old, stupid but safe method. Global Const $__OPT_EXPERIMENTAL_OBJCREATEINTERFACE = True ; This function is not exposed in any dll, but inlined in propvarutil.h so we need to rewrite it entirely in AutoIt Func _WinAPI_InitPropVariantFromString($sUnicodeString, ByRef $tPROPVARIANT) Static $nPropVariantDataIndex = 8 ; = DllStructGetSize(DllStructCreate('USHORT vt;WORD wReserved1;WORD wReserved2;WORD wReserved3')) DllStructSetData($tPROPVARIANT, 'vt', $VT_LPWSTR) Local $aRet = DllCall('Shlwapi.dll', 'LONG', 'SHStrDupW', _ 'WSTR', $sUnicodeString, 'PTR', DllStructGetPtr($tPROPVARIANT) + $nPropVariantDataIndex) If @error Then Return SetError(@error, @extended, False) Local $bSuccess = $aRet[0] = 0? True : False ; If fails, zero memory of the current PROPVARIANT struct If (Not $bSuccess) Then $tPROPVARIANT = DllStructCreate($tagPROPVARIANT) Return SetExtended($aRet[0], $bSuccess) EndFunc Func _WinAPI_SHGetPropertyStoreForWindow($hWnd, $sIID, ByRef $tPointer) Local $tIID = _WinAPI_GUIDFromString($sIID) Local $pp = IsPtr($tPointer)? $tPointer : DllStructGetPtr($tPointer) Local $aRet = DllCall('Shell32.dll', 'LONG', 'SHGetPropertyStoreForWindow', _ 'HWND', $hWnd, 'STRUCT*', $tIID, 'PTR', $pp) If @error Then Return SetError(@error, @extended, False) Return SetExtended($aRet[0], ($aRet[0] = 0)) EndFunc Global Const $tagIUnknown = 'PTR QueryInterface;PTR AddRef;PTR Release' Global Const $tagIPropertyStore = $tagIUnknown & _ ';PTR GetCount;' & _ ;HRESULT GetCount([Out] DWORD* cProps) 'PTR GetAt;' & _ ;HRESULT GetAt([In] DWORD iProp, [Out] PROPERTYKEY* pKey) 'PTR GetValue;' & _ ;HRESULT GetValue([In] PROPERTYKEY& key, [Out] PROPVARIANT * pv) 'PTR SetValue;' & _ ;HRESULT SetValue([In] PROPERTYKEY& key, [In] PROPVARIANT& propvar) 'PTR Commit' ;HRESULT Commit() Func _Win7Helper_PKEY_AppUserModel_ID() Local $tPROPERTYKEY = DllStructCreate($tagPROPERTYKEY) ;PKEY_AppUserModel_ID = { {0x9F4C2855, 0x9F79, 0x4B39, ; {0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3}}, 5 } _WinAPI_GUIDFromStringEx('{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}', _ DllStructGetPtr($tPROPERTYKEY)) DllStructSetData($tPROPERTYKEY, 'pid', 5) Return $tPROPERTYKEY EndFunc ; The application id is an up-to-128-characters string Func _Win7Helper_SetWindowAppId($hWnd, $sId) ; This variable is not change. You can use a global const but a local static is prefered due to it scope Local Static $tPROPERTYKEY = _Win7Helper_PKEY_AppUserModel_ID() ; Create and int PROPVARIANT variable Local $tPROPVARIANT = DllStructCreate($tagPROPVARIANT) _WinAPI_InitPropVariantFromString($sId, $tPROPVARIANT) ; Now create an instance of IPropertyStore interface. We must pass a void** (pointer to pointer) ; We can do it in AutoIt by using a pointer to a struct which have only 1 pointer member. Local $tpIPropertyStore = DllStructCreate('PTR') $ret = _WinAPI_SHGetPropertyStoreForWindow($hWnd, '{886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99}', $tpIPropertyStore) ; Retrieve the IPropertyStore pointer from result Local $pPropertyStoreObject = DllStructGetData($tpIPropertyStore, 1) ; Now we have a pointer to an IPropertyStore object. ; If you use a new AutoIt version, thank to elegant ObjCreateInterface by tracexx,; ; we can easily call method from the pointer we have If ($__OPT_EXPERIMENTAL_OBJCREATEINTERFACE) Then Local $oPropertyStoreObject = ObjCreateInterface($pPropertyStoreObject, _ '{886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99}', _ 'GetCount HRESULT(PTR);GetAt HRESULT(DWORD; PTR);GetValue HRESULT(PTR; PTR);' & _ 'SetValue HRESULT(PTR; PTR);Commit HRESULT()') If Not IsObj($oPropertyStoreObject) Then Return SetError(1, 0, '') $oPropertyStoreObject.SetValue(DllStructGetPtr($tPROPERTYKEY), DllStructGetPtr($tPROPVARIANT)) $oPropertyStoreObject.Commit() Else ; But if you don't want to use an experimental function, we can do it manually ; First retrieve the v-table of the object. IPropertyStore is directly inherited from IUnknown ; so we have only one v-table data, merge of IUnknown and IPropertyStore, ; located at the beginning of the object address Local $pIPropertyStore = DllStructGetData(DllStructCreate('PTR', $pPropertyStoreObject), 1) Local $tIPropertyStore = DllStructCreate($tagIPropertyStore, $pIPropertyStore) ; With the v-table, we can call methods (yes we use another advanced function, ; but not a beta function so its safe as much as we use it correctly ; We simulate a c++ method, first parameter should be pointer to object itself. ; SetValue DllCallAddress('LONG', DllStructGetData($tIPropertyStore, 'SetValue'), _ 'PTR', $pPropertyStoreObject, _ ; pThis 'PTR', DllStructGetPtr($tPROPERTYKEY), _ 'PTR', DllStructGetPtr($tPROPVARIANT)) DllCallAddress('LONG', DllStructGetData($tIPropertyStore, 'Commit'), 'PTR', $pPropertyStoreObject) EndIf EndFunc Func _Win7Helper_GetWindowAppId($hWnd) Local Static $tPROPERTYKEY = _Win7Helper_PKEY_AppUserModel_ID() Local $tPROPVARIANT = DllStructCreate($tagPROPVARIANT) Local $tpIPropertyStore = DllStructCreate('ptr') _WinAPI_SHGetPropertyStoreForWindow($hWnd, '{886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99}', $tpIPropertyStore) Local $pPropertyStoreObject = DllStructGetData($tpIPropertyStore, 1) Local $oPropertyStoreObject = ObjCreateInterface($pPropertyStoreObject, _ '{886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99}', _ 'GetCount HRESULT(PTR);GetAt HRESULT(DWORD; PTR);GetValue HRESULT(PTR; PTR);' & _ 'SetValue HRESULT(PTR; PTR);Commit HRESULT()') If Not IsObj($oPropertyStoreObject) Then Return SetError(1, 0, '') $oPropertyStoreObject.GetValue(DllStructGetPtr($tPROPERTYKEY), DllStructGetPtr($tPROPVARIANT)) ; Extracts a string value from a PROPVARIANT structure ; http://deletethis.net/dave/dev/setappusermodelid/ ; https://msdn.microsoft.com/en-us/library/windows/desktop/bb776559(v=vs.85).aspx Local $buf = DllStructCreate('wchar[128]') DllCall('Propsys.dll', 'long', 'PropVariantToString', _ 'ptr', DllStructGetPtr($tPROPVARIANT), _ 'ptr', DllStructGetPtr($buf), _ 'uint', DllStructGetSize($buf)) If @error Then Return SetError(@error, @extended, "") Else Return DllStructGetData($buf, 1) EndIf EndFunc #cs ; SetCurrentProcessExplicitAppUserModelID DllCall("Shell32.dll", "long", "SetCurrentProcessExplicitAppUserModelID", _ "wstr", "MyGreatCompany.MyAwesomeApplication.MyElegantWindow1") #ce ; Demo $hWnd1 = GUICreate('Test1', 400, 200, 0, 100) $hWnd2 = GUICreate('Test2', 400, 200, 500, 100) $hWnd3 = GUICreate('Test3', 400, 200, 1000, 100) ; The application id should be unique so it not effect any other application. ; Better use a format like MyCompany(orMyName).MyApplication.WindowId _Win7Helper_SetWindowAppId($hWnd1, 'MyGreatCompany.MyAwesomeApplication.MyElegantWindow1') _Win7Helper_SetWindowAppId($hWnd2, 'MyGreatCompany.MyAwesomeApplication.OhImNotAWindowImAnAlien') _Win7Helper_SetWindowAppId($hWnd3, 'MyGreatCompany.MyAwesomeApplication.MyElegantWindow1') GUISetState(@SW_SHOW, $hWnd1) GUISetState(@SW_SHOW, $hWnd2) GUISetState(@SW_SHOW, $hWnd3) $appid1 = _Win7Helper_GetWindowAppId($hWnd1) $appid3 = _Win7Helper_GetWindowAppId($hWnd3) MsgBox(0, 'AppUserModelId', 'AppId of Test1 Window: ' & @CRLF & $appid1 & @CRLF & @CRLF & _ 'AppId of Test3 Window: ' & $appid3 & @CRLF & @CRLF & _ 'Their AppIds are the same, so their icons should be grouped together.', 0, $hWnd1) Do Until GUIGetMsg() = -3 #cs ; Another example -- get Google Chrome's AppId ; Start Chrome and use AU3Info to get the handle $hWnd = HWnd('0x00000000001002CC') $appid = _Win7Helper_GetWindowAppId($hWnd) ConsoleWrite('Old AppId = ' & $appid & @CRLF) _Win7Helper_SetWindowAppId($hWnd, 'chrome.123456') $appid = _Win7Helper_GetWindowAppId($hWnd) ConsoleWrite('New AppId = ' & $appid & @CRLF) #ceThis Func can get AppId of a window created by Autoit in this example. I tried to get other window's AppId,such as IE, Google Chrome, Firefox, successfully got Google Chrome's AppId , "Chrome.ZABMGAMPWEKMYFXAM6OVHWYM.UserData.Default", while others failed.I don't know why.
  8. I tried to get / set appid of a .lnk file in a stupid way.I read and parsed the .lnk file directly in binary to get appid info, GetAppId() was ok while SetAppId() failed. can anyone help? Codes deleted due to not work in some circumstance.
  9. binhnx, I'd like to get a window's appid by it's hWnd,is it possible? It would be appreciated if you would add the udf,let's say, _Win7Helper_GetWindowAppId($hWnd).
  10. trancexx,perfect! Tons of thanks to you,you are our hero
  11. I found this topic https://groups.google.com/forum/#!topic/microsoft.public.security.crypto/WHwxS2vG_0E point out the way: Subject Alternative Names (SAN) are certificate extensions. Programmatically you can get to them by accessing rgExtension member of the pCertInfo(CERT_INFO) member of the CERT_CONTEXT structure. Use CertFindExtension() to find an extension by oid = szOID_SUBJECT_ALT_NAME2. To format the encoded string into WCHAR string use CryptFormatObject(). And this: http://swift.im/git/swift/plain/Swiften/TLS/Schannel/SchannelCertificate.cpp , some c codes to parse SSL certificate,but I have no knowledge of c language,it puzzles me.
  12. Hi trancexx,thanks for your help. My system: Win 7 professional SP1 X64 English version with Chinese support(I'm in China) Autoit version: 3.3.12.0. WinHttp.au3: 1.6.3.7 Below is the console output: >"C:\Program Files (x86)\AutoIt3\SciTE\AutoIt3Wrapper\AutoIt3Wrapper.exe" /run /prod /ErrorStdOut /in "C:\Users\jxc3358\Desktop\chrome\SubjectAltName.au3" /UserParams +>09:08:57 Starting AutoIt3Wrapper v.15.503.1200.1 SciTE v.3.5.4.0 Keyboard:00000804 OS:WIN_7/Service Pack 1 CPU:X64 OS:X64 Environment(Language:0409) +> SciTEDir => C:\Program Files (x86)\AutoIt3\SciTE UserDir => C:\Users\jxc3358\AppData\Local\AutoIt v3\SciTE\AutoIt3Wrapper SCITE_USERHOME => C:\Users\jxc3358\AppData\Local\AutoIt v3\SciTE >Running AU3Check (3.3.12.0) from:C:\Program Files (x86)\AutoIt3 input:C:\Users\jxc3358\Desktop\chrome\SubjectAltName.au3 +>09:08:57 AU3Check ended.rc:0 >Running:(3.3.12.0):C:\Program Files (x86)\AutoIt3\autoit3.exe "C:\Users\jxc3358\Desktop\chrome\SubjectAltName.au3" --> Press Ctrl+Alt+Break to Restart or Ctrl+Break to Stop > CERT_CONTEXT pointer: 0x00ACE128 Subject Alternative Name - DNS Name= *.facebook.com +>09:09:00 AutoIt3.exe ended.rc:0 +>09:09:00 AutoIt3Wrapper Finished. >Exit code: 0 Time: 3.147
  13. So many thanks for your reply! I tried your code and got DNS name from Subject Alternative Name.But it seems this code only return the first DNS name while some site's certificate contain many alternative DNS names.Currently I simply use getpeercert() in Python to get the SubjectAltName info,but how can I get the full list of all DNS names using Autoit? Thanks again.
  14. Hello Trancexx, Thanks for your contribution,I've learned more from your UDF. I need SSL certificate info from remote server,"https://www.autoitscript.com" for example.Codes below can get some certificate info as ExpiryTime, Subject etc.. Actually I need "Subject Alternative Name" info like "DNS Name=www.autoitscript.com DNS Name=autoitscript.com" from the certificate,It seems this info is contained in the "extention" part of certificate.Could any one point me how should I do? Thanks! #include <WinHttp.au3> Global Const $tagINTERNET_CERTIFICATE_INFO = "dword ExpiryTime[2]; dword StartTime[2]; ptr SubjectInfo; " & _ "ptr IssuerInfo; ptr ProtocolName; ptr SignatureAlgName; ptr EncryptionAlgName; dword KeySize" $hOpen = _WinHttpOpen() _WinHttpSetOption($hOpen, $WINHTTP_OPTION_SECURITY_FLAGS, 0x00003300) ; $SECURITY_FLAG_IGNORE_ALL $hConnect = _WinHttpConnect($hOpen, "www.autoitscript.com", 443) $hRequest = _WinHttpSimpleSendSSLRequest($hConnect, "GET", "/") ; I don't know why I failed to get info via _WinHttpQueryOption ;~ Global $tBuffer = _WinHttpQueryOption($hRequest, $WINHTTP_OPTION_SECURITY_CERTIFICATE_STRUCT) ;~ Global $tINTERNET_CERTIFICATE_INFO = DllStructCreate($tagINTERNET_CERTIFICATE_INFO, DllStructGetPtr($tBuffer)) ;~ Global $tInfo = DllStructCreate("wchar [128]", DllStructGetData($tINTERNET_CERTIFICATE_INFO, "SubjectInfo")) ;~ Global $Info = DllStructGetData($tInfo, 1) ;~ ConsoleWrite('SSLCertificate Info: ' & @CRLF & $Info & @CRLF & @CRLF) $tBufferLength = DllStructCreate("dword") DllStructSetData($tBufferLength, 1, 2048) $tBuffer = DllStructCreate("byte[2048]") $avResult = DllCall($hWINHTTPDLL__WINHTTP, "bool", "WinHttpQueryOption", _ "handle", $hRequest, _ "dword", $WINHTTP_OPTION_SECURITY_CERTIFICATE_STRUCT, _ "struct*", DllStructGetPtr($tBuffer), _ "dword*", DllStructGetPtr($tBufferLength)) If Not @error And $avResult[0] Then $tINTERNET_CERTIFICATE_INFO = DllStructCreate($tagINTERNET_CERTIFICATE_INFO, DllStructGetPtr($tBuffer)) ; Get Subject $tInfo = DllStructCreate("wchar [128]", DllStructGetData($tINTERNET_CERTIFICATE_INFO, "SubjectInfo")) $Info = DllStructGetData($tInfo, 1) ConsoleWrite('SSLCertificate Info: ' & @CRLF & $Info & @CRLF & @CRLF) ;Get ExpiryTime $tInfo = DllStructCreate("int64", DllStructGetPtr($tINTERNET_CERTIFICATE_INFO, "ExpiryTime")) $Info = DllStructGetData($tInfo, 1) ConsoleWrite('SSLCertificate Info: ' & @CRLF & $Info & @CRLF & @CRLF) ; How to get SSL certificate extention info? I need "Subject Alternative Name" like "DNS Name=www.autoitscript.com DNS Name=autoitscript.com" EndIf $tBufferLength = 0 $tBuffer = 0 $tINTERNET_CERTIFICATE_INFO = 0 $tInfo = 0 _WinHttpCloseHandle($hRequest) _WinHttpCloseHandle($hConnect) _WinHttpCloseHandle($hOpen)
  15. This script gets the ExpiryTime and issuerinfo,etc.. But how to get the extension info of the certificate as subjectAltName like "DNS Name=www.google.com" ? #include <WinINet.au3> ; Initialize WinINet _WinINet_Startup() ; Set variables Global $sURIScheme = "https" Global $sServerName = "www.autoitscript.com" Global $iServerPort = 443 ; Create handles ConsoleWrite("Opening Internet connection ..." & @LF) Global $hInternetOpen = _WinINet_InternetOpen("AutoIt/" & @AutoItVersion) if @error Then ConsoleWrite("Open Internet connection: failed." & @LF) Else ConsoleWrite("Open Internet connection: ok." & @LF) EndIf Global $hInternetOpenUrl = _WinINet_InternetOpenUrl($hInternetOpen, $sURIScheme & "://" & $sServerName, Default, $INTERNET_FLAG_SECURE) if @error Then ConsoleWrite("Open Internet URL: failed." & @LF) Else ConsoleWrite("Open Internet URL: ok." & @LF) EndIf ; Query certificate info... Global $tBuffer = _WinINet_InternetQueryOption($hInternetOpenUrl, $INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT) Local $tINTERNET_CERTIFICATE_INFO = DllStructCreate($tagINTERNET_CERTIFICATE_INFO, DllStructGetPtr($tBuffer)) Local $tExpiryTime = DllStructCreate("int64", DllStructGetPtr($tINTERNET_CERTIFICATE_INFO, "ExpiryTime")) ConsoleWrite("Expiry Time (100-nanoseconds): " & DllStructGetData($tExpiryTime, 1) & @LF) Local $tInfo = DllStructCreate("char [256]", DllStructGetData($tINTERNET_CERTIFICATE_INFO, "IssuerInfo")) ConsoleWrite("IssuerInfo: " & DllStructGetData($tInfo, 1) & @LF) ; Cleanup _WinINet_InternetCloseHandle($hInternetOpenUrl) _WinINet_InternetCloseHandle($hInternetOpen) _WinINet_Shutdown() ConsoleWrite("End of program..." & @LF)
  16. How to group different apps on Win 7+ taskbar? I pin my "chrome_portable_loader.exe" on taskbar to launch chrome browser, I wish chrome's icon can group with the "chrome_portable_loader.exe" icon on taskbar. Here is a solution, https://code.google.com/p/win7appid/, setting the shortcut's AppID the same as chrome window's AppUserModelId. see also http://msdn.microsoft.com/en-us/library/dd378459%28VS.85%29.aspx. Can someone translate it to Autoit? Thanks! // // Win7AppId // Author: David Roe (didroe) // The code's a bit rough and ready but it does the job // // Compile with VS 2010 express using command: // cl /EHsc /D _UNICODE Win7AppId.cpp /link ole32.lib #include <windows.h> #include <shobjidl.h> #include <propkey.h> #include <tchar.h> #include <iostream> using namespace std; EXTERN_C const PROPERTYKEY DECLSPEC_SELECTANY PKEY_AppUserModel_ID = { { 0x9F4C2855, 0x9F79, 0x4B39, { 0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3, } }, 5 }; void die(string message) { cout << "Error: " << message.c_str() << endl; exit(1); } void doOrDie(HRESULT hr, string message) { if (!SUCCEEDED(hr)) { die(message); } } int _tmain(int argc, _TCHAR* argv[]) { doOrDie(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED), "Failed to initialise COM"); IShellLink *link; doOrDie(CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&link)), "Failed to create ShellLink object"); IPersistFile* file; doOrDie(link->QueryInterface(IID_PPV_ARGS(&file)), "Failed to obtain PersistFile interface"); if (argc > 2) { doOrDie(file->Load(argv[1], STGM_READWRITE), "Failed to load shortcut file"); } else { doOrDie(file->Load(argv[1], STGM_READ | STGM_SHARE_DENY_NONE), "Failed to load shortcut file"); } IPropertyStore* store; doOrDie(link->QueryInterface(IID_PPV_ARGS(&store)), "Failed to obtain PropertyStore interface"); PROPVARIANT pv; doOrDie(store->GetValue(PKEY_AppUserModel_ID, &pv), "Failed to retrieve AppId"); if (pv.vt != VT_EMPTY) { if (pv.vt != VT_LPWSTR) { cout << "Type: " << pv.vt << endl; die("Unexpected property value type"); } wcout << "Current AppId: " << pv.pwszVal << endl; } else { cout << "No current AppId" << endl; } PropVariantClear(&pv); if (argc > 2) { wcout << "New AppId: " << argv[2] << endl; pv.vt = VT_LPWSTR; pv.pwszVal = argv[2]; doOrDie(store->SetValue(PKEY_AppUserModel_ID, pv), "Failed to set AppId"); // Not sure if we need to do this pv.pwszVal = NULL; PropVariantClear(&pv); doOrDie(store->Commit(), "Failed to commit AppId property"); doOrDie(file->Save(NULL, TRUE), "Failed to save shortcut"); } store->Release(); file->Release(); link->Release(); return 0; }  
  17. Thanks Jos! It will be really nice if Obfuscator and Au3Check could support UNICODE.
  18. Recently I installed Autoit 3.3.8.1 and SciTE4AutoIt3 12/29/2011 with AutoIt3Wrapper 2.1.0.8. My scripts contained Unicode characters(Chinese).When I Compiled these scripts,I got these info: ! * Input file is UTF8 without BOM encoded, Obfuscator do not support UNICODE and will be skipped. * ! * The file SHOULD BE encoded as UTF8 with BOM to continue processing by AutoIT3Wrapper. Actually I have successfully obfuscated(stripped) many Unicode encoded scripts using #Obfuscator_Parameters=/striponly.So I just reverted AutoIT3Wrapper to 2.0.1.24 and tried to obfuscate/Check my script with Obfuscator/AU3Check,it worked without any problem. So I guess Obfuscator/AU3Check supports Unicode,Chinese at least.There should be an option for Unicode check in AutoIt3Wrapper. I compared source of AutoIT3Wrapper 2.0.1.24 with AutoIT3Wrapper 2.1.0.8,I find below: AutoIT3Wrapper 2.0.1.24: If Not $InputFileIsUTF16 And $Option = "Compile" And $INP_Run_Obfuscator ="y" Then Global $ObfuscatorpgmVer = "" Global $Obfuscatorpgm = $SciTE_Dir & "ObfuscatorObfuscator.exe" ... AutoIT3Wrapper 2.1.0.8: If $Option = "Compile" And $INP_Run_Obfuscator = "y" Then If $InputFileIsUTF8 Or $InputFileIsUTF16 Or $InputFileIsUTF32 Then ;~ ConsoleWrite("!*************************************************************************************" & @CRLF) ConsoleWrite("! Input file is UTF" & $UTFtype & " encoded.Obfuscator does not support this and will be skipped." & @CRLF) ;~ ConsoleWrite("!*************************************************************************************" & @CRLF) Else Global $ObfuscatorpgmVer = "" Global $Obfuscatorpgm = $SciTE_Dir & "ObfuscatorObfuscator.exe" ...
  19. Now I have a simple solution: #include <GUIConstantsEx.au3> opt("GUIONEVENTMODE", 1) GUICreate("Test", 300, 200) GUISetOnEvent($GUI_EVENT_CLOSE, "_Exit") GUICtrlCreateButton("Run NotePad", 60, 150) GUICtrlSetOnEvent(-1,"_RunNotePad") GUICtrlCreateButton("Test", 200, 150) GUICtrlSetOnEvent(-1,"_Test") GUISetState() While 1 Sleep(100) WEnd func _RunNotePad() AdlibRegister("_DoRunNotePad", 10) ; leave the task to a timer and return as soon as possible EndFunc Func _DoRunNotePad() ; This routine can be interrupted by Gui Events AdlibUnRegister("_DoRunNotePad") ; Unregister the timer RunWait("notepad.exe") EndFunc func _Test() MsgBox(0,"", "Test") EndFunc Func _Exit() Exit EndFunc
  20. I think so too! An example as below:Preass "Run Notepad" to launch notepad.Then the button "test" will not available untill you close notepad. #include <GUIConstantsEx.au3> opt("GUIONEVENTMODE", 1) GUICreate("Test", 300, 200) GUISetOnEvent($GUI_EVENT_CLOSE, "_Exit") GUICtrlCreateButton("Run NotePad", 60, 150) GUICtrlSetOnEvent(-1,"_RunNotePad") GUICtrlCreateButton("Test", 200, 150) GUICtrlSetOnEvent(-1,"_Test") GUISetState() While 1 Sleep(100) WEnd func _RunNotePad() RunWait("notepad.exe") EndFunc func _Test() MsgBox(0,"", "Test") ; Still not called.... EndFunc Func _Exit() Exit EndFunc AutoHotkey's events can interrupt each other, though.See below from AutoHotkey helpfile: Although AutoHotkey doesn't actually use multiple threads, it simulates some of that behavior: If a second thread is started -- such as by pressing another hotkey while the previous is still running -- the current thread will be interrupted (temporarily halted) to allow the new thread to become current. If a third thread is started while the second is still running, both the second and first will be in a dormant state, and so on. When the current thread finishes, the one most recently interrupted will be resumed, and so on, until all the threads finally finish. When resumed, a thread's settings for things such as ErrorLevel and SendMode are automatically restored to what they were just prior to its interruption; in other words, a thread will experience no side-effects from having been interrupted (except for a possible change in the active window). A single script can have multiple simultaneous MsgBox, InputBox, FileSelectFile, and FileSelectFolder dialogs. This is achieved by launching a new thread (via hotkey, timed subroutine, custom menu item, etc.) while a prior thread already has a dialog displayed. Example: Gui, Add, Button, x60 y150 gRunNotePad, Run NotePad Gui, Add, Button, x200 y150 gTest, Test Gui, Show, w300 h200 Return GuiClose: ExitApp Return RunNotePad: RunWait, notepad.exe Return Test: MsgBox, Test Return
  21. I tried to change this line: $ret = DllCall("kernel32.dll", "Int", "ReadDirectoryChangesW", "hwnd", $hDir, "ptr", $pBuffer, "dword", $iBufferSize, "int", False, "dword", BitOR(0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x100), "Uint", 0, "Uint", $pOverLapped, "Uint", 0) to $ret = DllCall("kernel32.dll", "Int", "ReadDirectoryChangesW", "hwnd", $hDir, "ptr", $pBuffer, "dword", $iBufferSize, "int", True, "dword", BitOR(0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x100), "Uint", 0, "Uint", $pOverLapped, "Uint", 0) Now this problem solved.
  22. This question has been bothering me for a long time. Tvern:Those script you mentioned do not support monitoring subdiretories. There is a great function in Autohotkey forum named WatchDiretory() which can monitor filesystem with many options. Here is the function:WatchDirectory().zip Here is the original topic:WatchDiretory() It will be greatly appreciated if someone could translate this function from Autohotkey to Autoit. Someone made a similar autoit script like this: [NOW WORKING] a (broken) monitor file changes script which uses ReadDirectoryChangeW But it's not an independant funtion and is not as flexible as WatchDiretory().
  23. This script drives my CPU up to 100%.I do need this functionality, so I tried to modify those scripts myself. According to http://msdn.microsoft.com/en-us/library/ms684245%28VS.85%29.aspx,if change these lines: $aMsg = DllCall("User32.dll", "dword", "MsgWaitForMultipleObjectsEx", _ "dword", UBound($aOverlapped), _ "ptr", DllStructGetPtr($hEvents), _ "dword", $nTimeout, _ "dword", 0, _ "dword", 0x6) to: $aMsg = DllCall("User32.dll", "dword", "MsgWaitForMultipleObjectsEx", _ "dword", UBound($aOverlapped), _ "ptr", DllStructGetPtr($hEvents), _ "dword", $nTimeout, _ "dword", 0x4FF, _ "dword", 0x6) The script will not be resource-consuming. And we should do some cleaning and close handles when stopping monitoring, thus the monitor can stop and restart without crash. I've developed a UDF _MonitorDirectory() for filesystem monitoring, the function with an example as below: ;~ Example OnAutoItExitRegister("_Exit") Global $aDirs[2] $aDirs[0] = @ScriptDir $aDirs[1] = "C:\" _MonitorDirectory($aDirs, True, 100, "_ReportFileChanges") While 1 Sleep(20) WEnd ;~ This function will be called when changes are registered Func _ReportFileChanges($Action, $FilePath) ;~ Your own script here ... ;~ ... ConsoleWrite($Action & ": " & $FilePath & @CRLF) TrayTip("MonitorDirectory", $Action & ": " & $FilePath, 5, 1) EndFunc ;==>_ReportFileChanges ;~ Stop monitoring before exiting at least Func _Exit() _MonitorDirectory() Exit EndFunc ;==>_Quit ;~ =========================== FUNCTION _MonitorDirectory() ============================== #cs Description: Monitors the user defined directories for file activity. Original: http://www.autoitscript.com/forum/index.php?showtopic=69044&hl=folderspy&st=0 Modified: Jack Chen Syntax: _MonitorDirectory($Dirs = "", $Subtree = True, $TimerMs = 250, $Function = "_ReportChanges") Parameters: $Dirs - Optional: Zero-based array of valid directories to be monitored. $Subtree - Optional: Subtrees will be monitored if $Subtree = True. $TimerMs - Optional: Timer to register changes in milliseconds. $Function - Optional: Function to launch when changes are registered. e.g. _ReportChanges Syntax of your function must be e.g._ReportChanges($Action, $FilePath) Possible actions: Created, Deleted, Modified, Rename-, Rename+, Unknown Remarks: Call _MonitorDirectory() without parameters to stop monitoring all directories. THIS SHOULD BE DONE BEFORE EXITING SCRIPT AT LEAST. #ce Func _MonitorDirectory($Dirs = "", $Subtree = True, $TimerMs = 250, $Function = "_ReportChanges") Local Static $nMax, $hBuffer, $hEvents, $aSubtree, $sFunction If IsArray($Dirs) Then $nMax = UBound($Dirs) ElseIf $nMax < 1 Then Return EndIf Local Static $aDirHandles[$nMax], $aOverlapped[$nMax], $aDirs[$nMax] If IsArray($Dirs) Then $aDirs = $Dirs $aSubtree = $Subtree $sFunction = $Function ;~ $hBuffer = DllStructCreate("byte[4096]") $hBuffer = DllStructCreate("byte[65536]") For $i = 0 To $nMax - 1 If StringRight($aDirs[$i], 1) <> "\" Then $aDirs[$i] &= "\" ;~ http://msdn.microsoft.com/en-us/library/aa363858%28VS.85%29.aspx $aResult = DllCall("kernel32.dll", "hwnd", "CreateFile", "Str", $aDirs[$i], "Int", 0x1, "Int", BitOR(0x1, 0x4, 0x2), "ptr", 0, "int", 0x3, "int", BitOR(0x2000000, 0x40000000), "int", 0) $aDirHandles[$i] = $aResult[0] $aOverlapped[$i] = DllStructCreate("ulong_ptr Internal;ulong_ptr InternalHigh;dword Offset;dword OffsetHigh;handle hEvent") For $j = 1 To 5 DllStructSetData($aOverlapped, $j, 0) Next _SetReadDirectory($aDirHandles[$i], $hBuffer, $aOverlapped[$i], True, $aSubtree) Next $hEvents = DllStructCreate("hwnd hEvent[" & UBound($aOverlapped) & "]") For $j = 1 To UBound($aOverlapped) DllStructSetData($hEvents, "hEvent", DllStructGetData($aOverlapped[$j - 1], "hEvent"), $j) Next AdlibRegister("_GetChanges", $TimerMs) ElseIf $Dirs = "ReadDirChanges" Then _GetDirectoryChanges($aDirHandles, $hBuffer, $aOverlapped, $hEvents, $aDirs, $aSubtree, $sFunction) ElseIf $Dirs = "" Then AdlibUnRegister("_GetChanges") ;~ Close Handle For $i = 0 To $nMax - 1 DllCall("kernel32.dll", "bool", "CloseHandle", "handle", $aDirHandles[$i]) DllCall("kernel32.dll", "bool", "CloseHandle", "handle", $aOverlapped[$i]) Next DllCall("kernel32.dll", "bool", "CloseHandle", "handle", $hEvents) $nMax = 0 $hBuffer = "" $hEvents = "" $aDirHandles = "" $aOverlapped = "" $aDirs = "" $aSubtree = "" $sFunction = "" EndIf EndFunc ;==>_MonitorDirectory Func _SetReadDirectory($hDir, $hBuffer, $hOverlapped, $bInitial, $bSubtree) Local $hEvent, $pBuffer, $nBufferLength, $pOverlapped $pBuffer = DllStructGetPtr($hBuffer) $nBufferLength = DllStructGetSize($hBuffer) $pOverlapped = DllStructGetPtr($hOverlapped) If $bInitial Then $hEvent = DllCall("kernel32.dll", "hwnd", "CreateEvent", "UInt", 0, "Int", True, "Int", False, "UInt", 0) DllStructSetData($hOverlapped, "hEvent", $hEvent[0]) EndIf ;~ http://msdn.microsoft.com/en-us/library/aa365465%28VS.85%29.aspx $aResult = DllCall("kernel32.dll", "Int", "ReadDirectoryChangesW", "hwnd", $hDir, "ptr", $pBuffer, "dword", $nBufferLength, "int", $bSubtree, "dword", BitOR(0x1, 0x2, 0x4, 0x8, 0x10, 0x40, 0x100), "Uint", 0, "Uint", $pOverlapped, "Uint", 0) Return $aResult[0] EndFunc ;==>_SetReadDirectory Func _GetChanges() _MonitorDirectory("ReadDirChanges") EndFunc ;==>_GetChanges Func _GetDirectoryChanges($aDirHandles, $hBuffer, $aOverlapped, $hEvents, $aDirs, $aSubtree, $sFunction) Local $aMsg, $i, $nBytes = 0 $aMsg = DllCall("User32.dll", "dword", "MsgWaitForMultipleObjectsEx", "dword", UBound($aOverlapped), "ptr", DllStructGetPtr($hEvents), "dword", -1, "dword", 0x4FF, "dword", 0x6) $i = $aMsg[0] Switch $i Case 0 To UBound($aDirHandles) - 1 DllCall("Kernel32.dll", "Uint", "ResetEvent", "uint", DllStructGetData($aOverlapped[$i], "hEvent")) _ParseFileMessages($hBuffer, $aDirs[$i], $sFunction) _SetReadDirectory($aDirHandles[$i], $hBuffer, $aOverlapped[$i], False, $aSubtree) Return $nBytes EndSwitch Return 0 EndFunc ;==>_GetDirectoryChanges Func _ParseFileMessages($hBuffer, $sDir, $sFunction) Local $hFileNameInfo, $pBuffer, $FilePath Local $nFileNameInfoOffset = 12, $nOffset = 0, $nNext = 1 $pBuffer = DllStructGetPtr($hBuffer) While $nNext <> 0 $hFileNameInfo = DllStructCreate("dword NextEntryOffset;dword Action;dword FileNameLength", $pBuffer + $nOffset) $hFileName = DllStructCreate("wchar FileName[" & DllStructGetData($hFileNameInfo, "FileNameLength") / 2 & "]", $pBuffer + $nOffset + $nFileNameInfoOffset) Switch DllStructGetData($hFileNameInfo, "Action") Case 0x1 ; $FILE_ACTION_ADDED $Action = "Created" Case 0x2 ; $FILE_ACTION_REMOVED $Action = "Deleted" Case 0x3 ; $FILE_ACTION_MODIFIED $Action = "Modified" Case 0x4 ; $FILE_ACTION_RENAMED_OLD_NAME $Action = "Rename-" Case 0x5 ; $FILE_ACTION_RENAMED_NEW_NAME $Action = "Rename+" Case Else $Action = "Unknown" EndSwitch $FilePath = $sDir & DllStructGetData($hFileName, "FileName") Call($sFunction, $Action, $FilePath) ; Launch the specified function $nNext = DllStructGetData($hFileNameInfo, "NextEntryOffset") $nOffset += $nNext WEnd EndFunc ;==>_ParseFileMessages ;~ ===========================End of FUNCTION _MonitorDirectory() ============================== I have modified zorphnog's FolderSpy with this function _MonitorDirectory().Now it's light and reliable. Code: #cs ---------------------------------------------------------------------------- AutoIt Version: 3.3.6.1 Author: zorphnog (M. Mims) Modified: Jackchen Script Function: Monitors the user defined directories for file activity. #ce ---------------------------------------------------------------------------- #include <Constants.au3> #include <WinAPI.au3> #include <Date.au3> #include <GUIConstants.au3> #include <GuiListBox.au3> #include <GuiListView.au3> #include <Array.au3> Global $bMonitoring = False AutoItSetOption("GUIOnEventMode", 1) $gFileMon = GUICreate("Directory Monitor", 731, 385, 194, 126) GUISetOnEvent($GUI_EVENT_CLOSE, "_OnEvent_Close") GUICtrlCreateGroup("Monitored Directories", 8, 0, 713, 105) $btAdd = GUICtrlCreateButton("Add", 16, 24, 75, 25, 0) GUICtrlSetOnEvent(-1, "_OnEvent_Add") $btRemove = GUICtrlCreateButton("Remove", 16, 56, 75, 25, 0) GUICtrlSetOnEvent(-1, "_OnEvent_Remove") GUICtrlSetState(-1, $GUI_DISABLE) $lbDirectories = GUICtrlCreateList("", 104, 16, 506, 71) $btMonitor = GUICtrlCreateButton("Start Monitor", 632, 24, 75, 25, 0) GUICtrlSetOnEvent(-1, "_OnEvent_Monitor") GUICtrlSetState(-1, $GUI_DISABLE) $btClear = GUICtrlCreateButton("Clear", 632, 56, 75, 25, 0) GUICtrlSetOnEvent(-1, "_OnEvent_Clear") GUICtrlCreateGroup("", -99, -99, 1, 1) $lvNotifications = GUICtrlCreateListView("Action|Time|File", 8, 112, 714, 262) GUICtrlSendMsg(-1, 0x101E, 0, Int(.1 * 710)) GUICtrlSendMsg(-1, 0x101E, 1, Int(.2 * 710)) GUICtrlSendMsg(-1, 0x101E, 2, Int(.7 * 710) - 20) GUISetState(@SW_SHOW) While 1 If Not $bMonitoring And _GUICtrlListBox_GetCurSel($lbDirectories) <> -1 And GUICtrlGetState($btRemove) = $GUI_SHOW + $GUI_DISABLE Then GUICtrlSetState($btRemove, $GUI_ENABLE) EndIf Sleep(20) WEnd Func _ReportChanges($Action, $FilePath) Local $hTime $hTime = _Date_Time_GetSystemTime() _GUICtrlListView_InsertItem($lvNotifications, $Action, 0) _GUICtrlListView_AddSubItem($lvNotifications, 0, _Date_Time_SystemTimeToDateTimeStr($hTime), 1) _GUICtrlListView_AddSubItem($lvNotifications, 0, $FilePath, 2) EndFunc ;==>_ReportChanges Func _OnEvent_Add() Local $sDir, $nMax, $i $sDir = FileSelectFolder("Select directory to monitor", "") If $sDir <> "" Then If StringRight($sDir, 1) <> "\" Then $sDir &= "\" $nMax = _GUICtrlListBox_GetCount($lbDirectories) - 1 For $i = 0 To $nMax If _GUICtrlListBox_GetText($lbDirectories, $i) = $sDir Then Return Next _GUICtrlListBox_AddString($lbDirectories, $sDir) GUICtrlSetState($btMonitor, $GUI_ENABLE) EndIf EndFunc ;==>_OnEvent_Add Func _OnEvent_Clear() _GUICtrlListView_DeleteAllItems(GUICtrlGetHandle($lvNotifications)) EndFunc ;==>_OnEvent_Clear Func _OnEvent_Close() If $bMonitoring Then _MonitorDirectory() Exit EndFunc ;==>_OnEvent_Close Func _OnEvent_Monitor() If Not $bMonitoring Then $bMonitoring = True GUICtrlSetData($btMonitor, "Stop Monitor") GUICtrlSetState($btAdd, $GUI_DISABLE) GUICtrlSetState($btRemove, $GUI_DISABLE) GUICtrlSetState($lbDirectories, $GUI_DISABLE) $n = _GUICtrlListBox_GetCount($lbDirectories) Local $Dirs[$n] For $i = 0 To $n - 1 $Dirs[$i] = _GUICtrlListBox_GetText($lbDirectories, $i) Next _MonitorDirectory($Dirs) Else $bMonitoring = False GUICtrlSetState($lbDirectories, $GUI_ENABLE) GUICtrlSetState($btAdd, $GUI_ENABLE) GUICtrlSetData($btMonitor, "Start Monitor") _MonitorDirectory() EndIf EndFunc ;==>_OnEvent_Monitor Func _OnEvent_Remove() _GUICtrlListBox_DeleteString($lbDirectories, _GUICtrlListBox_GetCurSel($lbDirectories)) If _GUICtrlListBox_GetCount($lbDirectories) = 0 Then GUICtrlSetState($btMonitor, $GUI_DISABLE) EndIf EndFunc ;==>_OnEvent_Remove ;~ =========================== FUNCTION _MonitorDirectory() ============================== #cs Description: Monitors the user defined directories for file activity. Original: http://www.autoitscript.com/forum/index.php?showtopic=69044&hl=folderspy&st=0 Modified: Jack Chen Syntax: _MonitorDirectory($Dirs = "", $Subtree = True, $TimerMs = 250, $Function = "_ReportChanges") Parameters: $Dirs - Optional: Zero-based array of valid directories to be monitored. $Subtree - Optional: Subtrees will be monitored if $Subtree = True. $TimerMs - Optional: Timer to register changes in milliseconds. $Function - Optional: Function to launch when changes are registered. e.g. _ReportChanges Syntax of your function must be e.g._ReportChanges($Action, $FilePath) Possible actions: Created, Deleted, Modified, Rename-, Rename+, Unknown Remarks: Call _MonitorDirectory() without parameters to stop monitoring all directories. THIS SHOULD BE DONE BEFORE EXITING SCRIPT AT LEAST. #ce Func _MonitorDirectory($Dirs = "", $Subtree = True, $TimerMs = 250, $Function = "_ReportChanges") Local Static $nMax, $hBuffer, $hEvents, $aSubtree, $sFunction If IsArray($Dirs) Then $nMax = UBound($Dirs) ElseIf $nMax < 1 Then Return EndIf Local Static $aDirHandles[$nMax], $aOverlapped[$nMax], $aDirs[$nMax] If IsArray($Dirs) Then $aDirs = $Dirs $aSubtree = $Subtree $sFunction = $Function ;~ $hBuffer = DllStructCreate("byte[4096]") $hBuffer = DllStructCreate("byte[65536]") For $i = 0 To $nMax - 1 If StringRight($aDirs[$i], 1) <> "\" Then $aDirs[$i] &= "\" ;~ http://msdn.microsoft.com/en-us/library/aa363858%28VS.85%29.aspx $aResult = DllCall("kernel32.dll", "hwnd", "CreateFile", "Str", $aDirs[$i], "Int", 0x1, "Int", BitOR(0x1, 0x4, 0x2), "ptr", 0, "int", 0x3, "int", BitOR(0x2000000, 0x40000000), "int", 0) $aDirHandles[$i] = $aResult[0] $aOverlapped[$i] = DllStructCreate("ulong_ptr Internal;ulong_ptr InternalHigh;dword Offset;dword OffsetHigh;handle hEvent") For $j = 1 To 5 DllStructSetData($aOverlapped, $j, 0) Next _SetReadDirectory($aDirHandles[$i], $hBuffer, $aOverlapped[$i], True, $aSubtree) Next $hEvents = DllStructCreate("hwnd hEvent[" & UBound($aOverlapped) & "]") For $j = 1 To UBound($aOverlapped) DllStructSetData($hEvents, "hEvent", DllStructGetData($aOverlapped[$j - 1], "hEvent"), $j) Next AdlibRegister("_GetChanges", $TimerMs) ElseIf $Dirs = "ReadDirChanges" Then _GetDirectoryChanges($aDirHandles, $hBuffer, $aOverlapped, $hEvents, $aDirs, $aSubtree, $sFunction) ElseIf $Dirs = "" Then AdlibUnRegister("_GetChanges") ;~ Close Handle For $i = 0 To $nMax - 1 DllCall("kernel32.dll", "bool", "CloseHandle", "handle", $aDirHandles[$i]) DllCall("kernel32.dll", "bool", "CloseHandle", "handle", $aOverlapped[$i]) Next DllCall("kernel32.dll", "bool", "CloseHandle", "handle", $hEvents) $nMax = 0 $hBuffer = "" $hEvents = "" $aDirHandles = "" $aOverlapped = "" $aDirs = "" $aSubtree = "" $sFunction = "" EndIf EndFunc ;==>_MonitorDirectory Func _SetReadDirectory($hDir, $hBuffer, $hOverlapped, $bInitial, $bSubtree) Local $hEvent, $pBuffer, $nBufferLength, $pOverlapped $pBuffer = DllStructGetPtr($hBuffer) $nBufferLength = DllStructGetSize($hBuffer) $pOverlapped = DllStructGetPtr($hOverlapped) If $bInitial Then $hEvent = DllCall("kernel32.dll", "hwnd", "CreateEvent", "UInt", 0, "Int", True, "Int", False, "UInt", 0) DllStructSetData($hOverlapped, "hEvent", $hEvent[0]) EndIf ;~ http://msdn.microsoft.com/en-us/library/aa365465%28VS.85%29.aspx $aResult = DllCall("kernel32.dll", "Int", "ReadDirectoryChangesW", "hwnd", $hDir, "ptr", $pBuffer, "dword", $nBufferLength, "int", $bSubtree, "dword", BitOR(0x1, 0x2, 0x4, 0x8, 0x10, 0x40, 0x100), "Uint", 0, "Uint", $pOverlapped, "Uint", 0) Return $aResult[0] EndFunc ;==>_SetReadDirectory Func _GetChanges() _MonitorDirectory("ReadDirChanges") EndFunc ;==>_GetChanges Func _GetDirectoryChanges($aDirHandles, $hBuffer, $aOverlapped, $hEvents, $aDirs, $aSubtree, $sFunction) Local $aMsg, $i, $nBytes = 0 $aMsg = DllCall("User32.dll", "dword", "MsgWaitForMultipleObjectsEx", "dword", UBound($aOverlapped), "ptr", DllStructGetPtr($hEvents), "dword", -1, "dword", 0x4FF, "dword", 0x6) $i = $aMsg[0] Switch $i Case 0 To UBound($aDirHandles) - 1 DllCall("Kernel32.dll", "Uint", "ResetEvent", "uint", DllStructGetData($aOverlapped[$i], "hEvent")) _ParseFileMessages($hBuffer, $aDirs[$i], $sFunction) _SetReadDirectory($aDirHandles[$i], $hBuffer, $aOverlapped[$i], False, $aSubtree) Return $nBytes EndSwitch Return 0 EndFunc ;==>_GetDirectoryChanges Func _ParseFileMessages($hBuffer, $sDir, $sFunction) Local $hFileNameInfo, $pBuffer, $FilePath Local $nFileNameInfoOffset = 12, $nOffset = 0, $nNext = 1 $pBuffer = DllStructGetPtr($hBuffer) While $nNext <> 0 $hFileNameInfo = DllStructCreate("dword NextEntryOffset;dword Action;dword FileNameLength", $pBuffer + $nOffset) $hFileName = DllStructCreate("wchar FileName[" & DllStructGetData($hFileNameInfo, "FileNameLength") / 2 & "]", $pBuffer + $nOffset + $nFileNameInfoOffset) Switch DllStructGetData($hFileNameInfo, "Action") Case 0x1 ; $FILE_ACTION_ADDED $Action = "Created" Case 0x2 ; $FILE_ACTION_REMOVED $Action = "Deleted" Case 0x3 ; $FILE_ACTION_MODIFIED $Action = "Modified" Case 0x4 ; $FILE_ACTION_RENAMED_OLD_NAME $Action = "Rename-" Case 0x5 ; $FILE_ACTION_RENAMED_NEW_NAME $Action = "Rename+" Case Else $Action = "Unknown" EndSwitch $FilePath = $sDir & DllStructGetData($hFileName, "FileName") Call($sFunction, $Action, $FilePath) ; Launch the specified function $nNext = DllStructGetData($hFileNameInfo, "NextEntryOffset") $nOffset += $nNext WEnd EndFunc ;==>_ParseFileMessages ;~ ===========================End of FUNCTION _MonitorDirectory() ==============================
×
×
  • Create New...