Jump to content

Recommended Posts

Posted

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.

 

#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

 

 

Screenshot 2026-01-02 200848.png

Posted (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 by Nine

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...