FadeSoft Posted Saturday at 01:10 AM Posted Saturday at 01:10 AM I am trying to get this working but can't seem to get it or find a easier way about going about this. Attached picture for context. expandcollapse popup#include <GUIConstantsEx.au3> #include <WindowsConstants.au3> ; ====== Define all necessary GUIDs ====== ; CLSIDs Global Const $CLSID_DestinationList = "{77f10cf0-3db5-4966-b520-b7c54fd35ed6}" Global Const $CLSID_EnumerableObjectCollection = "{2d3468c1-36a7-43b6-ac24-d3f02fd9607a}" Global Const $CLSID_ShellLink = "{00021401-0000-0000-C000-000000000046}" ; IIDs Global Const $IID_ICustomDestinationList = "{6332debf-87b5-4673-8ff5-7ca6a2ff46a8}" Global Const $IID_IObjectCollection = "{2d3468c1-36a7-43b6-ac24-d3f02fd9607a}" Global Const $IID_IObjectArray = "{92ca9dcd-5622-4bba-a805-5e9f541bd8c9}" Global Const $IID_IShellLinkW = "{000214F9-0000-0000-C000-000000000046}" Global Const $IID_IPropertyStore = "{886d8eeb-8cf2-4446-8d02-cdba1dbdcf99}" ; Property key for title Global Const $PKEY_Title_FMTID = "{F29F85E0-4FF9-1068-AB91-08002B27B3D9}" Global Const $PKEY_Title_PID = 2 ; AppUserModelID for our application Global Const $APPID = "AutoIt.JumpList.Example." & Random(1000, 9999, 1) ; Main GUI Global $hGUI = GUICreate("Jump List Example", 400, 300) GUICtrlCreateLabel("Right-click this application in the taskbar", 50, 120, 300, 30) GUICtrlSetFont(-1, 12) GUICtrlCreateLabel("Tasks: Notepad, Calculator, CMD", 50, 150, 300, 30) GUISetState(@SW_SHOW) ; Initialize COM DllCall("ole32.dll", "long", "CoInitialize", "ptr", 0) ; Set AppUserModelID for jump list _SetAppUserModelID() ; Create jump list _CreateJumpList() While 1 $nMsg = GUIGetMsg() Switch $nMsg Case $GUI_EVENT_CLOSE ; Uninitialize COM DllCall("ole32.dll", "none", "CoUninitialize") Exit EndSwitch WEnd Func _SetAppUserModelID() ; Set the AppUserModelID for our process Local $aRet = DllCall("shell32.dll", "long", "SetCurrentProcessExplicitAppUserModelID", "wstr", $APPID) If @error Or $aRet[0] <> 0 Then ConsoleWrite("Warning: Failed to set AppUserModelID: " & Hex($aRet[0]) & @CRLF) Return False EndIf ConsoleWrite("AppUserModelID set to: " & $APPID & @CRLF) Return True EndFunc Func _CreateJumpList() ConsoleWrite("Creating jump list..." & @CRLF) ; Create destination list COM object Local $pDestList = 0 Local $aRet = DllCall("ole32.dll", "long", "CoCreateInstance", _ "ptr", _GUIDFromString($CLSID_DestinationList), _ "ptr", 0, _ "dword", 1, _ ; CLSCTX_INPROC_SERVER "ptr", _GUIDFromString($IID_ICustomDestinationList), _ "ptr*", 0) If @error Or $aRet[0] <> 0 Then ConsoleWrite("Failed to create DestinationList: 0x" & Hex($aRet[0], 8) & @CRLF) Return False EndIf Local $pDestList = $aRet[5] ConsoleWrite("DestinationList created at: 0x" & Hex($pDestList) & @CRLF) ; Get the vtable Local $pVTable = DllStructGetData(DllStructCreate("ptr", $pDestList), 1) ; Get BeginList method (vtable index 3) Local $pBeginList = DllStructGetData(DllStructCreate("ptr", $pVTable + 3*4), 1) ; Call BeginList Local $cMaxSlots = 0 Local $pRemovedArray = 0 Local $aRet2 = DllCallAddress("long", $pBeginList, _ "ptr", $pDestList, _ "uint*", $cMaxSlots, _ "ptr", _GUIDFromString($IID_IObjectArray), _ "ptr*", 0) If @error Or $aRet2[0] <> 0 Then ConsoleWrite("BeginList failed: 0x" & Hex($aRet2[0], 8) & @CRLF) _ReleaseObject($pDestList) Return False EndIf ConsoleWrite("BeginList succeeded. Max slots: " & $cMaxSlots & @CRLF) ; Create object collection for tasks Local $pObjCollection = 0 Local $aRet3 = DllCall("ole32.dll", "long", "CoCreateInstance", _ "ptr", _GUIDFromString($CLSID_EnumerableObjectCollection), _ "ptr", 0, _ "dword", 1, _ "ptr", _GUIDFromString($IID_IObjectCollection), _ "ptr*", 0) If @error Or $aRet3[0] <> 0 Then ConsoleWrite("Failed to create ObjectCollection: 0x" & Hex($aRet3[0], 8) & @CRLF) _ReleaseObject($pDestList) Return False EndIf Local $pObjCollection = $aRet3[5] ConsoleWrite("ObjectCollection created at: 0x" & Hex($pObjCollection) & @CRLF) ; Create and add tasks Local $bSuccess = True ; Task 1: Notepad If Not _AddTaskToCollection($pObjCollection, "Open Notepad", @WindowsDir & "\notepad.exe", "", "Launch Notepad text editor", @SystemDir & "\imageres.dll", 2) Then ConsoleWrite("Failed to add Notepad task" & @CRLF) $bSuccess = False EndIf ; Task 2: Calculator If Not _AddTaskToCollection($pObjCollection, "Open Calculator", @WindowsDir & "\system32\calc.exe", "", "Launch Calculator", @SystemDir & "\shell32.dll", 24) Then ConsoleWrite("Failed to add Calculator task" & @CRLF) $bSuccess = False EndIf ; Task 3: Command Prompt If Not _AddTaskToCollection($pObjCollection, "Open CMD", @ComSpec, "/k echo Hello from Jump List", "Launch Command Prompt", @SystemDir & "\shell32.dll", 61) Then ConsoleWrite("Failed to add CMD task" & @CRLF) $bSuccess = False EndIf If Not $bSuccess Then ConsoleWrite("Some tasks failed to add, but continuing..." & @CRLF) EndIf ; Get AddUserTasks method (vtable index 6) Local $pAddUserTasks = DllStructGetData(DllStructCreate("ptr", $pVTable + 6*4), 1) ; Add tasks to jump list Local $aRet4 = DllCallAddress("long", $pAddUserTasks, _ "ptr", $pDestList, _ "ptr", $pObjCollection) If @error Or $aRet4[0] <> 0 Then ConsoleWrite("AddUserTasks failed: 0x" & Hex($aRet4[0], 8) & @CRLF) _ReleaseObject($pObjCollection) _ReleaseObject($pDestList) Return False EndIf ConsoleWrite("Tasks added to jump list" & @CRLF) ; Get CommitList method (vtable index 4) Local $pCommitList = DllStructGetData(DllStructCreate("ptr", $pVTable + 4*4), 1) ; Commit the jump list Local $aRet5 = DllCallAddress("long", $pCommitList, _ "ptr", $pDestList) If @error Or $aRet5[0] <> 0 Then ConsoleWrite("CommitList failed: 0x" & Hex($aRet5[0], 8) & @CRLF) Else ConsoleWrite("Jump list created successfully!" & @CRLF) MsgBox(64, "Success", "Jump list created!" & @CRLF & "Pin this app to taskbar and right-click to see the custom tasks.") EndIf ; Cleanup _ReleaseObject($pObjCollection) _ReleaseObject($pDestList) Return True EndFunc Func _AddTaskToCollection($pCollection, $sTitle, $sTargetPath, $sArguments, $sDescription, $sIconPath, $iIconIndex) ConsoleWrite("Adding task: " & $sTitle & @CRLF) ; Create shell link COM object Local $pShellLink = 0 Local $aRet = DllCall("ole32.dll", "long", "CoCreateInstance", _ "ptr", _GUIDFromString($CLSID_ShellLink), _ "ptr", 0, _ "dword", 1, _ "ptr", _GUIDFromString($IID_IShellLinkW), _ "ptr*", 0) If @error Or $aRet[0] <> 0 Then ConsoleWrite("Failed to create ShellLink: 0x" & Hex($aRet[0], 8) & @CRLF) Return False EndIf Local $pShellLink = $aRet[5] ConsoleWrite("ShellLink created at: 0x" & Hex($pShellLink) & @CRLF) ; Get IShellLink vtable Local $pVTableSL = DllStructGetData(DllStructCreate("ptr", $pShellLink), 1) ; Set path (vtable index 20) Local $pSetPath = DllStructGetData(DllStructCreate("ptr", $pVTableSL + 20*4), 1) Local $aRet2 = DllCallAddress("long", $pSetPath, _ "ptr", $pShellLink, _ "wstr", $sTargetPath) If @error Or $aRet2[0] <> 0 Then ConsoleWrite("SetPath failed: 0x" & Hex($aRet2[0], 8) & @CRLF) EndIf ; Set arguments if provided If $sArguments <> "" Then Local $pSetArguments = DllStructGetData(DllStructCreate("ptr", $pVTableSL + 11*4), 1) Local $aRet3 = DllCallAddress("long", $pSetArguments, _ "ptr", $pShellLink, _ "wstr", $sArguments) If @error Or $aRet3[0] <> 0 Then ConsoleWrite("SetArguments failed: 0x" & Hex($aRet3[0], 8) & @CRLF) EndIf EndIf ; Set description Local $pSetDescription = DllStructGetData(DllStructCreate("ptr", $pVTableSL + 7*4), 1) Local $aRet4 = DllCallAddress("long", $pSetDescription, _ "ptr", $pShellLink, _ "wstr", $sDescription) If @error Or $aRet4[0] <> 0 Then ConsoleWrite("SetDescription failed: 0x" & Hex($aRet4[0], 8) & @CRLF) EndIf ; Set icon if provided If $sIconPath <> "" And FileExists($sIconPath) Then Local $pSetIconLocation = DllStructGetData(DllStructCreate("ptr", $pVTableSL + 17*4), 1) Local $aRet5 = DllCallAddress("long", $pSetIconLocation, _ "ptr", $pShellLink, _ "wstr", $sIconPath, _ "int", $iIconIndex) If @error Or $aRet5[0] <> 0 Then ConsoleWrite("SetIconLocation failed: 0x" & Hex($aRet5[0], 8) & @CRLF) EndIf Else ConsoleWrite("Icon path not found: " & $sIconPath & @CRLF) EndIf ; Try to set title using property store (optional) _SetShellLinkTitle($pShellLink, $sTitle) ; Add object to collection Local $pVTableOC = DllStructGetData(DllStructCreate("ptr", $pCollection), 1) Local $pAddObject = DllStructGetData(DllStructCreate("ptr", $pVTableOC + 6*4), 1) Local $aRet8 = DllCallAddress("long", $pAddObject, _ "ptr", $pCollection, _ "ptr", $pShellLink) _ReleaseObject($pShellLink) If @error Or $aRet8[0] <> 0 Then ConsoleWrite("AddObject failed: 0x" & Hex($aRet8[0], 8) & @CRLF) Return False EndIf ConsoleWrite("Task added successfully: " & $sTitle & @CRLF) Return True EndFunc Func _SetShellLinkTitle($pShellLink, $sTitle) ; Try to set the title property (optional - shell link will still work without it) Local $pPropertyStore = 0 Local $pVTableSL = DllStructGetData(DllStructCreate("ptr", $pShellLink), 1) ; QueryInterface for IPropertyStore (vtable index 0) Local $pQueryInterface = DllStructGetData(DllStructCreate("ptr", $pVTableSL), 1) Local $aRet = DllCallAddress("long", $pQueryInterface, _ "ptr", $pShellLink, _ "ptr", _GUIDFromString($IID_IPropertyStore), _ "ptr*", 0) If @error Or $aRet[0] <> 0 Then ; ConsoleWrite("QueryInterface for IPropertyStore failed: 0x" & Hex($aRet[0], 8) & @CRLF) Return False EndIf Local $pPropertyStore = $aRet[3] Local $pVTablePS = DllStructGetData(DllStructCreate("ptr", $pPropertyStore), 1) ; Create property key Local $tPropKey = _CreatePROPERTYKEY($PKEY_Title_FMTID, $PKEY_Title_PID) ; Create PROPVARIANT for title Local $tTitleWStr = DllStructCreate("wchar[" & StringLen($sTitle) + 1 & "]") DllStructSetData($tTitleWStr, 1, $sTitle) Local $tPropVar = DllStructCreate("word vt; word wReserved1; word wReserved2; word wReserved3; ptr pValue") DllStructSetData($tPropVar, "vt", 31) ; VT_LPWSTR DllStructSetData($tPropVar, "pValue", DllStructGetPtr($tTitleWStr)) ; SetValue method (vtable index 5) Local $pSetValue = DllStructGetData(DllStructCreate("ptr", $pVTablePS + 5*4), 1) Local $aRet2 = DllCallAddress("long", $pSetValue, _ "ptr", $pPropertyStore, _ "ptr", DllStructGetPtr($tPropKey), _ "ptr", DllStructGetPtr($tPropVar)) ; Commit (vtable index 7) If Not @error And $aRet2[0] = 0 Then Local $pCommit = DllStructGetData(DllStructCreate("ptr", $pVTablePS + 7*4), 1) DllCallAddress("long", $pCommit, "ptr", $pPropertyStore) EndIf _ReleaseObject($pPropertyStore) Return True EndFunc Func _CreatePROPERTYKEY($sFmtID, $iPID) Local $tGUID = _GUIDFromString($sFmtID) Local $tPropKey = DllStructCreate("byte[16]; dword PID") ; Copy GUID to structure DllCall("kernel32.dll", "none", "RtlMoveMemory", _ "ptr", DllStructGetPtr($tPropKey), _ "ptr", DllStructGetPtr($tGUID), _ "dword", 16) DllStructSetData($tPropKey, "PID", $iPID) Return $tPropKey EndFunc Func _GUIDFromString($sGUID) Local $tGUID = DllStructCreate("dword Data1; word Data2; word Data3; byte Data4[8]") DllCall("ole32.dll", "long", "CLSIDFromString", "wstr", $sGUID, "ptr", DllStructGetPtr($tGUID)) Return $tGUID EndFunc Func _ReleaseObject($pObject) If $pObject Then Local $pVTable = DllStructGetData(DllStructCreate("ptr", $pObject), 1) If $pVTable Then ; Release is 3rd vtable entry (index 2) Local $pRelease = DllStructGetData(DllStructCreate("ptr", $pVTable + 2*4), 1) Local $aRet = DllCallAddress("dword", $pRelease, "ptr", $pObject) Return $aRet[0] EndIf EndIf Return 0 EndFunc
Nine Posted Saturday at 02:42 AM Posted Saturday at 02:42 AM (edited) First the IID of ICustomDestinationList should be "{6332debf-87b5-4670-90c0-5e57b408a49e}". Using ObjCreateInterface I was able to successfully create the object. It should be easier to manage than with "CoCreateInstance". And certainly more readable. But if you insist going that path there is a number of problems with your first call, it should be something like this : Local $tGUID1 = _GUIDFromString($CLSID_DestinationList) Local $tGUID2 = _GUIDFromString($IID_ICustomDestinationList) Local $aRet = DllCall("ole32.dll", "long", "CoCreateInstance", _ "struct*", $tGUID1, _ "ptr", 0, _ "dword", 1, _ ; CLSCTX_INPROC_SERVER "struct*", $tGUID2, _ "ptr*", 0) I have a correctly returned pointer this way. That should be enough to get you started. But I strongly recommend that you go with ObjCreateInterface... edit : where did you get that code ? Is this coming from some AI ? Edited Saturday at 02:56 AM by Nine “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Debug Messages Monitor UDF Screen Scraping Round Corner GUI UDF Multi-Threading Made Easy Interface Object based on Tag
FadeSoft Posted Saturday at 04:36 AM Author Posted Saturday at 04:36 AM Thank you, Yes, I used AI as a base because I had no clue where to start and it was much more complicated than anticipated so figured I would post what I could here. I really don't have a preference of how I get the end result, just can't wrap my head around it. My fear is that using my code it could break (if it worked) if distributed killing the main function of my program.
argumentum Posted Saturday at 04:59 AM Posted Saturday at 04:59 AM Auto hotkey have a v1 and a v2 of their versions that may give you a heads up ? ;~ https://github.com/gedoor/ahkLauncher/blob/main/lib/ICustomDestinationList.ahk ;~ https://github.com/Ixiko/AHK-libs-and-classes-collection/blob/master/libs/g-n/JumpList.ahk 21 minutes ago, FadeSoft said: just can't wrap my head around it. Neither can I. But I'll wait right here for working code Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting.
Nine Posted Saturday at 02:49 PM Posted Saturday at 02:49 PM 9 hours ago, FadeSoft said: I used AI as a base because I had no clue where to start and it was much more complicated than anticipated Ok fair enough. After taking a better look at the code, it stands like AI translated it from some other language to AutoIt. The functionality seems alright although there are numerous errors in GUIDs, vtable numbers and DllCall parameters. Not sure you will end up with a working script if you are to correct all the errors, as I have no experience with those particular interfaces. I still believe that going with ObjCreateInterface would be a much more elegant solution, but at this stage, it would require quite a lot of work without knowing the outcome. For now, if you want to take a shot at it, you should stay with "CoCreateInstance" and DllCall by address, correct the errors one after the other, until it is working as expected, then you could convert it to ObjCreateInterface. Good luck. “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Debug Messages Monitor UDF Screen Scraping Round Corner GUI UDF Multi-Threading Made Easy Interface Object based on Tag
MattyD Posted Saturday at 04:45 PM Posted Saturday at 04:45 PM yep @Nine is correct, this is all over the shop. I started to fix a few things, and got down to here. But doesn't get any better.. There's about 3 or 4 issues in this one call. ; Get BeginList method (vtable index 3) ---Nope, it's 5. first is at $pVTable so offset = 4 * ptrSize Local $pBeginList = DllStructGetData(DllStructCreate("ptr", $pVTable + 4*4), 1) ; 4*4 only worx for x86 ; Call BeginList Local $cMaxSlots = 0 Local $pRemovedArray = 0 Local $aRet2 = DllCallAddress("long", $pBeginList, _ "ptr", $pDestList, _ "uint*", 0, _ ; $cMaxSlots. This is an output param. should be 0 for DllCall. check $aRet[2] for Value "struct*", _WinAPI_GUIDFromString($IID_IObjectArray), _ ;GUIDFromString returns a struct. So use "struct*" not "ptr". "ptr*", 0) This should help with the vtable order if you want to start on ObjCreateInterface... FWIW the other GUIDs looked like they should be correct, based on some other dude's code on the interweb - so that's probably a good starting point too. Maybe try wrapping a couple more funcs here and see how you get on - at least with understanding the process. Shout out if you run into trouble #include <WinAPI.au3> Global Const $CLSID_DestinationList = "{77f10cf0-3db5-4966-b520-b7c54fd35ed6}" Global Const $IID_ICustomDestinationList = "{6332debf-87b5-4670-90c0-5e57b408a49e}" Global Const $IID_IObjectArray = "{92ca9dcd-5622-4bba-a805-5e9f541bd8c9}" Local $tagICustomDestinationList = "" & _ "SetAppID hresult(wstr);" & _ "BeginList hresult(uint*; struct*; ptr*);" ;~ HRESULT SetAppID( ;~ [in] LPCWSTR pszAppID ;~ ); ;~ HRESULT BeginList( ;~ [out] UINT *pcMinSlots, ;~ [in] REFIID riid, ;~ [out] void **ppv ;~ ); Local $hResult, $cMinSlots, $pObjArr $oDestList = ObjCreateInterface($CLSID_DestinationList, $IID_ICustomDestinationList, $tagICustomDestinationList) $hResult = $oDestList.BeginList($cMinSlots, _WinAPI_GUIDFromString($IID_IObjectArray), $pObjArr) ConsoleWrite("Slots: " & $cMinSlots & @CRLF) ConsoleWrite("Object Arr: " & $pObjArr & @CRLF) argumentum 1
argumentum Posted Saturday at 05:30 PM Posted Saturday at 05:30 PM (edited) 1 hour ago, MattyD said: $tagICustomDestinationList ... Local $tagICustomDestinationList = _ "SetAppID hresult(wstr);" & _ "BeginList hresult(uint*;clsid;ptr*);" & _ "AppendCategory hresult(wstr;ptr);" & _ "AppendKnownCategory hresult(int);" & _ "AddUserTasks hresult(ptr);" & _ "CommitList hresult();" & _ "AbortList hresult();" & _ "DeleteList hresult(wstr);" & _ "GetRemovedDestinations hresult(clsid;ptr*);" ... hope this is good. I hope it is correct close to it. 😅 Edited Saturday at 06:09 PM by argumentum oops Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting.
FadeSoft Posted Saturday at 09:35 PM Author Posted Saturday at 09:35 PM I appreciate everyone's help so far, I am definitely going to have to sit down and rethink this. If AHK is capable of making it work, than Autoit should also be able to? Maybe i'm missing something but Windows didn't do a great job at making this accessible for everyone or is it more a language limitation if not just a user error (me)
Solution Nine Posted Saturday at 10:23 PM Solution Posted Saturday at 10:23 PM (edited) Here my latest attempt. And again my curiosity got over me. expandcollapse popup; From Nine : JumpList (tasks) ; https://www.autoitscript.com/forum/topic/213392-windows-11-jump-list-tasks/#findComment-1548652 #include <GUIConstants.au3> #include <WinAPI.au3> Opt("MustDeclareVars", True) #Region Global Const Global Const $CLSID_DestinationList = "{77f10cf0-3db5-4966-b520-b7c54fd35ed6}" Global Const $IID_ICustomDestinationList = "{6332debf-87b5-4670-90c0-5e57b408a49e}" Global Const $tag_ICustomDestinationList = _ "SetAppID hresult(wstr);" & _ "BeginList hresult(uint*;clsid;ptr*);" & _ "AppendCategory hresult(wstr;ptr*);" & _ "AppendKnownCategory hresult(int);" & _ "AddUserTasks hresult(ptr);" & _ "CommitList hresult();" & _ "GetRemovedDestinations hresult(struct*;ptr*);" & _ "DeleteList hresult(wstr);" & _ "AbortList hresult();" Global Const $IID_IObjectArray = "{92ca9dcd-5622-4bba-a805-5e9f541bd8c9}" Global Const $IID_IPropertyStore = "{886d8eeb-8cf2-4446-8d02-cdba1dbdcf99}" Global Const $tagIPropertyStore = _ "GetCount hresult(dword*);" & _ "GetAt hresult(dword;ptr*);" & _ "GetValue hresult(ptr;variant*);" & _ "SetValue hresult(ptr;ptr);" & _ "Commit hresult();" Global Const $FMTID_SummaryInformation = "{F29F85E0-4FF9-1068-AB91-08002B27B3D9}" Global Const $PIDSI_TITLE = 2 Global Const $VT_LPWSTR = 31 Global Const $CLSID_EnumerableObjectCollection = "{2d3468c1-36a7-43b6-ac24-d3f02fd9607a}" Global Const $IID_IObjectCollection = "{5632b1a4-e38a-400a-928a-d4cd63230295}" Global Const $tag_IObjectCollection = _ "GetCount hresult(uint*);" & _ "GetAt hresult(uint;struct*;ptr*);" & _ "AddObject hresult(ptr);" & _ "AddFromArray hresult(ptr*);" & _ "RemoveObjectAt hresult(uint);" & _ "Clear hresult();" Global Const $CLSID_ShellLink = "{00021401-0000-0000-C000-000000000046}" Global Const $IID_IShellLink = "{000214F9-0000-0000-C000-000000000046}" Global Const $tag_IShellLinkW = _ "GetPath hresult(wstr;int;ptr*;dword);" & _ "GetIDList hresult(ptr*);" & _ "SetIDList hresult(ptr*);" & _ "GetDescription hresult(ptr;int);" & _ "SetDescription hresult(wstr);" & _ "GetWorkingDirectory hresult(otr;int);" & _ "SetWorkingDirectory hresult(wstr);" & _ "GetArguments hresult(ptr;int);" & _ "SetArguments hresult(wstr);" & _ "GetHotkey hresult(word*);" & _ "SetHotkey hresult(word);" & _ "GetShowCmd hresult(int*);" & _ "SetShowCmd hresult(int);" & _ "GetIconLocation hresult(wstr;int;int*);" & _ "SetIconLocation hresult(wstr;int);" & _ "SetRelativePath hresult(wstr;dword);" & _ "Resolve hresult(hwnd;dword);" & _ "SetPath hresult(wstr);" #EndRegion Example() Func Example() Local $hGUI = GUICreate("Jump List Example", 400, 300) _WinAPI_SetCurrentProcessExplicitAppUserModelID("Jump List Example") GUICtrlCreateLabel("Right-click this application in the taskbar", 50, 120, 300, 30) GUICtrlSetFont(-1, 12) GUICtrlCreateLabel("Tasks: Notepad", 50, 150, 300, 30) GUISetState(@SW_SHOW) Local $oICustDestList = ObjCreateInterface($CLSID_DestinationList, $IID_ICustomDestinationList, $tag_ICustomDestinationList) ConsoleWrite("$oICustDestList : " & IsObj($oICustDestList) & @CRLF) Local $pList, $iMin = 1 $oICustDestList.BeginList($iMin, $IID_IObjectArray, $pList) ConsoleWrite("$pList : " & $pList & @CRLF) Local $oIObjectCollection = ObjCreateInterface($CLSID_EnumerableObjectCollection, $IID_IObjectCollection, $tag_IObjectCollection) ConsoleWrite("$oIObjectCollection : " & IsObj($oIObjectCollection) & @CRLF) AddTaskToCollection($oIObjectCollection, "Notepad", @WindowsDir & "\notepad.exe", "", "Launch Notepad text editor", @SystemDir & "\imageres.dll", 19) Local $iCount $oIObjectCollection.GetCount($iCount) ConsoleWrite("returned " & $iCount & @CRLF) $oICustDestList.AddUserTasks($oIObjectCollection) $oICustDestList.CommitList() While True Switch GUIGetMsg() Case $GUI_EVENT_CLOSE ExitLoop EndSwitch WEnd EndFunc ;==>Example Func AddTaskToCollection(ByRef $oColl, $sTitle, $sPath, $sArg, $sDesc, $sIcon, $iIcon) Local $oShellLink = ObjCreateInterface($CLSID_ShellLink, $IID_IShellLink, $tag_IShellLinkW) ConsoleWrite("$oShellLink : " & IsObj($oShellLink) & @CRLF) $oShellLink.SetPath($sPath) If $sArg Then $oShellLink.SetArguments($sArg) $oShellLink.SetDescription($sDesc) $oShellLink.SetIconLocation($sIcon, $iIcon) Local $tText = DllStructCreate("wchar string[256]") $oShellLink.GetDescription(DllStructGetPtr($tText), 255) ConsoleWrite("Description : " & $tText.string & @CRLF) Local $iCount $oColl.GetCount($iCount) ConsoleWrite("before : " & $iCount & @CRLF) $oColl.AddObject($oShellLink) $oColl.GetCount($iCount) ConsoleWrite("after : " & $iCount & @CRLF) SetShellLinkTitle($oShellLink, $sTitle) EndFunc ;==>AddTaskToCollection Func SetShellLinkTitle(ByRef $oShell, $sTitle) Local $pStore $oShell.QueryInterface($IID_IPropertyStore, $pStore) ConsoleWrite("$pStore : " & $pStore & @CRLF) Local $oIPropertyStore = ObjCreateInterface($pStore, $IID_IPropertyStore, $tagIPropertyStore) ConsoleWrite("$oIPropertyStore : " & IsObj($oIPropertyStore) & @CRLF) Local $tPropKey = _WinAPI_PKEYFromString($FMTID_SummaryInformation, $PIDSI_TITLE) ; Create PROPVARIANT for title Local $tTitleWStr = DllStructCreate("wchar string[" & StringLen($sTitle) + 1 & "]") $tTitleWStr.string = $sTitle Local $tPropVar = DllStructCreate("word vt; word wReserved1; word wReserved2; word wReserved3; ptr pValue") $tPropVar.vt = $VT_LPWSTR $tPropVar.pValue = DllStructGetPtr($tTitleWStr) $oIPropertyStore.SetValue(DllStructGetPtr($tPropKey), DllStructGetPtr($tPropVar)) Local $sText $oIPropertyStore.GetValue(DllStructGetPtr($tPropKey), $sText) ConsoleWrite("Title : " & $sText & @CRLF) EndFunc ;==>SetShellLinkTitle Func _WinAPI_PKEYFromString($sPKEY, $iPID) Local $tPKey = DllStructCreate("byte GUID[16]; dword PID;") DllCall("propsys.dll", "long", "PSPropertyKeyFromString", "wstr", $sPKEY, "struct*", $tPKey) $tPKey.PID = $iPID Return $tPKey EndFunc ;==>_WinAPI_PKEYFromString Code updated. Should now work as expected. ps. Just noticed that some of the tags I made did not end each method with ; Funny that it is working without it. Anyway added it. Edited Monday at 07:00 PM by Nine Danyfirex, FadeSoft, argumentum and 1 other 3 1 “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Debug Messages Monitor UDF Screen Scraping Round Corner GUI UDF Multi-Threading Made Easy Interface Object based on Tag
FadeSoft Posted Saturday at 11:33 PM Author Posted Saturday at 11:33 PM Incredible job! 😁 Excellent base to learn and work from, you should consider sharing it in the example forums! As I don't want to take credit for your work but I don't think this has been done before at least anything I could find related to Autoit. This would add a lot to already existing Autoit programs for sure! Thanks again 🙂 Nine 1
Nine Posted Sunday at 03:39 PM Posted Sunday at 03:39 PM Made some modifications. Cleaned-up the mess, but kept the CW as error handling. Now working correctly. Previous code updated. MattyD, ioa747 and argumentum 2 1 “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Debug Messages Monitor UDF Screen Scraping Round Corner GUI UDF Multi-Threading Made Easy Interface Object based on Tag
MattyD Posted Sunday at 05:54 PM Posted Sunday at 05:54 PM I was having a look at the example, and got a bit sidetracked with a thought.. (apologies if its a bit too far off topic) Take the line: $oIObjectCollection.QueryInterface($IID_IObjectArray, $pArray). A successful QueryInterface performs an addRef on the object, so we now have 2 references... One for $oIObjectCollection on IObjectCollection, and another for $pArray on IObjectArray. Now when an autoit variable of type "object" goes out of scope, I believe Autoit will release a reference to that object. But, does it only call IUnknown::Release() once at that point?? I would think that'd be logical because we could have references to it elsewhere... My understanding is that once the refcount reaches 0, the system automatically frees the obj - this is not something AutoIt explicitly does. It's why there's no reciprocal cleanup func for the cocreateinstance system call... Assuming all that is true, then I guess when Example() exits we probably get stuck with a rogue object in memory...That's because AutoIt thinks $pArray just a random ptr. So when that's invalidated, its reference is never released. Well that's the theory. If the scenario is correct, then we should really do a $oIObjectCollection.Release() once at some point after calling QI. I'll have a play at some point tomorrow time permitting! argumentum 1
argumentum Posted Sunday at 06:06 PM Posted Sunday at 06:06 PM 11 minutes ago, MattyD said: then we should really do a $oIObjectCollection.Release() While True Switch GUIGetMsg() Case $GUI_EVENT_CLOSE ExitLoop EndSwitch WEnd $oIObjectCollection.GetCount($iCount) ConsoleWrite("returned " & $iCount & @CRLF) $oIObjectCollection.Release() $oIObjectCollection.GetCount($iCount) ConsoleWrite("returned " & $iCount & @CRLF) I don't think that does it. 11 minutes ago, MattyD said: I'll have a play at some point tomorrow time permitting! Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting.
FadeSoft Posted Sunday at 07:24 PM Author Posted Sunday at 07:24 PM expandcollapse popup#include <MsgBoxConstants.au3> If $CmdLine[0] > 0 Then Switch $CmdLine[1] Case "/func1" Function1() Exit Case "/func2" Function2() Exit Case "/add", "-a" If $CmdLine[0] < 3 Then MsgBox($MB_OK, "Error", "/add requires two numbers" & @CRLF & "Usage: " & @ScriptName & " /add <num1> <num2>") Exit EndIf AddNumbers($CmdLine[2], $CmdLine[3]) Exit Case "/help", "-h", "-?" ShowHelp() Exit EndSwitch EndIf Func Function1() MsgBox($MB_OK, "Function1", "Function1 executed!") EndFunc Func Function2() MsgBox($MB_OK, "Function2", "Function2 executed!") EndFunc Func AddNumbers($num1, $num2) If Not StringIsDigit($num1) Or Not StringIsDigit($num2) Then MsgBox($MB_OK, "Error", "Both parameters must be numbers!") Exit EndIf Local $result = Number($num1) + Number($num2) MsgBox($MB_OK, "Adding", $num1 & " + " & $num2 & " = " & $result) EndFunc Func ShowHelp() MsgBox($MB_OK, "Help", "/func1" & @CRLF & "/func2" & @CRLF & "/help /-h /-?") EndFunc Can call commands from the task bar
Nine Posted Sunday at 09:16 PM Posted Sunday at 09:16 PM @MattyD Yes I thought of it and did some tests. At this stage I have no reason to believe that there is memory leak after the application is closed. Doing repeating run/close does not increment memory usage nor increase handles number. My thought was that C++ garbage collector (AutoIt3.exe) is taking care of it. But I could be wrong on this one. However, if there is memory leak, it is very very minimal. Not observable from task manager... “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Debug Messages Monitor UDF Screen Scraping Round Corner GUI UDF Multi-Threading Made Easy Interface Object based on Tag
MattyD Posted Sunday at 09:18 PM Posted Sunday at 09:18 PM 2 hours ago, argumentum said: While True Switch GUIGetMsg() Case $GUI_EVENT_CLOSE ExitLoop EndSwitch WEnd $oIObjectCollection.GetCount($iCount) ConsoleWrite("returned " & $iCount & @CRLF) $oIObjectCollection.Release() $oIObjectCollection.GetCount($iCount) ConsoleWrite("returned " & $iCount & @CRLF) I don't think that does it. GetCount is a count on the objects in the collection, which is a different thing to the refcount used in IUnknown , These funcs will allow us to interact with the object via the $pArray param. Func _AddRef($pThis) Local $pVTab = DllStructGetData(DllStructCreate("ptr", $pThis), 1) Local $pFunc = DllStructGetData(DllStructCreate("ptr", $pVTab + (@AutoItX64 ? 8 : 4)), 1) Local $aCall = DllCallAddress("uint", $pFunc, "ptr", $pThis) Return $aCall[0] EndFunc Func _Release($pThis) Local $pVTab = DllStructGetData(DllStructCreate("ptr", $pThis), 1) Local $pFunc = DllStructGetData(DllStructCreate("ptr", $pVTab + 2 * (@AutoItX64 ? 8 : 4)), 1) Local $aCall = DllCallAddress("uint", $pFunc, "ptr", $pThis) Return $aCall[0] EndFunc And if you "Return String($pArray)" from Example()... I'm using String() to ensure $pArray as a variable is totally invalidated - so it isn't somehow preserved/copied etc, by being Returned. Local $pObj Ptr(Example()) ConsoleWrite(_AddRef($pObj) & @CRLF) ConsoleWrite(_AddRef($pObj) & @CRLF) ConsoleWrite(_AddRef($pObj) & @CRLF) And you get: 2 3 4 So yep - it seems the object sticks around and has a refcount of 1 after Example() exits. argumentum 1
argumentum Posted Sunday at 10:07 PM Posted Sunday at 10:07 PM Spoiler AI Overview In .NET applications (specifically WPF using the System.Windows.Shell namespace), the JumpList object does not need to be explicitly released or disposed of with a using statement or similar mechanism in C# or Visual Basic. The JumpList class manages the items in a Windows application's Jump List, which is a feature of the Windows Shell. Key Points on the JumpList Object Lifecycle Automatic Management: When you create a JumpList and associate it with the current Application object (either in XAML or by calling the static JumpList.SetJumpList method), the Windows Shell takes ownership of displaying the items. Shell Handles Persistence: The operating system handles the storage and persistence of the actual Jump List data in specific system directories (%APPDATA% related folders). Your application provides the instructions for what should be in the list, but Windows manages the data file and its lifecycle on the system. No Unmanaged Resources: The JumpList class itself is a managed .NET object and does not hold significant unmanaged resources that require manual cleanup. The .NET garbage collector will reclaim the memory when the object is no longer referenced. Updating the List: To modify or clear the list, you interact with the JumpItems collection and then call the Apply method, or call SetJumpList with a new object. This sends the updated instructions to the Windows Shell, which then updates the persistent data. In summary, the developer does not need to worry about manually "releasing" the JumpList object itself; the system manages the underlying data structure and file persistence. AI say that it does not need releasing/clean up, because the shell/OS takes care of it. Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting.
Nine Posted Sunday at 10:48 PM Posted Sunday at 10:48 PM My comment here is not theoretical, just empirical. So don't bother with pseudo-leak of memory. Just enjoy the code as it is... MattyD 1 “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Debug Messages Monitor UDF Screen Scraping Round Corner GUI UDF Multi-Threading Made Easy Interface Object based on Tag
jugador Posted Monday at 05:27 PM Posted Monday at 05:27 PM I haven't tested the code, so this is just a guess The reference count increases because of $oIObjectCollection.QueryInterface($IID_IObjectArray, $pArray) However, is that line necessary? I believe BeginList already returns an IObjectArray($pList) $oICustDestList.BeginList($iMin, $IID_IObjectArray, $pList)
Nine Posted Monday at 06:04 PM Posted Monday at 06:04 PM 32 minutes ago, jugador said: However, is that line necessary? I believe BeginList already returns an IObjectArray($pList) Yes and no. Using $pList does not work, it must come from $oIObjectCollection. However, you made me think to use the object itself instead of a IObjectArray *poa as described in MSDN. And it works. Thanks you for that. Code updated. argumentum 1 “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Debug Messages Monitor UDF Screen Scraping Round Corner GUI UDF Multi-Threading Made Easy Interface Object based on Tag
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