Skip to content

Instantly share code, notes, and snippets.

@yajd
Last active August 29, 2015 14:03
Show Gist options
  • Save yajd/862832b07a1d46caef8b to your computer and use it in GitHub Desktop.
Save yajd/862832b07a1d46caef8b to your computer and use it in GitHub Desktop.
_ff-addon-snippet-RelaunchCommand - Working on Relaunch properties of Win7.
Cu.import('resource://gre/modules/ctypes.jsm');
var shell32 = ctypes.open('shell32.dll');
var ole32 = ctypes.open('Ole32.dll');
var shlwapi = ctypes.open('Shlwapi.dll');
/* http://msdn.microsoft.com/en-us/library/ff718266.aspx
* typedef struct {
* unsigned long Data1;
* unsigned short Data2;
* unsigned short Data3;
* byte Data4[8];
* } GUID, UUID, *PGUID;
*/
var struct_GUID = ctypes.StructType('GUID', [
{'Data1': ctypes.unsigned_long},
{'Data2': ctypes.unsigned_short},
{'Data3': ctypes.unsigned_short},
{'Data4': ctypes.char.array(8)}
]);
/* http://msdn.microsoft.com/en-us/library/cc237652.aspx
* typedef GUID IID;
*/
var IID = struct_GUID;
/* http://msdn.microsoft.com/en-us/library/cc237816.aspx
* typedef IID* REFIID;
*/
var REFIID = new ctypes.PointerType(IID);
/* http://msdn.microsoft.com/en-us/library/windows/desktop/ms680589%28v=vs.85%29.aspx
* HRESULT CLSIDFromString(
* __in_ LPCOLESTR lpsz,
* __out_ LPCLSID pclsid
);
*/
var CLSIDFromString = ole32.declare('CLSIDFromString', ctypes.winapi_abi, ctypes.long, // HRESULT
new ctypes.PointerType(ctypes.jschar), // LPCOLESTR
struct_GUID.ptr // LPCLSID
);
/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd378430%28v=vs.85%29.aspx
* HRESULT SHGetPropertyStoreForWindow(
* __in_ HWND hwnd,
* __in_ REFIID riid,
* __out_ void **ppv
* );
*/
var SHGetPropertyStoreForWindow = shell32.declare('SHGetPropertyStoreForWindow', ctypes.winapi_abi, ctypes.long, // HRESULT
ctypes.voidptr_t, // HWND
REFIID, // REFIID
ctypes.voidptr_t // VOID
);
/* http://msdn.microsoft.com/en-us/library/windows/desktop/bb773381%28v=vs.85%29.aspx
* typedef struct {
* GUID fmtid;
* DWORD pid;
* } PROPERTYKEY;
*/
var struct_PROPERTYKEY = ctypes.StructType('PROPERTYKEY', [ // this is also known as PKEY i learned from seeing this: https://github.com/truonghinh/TnX/blob/260a8a623751ffbce14bad6018ea48febbc21bc6/TnX-v8/Microsoft.Windows.Shell/Standard/ShellProvider.cs#L345
{'fmtid': struct_GUID}, // GUID
{'pid': ctypes.unsigned_long} // DWORD
]);
/* http://msdn.microsoft.com/en-us/library/windows/desktop/bb773381%28v=vs.85%29.aspx
* typedef struct PROPVARIANT {
* VARTYPE vt;
* WORD wReserved1;
* WORD wReserved2;
* WORD wReserved3;
* union {
* CHAR cVal;
* UCHAR bVal;
* SHORT iVal;
* USHORT uiVal;
* LONG lVal;
* ULONG ulVal;
* INT intVal;
* UINT uintVal;
* LARGE_INTEGER hVal;
* ULARGE_INTEGER uhVal;
* FLOAT fltVal;
* DOUBLE dblVal;
* VARIANT_BOOL boolVal;
* SCODE scode;
* CY cyVal;
* DATE date;
* FILETIME filetime;
* CLSID *puuid;
* CLIPDATA *pclipdata;
* BSTR bstrVal;
* BSTRBLOB bstrblobVal;
* BLOB blob;
* LPSTR pszVal;
* LPWSTR pwszVal;
* IUnknown *punkVal;
* IDispatch *pdispVal;
* IStream *pStream;
* IStorage *pStorage;
* LPVERSIONEDSTREAM pVersionedStream;
* LPSAFEARRAY parray;
* CAC cac;
* CAUB caub;
* CAI cai;
* CAUI caui;
* CAL cal;
* CAUL caul;
* CAH cah;
* CAUH cauh;
* CAFLT caflt;
* CADBL cadbl;
* CABOOL cabool;
* CASCODE cascode;
* CACY cacy;
* CADATE cadate;
* CAFILETIME cafiletime;
* CACLSID cauuid;
* CACLIPDATA caclipdata;
* CABSTR cabstr;
* CABSTRBLOB cabstrblob;
* CALPSTR calpstr;
* CALPWSTR calpwstr;
* CAPROPVARIANT capropvar;
* CHAR *pcVal;
* UCHAR *pbVal;
* SHORT *piVal;
* USHORT *puiVal;
* LONG *plVal;
* ULONG *pulVal;
* INT *pintVal;
* UINT *puintVal;
* FLOAT *pfltVal;
* DOUBLE *pdblVal;
* VARIANT_BOOL *pboolVal;
* DECIMAL *pdecVal;
* SCODE *pscode;
* CY *pcyVal;
* DATE *pdate;
* BSTR *pbstrVal;
* IUnknown **ppunkVal;
* IDispatch **ppdispVal;
* LPSAFEARRAY *pparray;
* PROPVARIANT *pvarVal;
* };
* } PROPVARIANT;
*/
var struct_PROPVARIANT = ctypes.StructType('PROPVARIANT', [
{'fntud': struct_GUID}, // GUID
{'pid': ctypes.unsigned_long}, // DWORD
// union not supported by js-ctypes
// https://bugzilla.mozilla.org/show_bug.cgi?id=535378 "You can always
// typecast pointers, at least as long as you know which type is the biggest"
{'pwszVal': new ctypes.PointerType(ctypes.jschar)} // LPWSTR
]);
/* http://msdn.microsoft.com/en-us/library/windows/desktop/bb762305%28v=vs.85%29.aspx
* REMARKS: This is an inline function, with its source code provided in the header. It is not included in any .dll or .lib file.
* HRESULT InitPropVariantFromString(
* __in_ PCWSTR psz,
* __out_ PROPVARIANT *ppropvar
* );
SOURCE2: https://github.com/dreamsxin/qutIM/blob/f4c4e4da3f82f14d82cfd42a16f31219c9e5cacb/plugins/docktile/qtdocktile/src/plugins/windows/wrapper/_winapi.h#L17
inline HRESULT InitPropVariantFromString(PCWSTR string, PROPVARIANT *propvar)
{
propvar->vt = VT_LPWSTR;
HRESULT hr = SHStrDupW(string, &propvar->pwszVal);
if (FAILED(hr)) {
WinApi::PropVariantInit(propvar);
}
return hr;
}
*/
/*
* var InitPropVariantFromString = propsys.declare('InitPropVariantFromString', ctypes.winapi_abi, ctypes.long, // HRESULT
* ctypes.jschar.ptr, // PCWSTR
* struct_PROPVARIANT.ptr // PROPVARIANT
* );
*/
function InitPropVariantFromString(string /** PCWSTR **/, propvarPtr /** PROPVARIANT pointer **/) {
//console.log('propvarPtr.contents.pwszVal', propvarPtr.contents.pwszVal, propvarPtr.contents.pwszVal.toSource(), uneval(propvarPtr.contents.pwszVal));
//console.log('propvarPtr', propvarPtr);
console.log('propvarPtr.contents.pwszVal', propvarPtr.contents.pwszVal);
console.log('propvarPtr.contents.pwszVal.address()', propvarPtr.contents.pwszVal.address());
var hr = SHStrDup(string, propvarPtr.contents.pwszVal.address());
console.error('hr = ', hr, hr.toSource(), uneval(hr), hr.toString());
console.log('propvarPtr.contents.pwszVal', propvarPtr.contents.pwszVal);
if (!checkHRESULT(hr)) {
console.log('having to PropVariantInit');
PropVariantInit(propvarPtr);
} else {
console.info('YAHOO SUCESSS');
}
return true;
}
/* http://msdn.microsoft.com/en-us/library/windows/desktop/aa380293%28v=vs.85%29.aspx
* REMARKS: Probably no dll for this one...
* void PropVariantInit(
* __out_ PROPVARIANT *pvar
* );
SOURCE2: https://github.com/dreamsxin/qutIM/blob/f4c4e4da3f82f14d82cfd42a16f31219c9e5cacb/plugins/docktile/qtdocktile/src/plugins/windows/wrapper/_winapi.h#L12
void PropVariantInit(PROPVARIANT *variant)
{
memset(variant, 0, sizeof(PROPVARIANT));
}
*/
var PropVariantInit = function(variantPointer) {
//memset(variantPointer, 0, variantPointer.size); //no need for this in js-ctypes because things always initialized at 0
return true;
}
/* http://msdn.microsoft.com/en-us/library/windows/desktop/bb759924%28v=vs.85%29.aspx
* HRESULT SHStrDup(
* __in_ LPCTSTR pszSource,
* __out_ LPTSTR *ppwsz
* );
*/
var SHStrDup = shlwapi.declare('SHStrDupW', ctypes.winapi_abi, ctypes.long, // HRESULT
ctypes.voidptr_t, // LPCTSTR // can possibly also make this ctypes.char.ptr // im trying to pass PCWSTR here, i am making it as `ctypes.jschar.array()('blah blah').address()`
ctypes.voidptr_t // LPTSTR // can possibly also make this ctypes.char.ptr
);
/* http://msdn.microsoft.com/en-us/library/windows/desktop/aa380073%28v=vs.85%29.aspx
* WINOLEAPI PropVariantClear(
* __in_ PROPVARIANT *pvar
* );
*/
var PropVariantClear = ole32.declare('PropVariantClear', ctypes.winapi_abi, ctypes.voidptr_t, // WINOLEAPI
struct_PROPVARIANT.ptr // PROPVARIANT
);
/*http://msdn.microsoft.com/en-us/library/windows/desktop/ee330727%28v=vs.85%29.aspx
* void IID_PPV_ARGS(
* T **pType
* );
*/
// maybe: http://blogs.msdn.com/b/yvesdolc/archive/2006/12/27/iid-ppv-args-macro.aspx
// var IID_PPV_ARGS = shell32.declare('IID_PPV_ARGS', ctypes.winapi_abi, ctypes.voidptr_t, // void
// ctypes.voidptr_t // T
// );
/* SOURCE1: http://msdn.microsoft.com/en-us/library/windows/desktop/bb761475%28v=vs.85%29.aspx
* HRESULT SetValue(
* __in_ REFPROPERTYKEY key,
* __in_ REFPROPVARIANT propvar
* );
* SOURCE2: http://blogs.msdn.com/b/oldnewthing/archive/2011/06/01/10170113.aspx
* HRESULT IPropertyStore_SetValue(IPropertyStore *pps,
* REFPROPERTYKEY pkey, PCWSTR pszValue)
* {
* PROPVARIANT var;
* HRESULT hr = InitPropVariantFromString(pszValue, &var);
* if (SUCCEEDED(hr))
* {
* hr = pps->SetValue(pkey, var);
* PropVariantClear(&var);
* }
* return hr;
* }
*/
var IPropertyStore_SetValue = function(pps /** IPopertyStore pointer **/, pkey /** PROPERTYKEY **/, pszValue /** PCWSTR **/) {
//pps must be passed in as reference
var v = new struct_PROPVARIANT(); // PROPVARIANT
var rez = InitPropVariantFromString(pszValue, v.address());
if (rez) {
console.info('pps.SetValue', pps.SetValue);
pps.SetValue(pkey, v);
} else {
throw new Error('failed InitPropVariantFromString');
}
return true;
}
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa378137%28v=vs.85%29.aspx
var S_OK = 0;
var S_FALSE = 1;
function onCreate(hwnd) {
var ppv = ctypes.voidptr_t(0);
var pps = new IID();
var HR_pps = CLSIDFromString('{886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99}', pps.address());
if (!checkHRESULT(HR_pps)) {
throw new Error('checkHRESULT error');
}
//console.log('pps', pps.toSource());
var hr = SHGetPropertyStoreForWindow(hwnd, pps.address(), ppv.address());
console.error('hr = ', hr, hr.toSource(), uneval(hr), hr.toString());
if (!checkHRESULT(hr)) { //this throws so no need to do an if on hr brelow, im not sure that was possible anyways as hr is now `-2147467262` and its throwing, before thi, with my `if (hr)` it would continue thinking it passed
throw new Error('checkHRESULT error');
}
var pszValue = ctypes.jschar.array()('Contoso.Scratch'); // PCWSTR
IPropertyStore_SetValue(pps.address(), PKEY_AppUserModel_ID, pszValue.address());
var pszValue = ctypes.jschar.array()('notepad.exe %windir%\\system.ini'); // PCWSTR
IPropertyStore_SetValue(pps.address(), PKEY_AppUserModel_RelaunchCommand, pszValue.address()); //relaucnhcommand and relaunchdispnameresc must both be set otherise if just do one, then it will not take
var pszValue = ctypes.jschar.array()('C:\\full\\path\\to\\scratch.exe,-1'); // PCWSTR;
IPropertyStore_SetValue(pps.address(), PKEY_AppUserModel_RelaunchDisplayNameResource, pszValue.address()); //relaucnhcommand and relaunchdispnameresc must both be set otherise if just do one, then it will not take
//var pszValue = ctypes.jschar.array()('C:\\full\\path\\to\\scratch.ico'); // PCWSTR;
//IPropertyStore_SetValue(pps, PKEY_AppUserModel_RelaunchIconResource, pszValue.address()); //optional
return true;
}
// start - error handling funcs (Source: https://github.com/FunkMonkey/Loomo/blob/2c0a7a042b91c430ca89fd22d5003ccb7dbf57dd/Loomo/chrome/content/modules/Utils/COM/COM.jsm)
function checkHRESULT(hr) {
if (hr != S_OK) {
//throw new Error("TESTERR");
console.error('checkHRESULT error = ', new COMError(hr));
return false;
}
return true;
}
function COMError(hr) {
// Well, we don't subclass, cause for some reason that fucks up the callstack
var name = "COMError " + hr + " (0x" + decimalToHexString(parseInt(hr.toString())) + ")";
var err = new Error(name);
err.isCOMError = true;
err.name = name;
err.message = name + " Well, go and bing!!!";
return err;
}
function decimalToHexString(number) {
if (number < 0) {
number = 0xFFFFFFFF + number + 1;
}
return number.toString(16).toUpperCase();
}
// end : error handling funcs
var me = Services.wm.getMostRecentWindow(null);
var baseWindow = me.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShellTreeItem)
.treeOwner
.QueryInterface(Ci.nsIInterfaceRequestor)
.nsIBaseWindow;
var nativeHandle = baseWindow.nativeHandle;
var targetWindow_handle = ctypes.voidptr_t(ctypes.UInt64(nativeHandle));
/* pid's and guid string's for my 4 properties above (PKEY_AppUserModel_ID, PKEY_AppUserModel_RelaunchIconResource, etc) and more found at following two sources:
* setting pkey = https://github.com/truonghinh/TnX/blob/260a8a623751ffbce14bad6018ea48febbc21bc6/TnX-v8/Microsoft.Windows.Shell/Standard/ShellProvider.cs#L358
* setting pkey in py = https://github.com/liuheng/byp/blob/28c26f8903dc122237982e2c19b58dce35612ad8/thirdlib/pywin32/com/win32comext/propsys/pscon.py#L712
*/
var myGUID = new struct_GUID();
var HR_myGUID = CLSIDFromString('{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}', myGUID.address()); // same for guid for: id, relaubchcommand, relaunchdisp, and relaunchicon
if (!checkHRESULT(HR_myGUID)) {
throw new Error('checkHRESULT error');
}
console.log('myGUID', myGUID);
// Create PKEY for AppUserModel_ID
var PKEY_AppUserModel_ID = new struct_PROPERTYKEY();
PKEY_AppUserModel_ID.fmtid = myGUID;
PKEY_AppUserModel_ID.pid = 5;
// Create PKEY for AppUserModel_RelaunchCommand
var PKEY_AppUserModel_RelaunchCommand = new struct_PROPERTYKEY();
PKEY_AppUserModel_RelaunchCommand.fmtid = myGUID;
PKEY_AppUserModel_RelaunchCommand.pid = 2;
// Create PKEY for AppUserModel_RelaunchDisplayNameResource
var PKEY_AppUserModel_RelaunchDisplayNameResource = new struct_PROPERTYKEY();
PKEY_AppUserModel_RelaunchDisplayNameResource.fmtid = myGUID;
PKEY_AppUserModel_RelaunchDisplayNameResource.pid = 4;
// Create PKEY for AppUserModel_RelaunchIconResource
var PKEY_AppUserModel_RelaunchIconResource = new struct_PROPERTYKEY();
PKEY_AppUserModel_RelaunchIconResource.fmtid = myGUID;
PKEY_AppUserModel_RelaunchIconResource.pid = 3;
onCreate(targetWindow_handle);
@Noitidart
Copy link

DLL for PropVariantClear is Ole32.dll

DLL for InitPropVariantFromString is Propsys.dll maybe/probably.

@Noitidart
Copy link

Issues summary as of Rev5

  • in the C code he's passing two arguments to SHGetPropertyStoreForWindow but it should take three. That's why in my jsctypes code it throws, not enough arguments. L#237
  • He is also running IID_PPV_ARGS on the pps, which is a macro. This macro takes one argument T **pType, what on earth is type T? I just set that to voidptr_t
  • IID_PPV_ARGS returns WINOLEAPI I can't figure out what the js-ctypes equivalent is so i set it to voidptr_t
  • The onCreate function takes a second argument lpcs but he doesnt use it anywhere in the code, is it safe to ommit?
  • The third argument to pass to IPropertyStore_SetValue is type PCWSTR this translates to ctypes.jschar.ptr. Is it proper to pass it like this: new ctypes.PointerType(ctypes.jschar('blah')) L#240
  • I have no idea what DLL winapi functions like PropVariantClear and InitPropVariantFromString are coming from, their MSDN pages don't say which DLL.
  • I'm conufsed on how to make the PROPVARIANT structure as it has something called a VARTYPE which I can't figure out the js-types equivalent. And a union which i found something on FireTray but is it rightly done? L#142

@yajd
Copy link
Author

yajd commented Jun 26, 2014

What is structure for pps? I need to pass address of this to IID_PPV_ARGS which I pass to SHGetPropertyStoreForWindow

@Noitidart
Copy link

For Rev9:

  • Probably make that unionOf72 to the pwszVal as that is what InitPropVariantFromString is using on first line: var hr = SHStrDupW(string, propvar.pwszVal);
  • Added the checkHRESULT for the other places I use HRESULTs because right now like in InitPropVariantFromString I'm just testing with if (hr) and that will obviously fail because if hr is successful it is 0.

@Noitidart
Copy link

Rev11

Not yet added as rev of this gist but its over here: https://github.com/Noitidart/_scratchpad/blob/master/IPropertyStore%20COM%20jsctypes.js
  • REAL progress, fixed up A LOT as I didn't know what I was doing back then
  • Put code style into ostypes scratchpad format for easy porting
  • Fixed up InitPropVariantFromString
  • Got SetValue and Release
  • Fixed up a lot of stuff, worked on it from ground up, based on Tim A.'s shortcutservice.jsm, and now Im not getting an error when trying to set ID of most recent window, however its not working in that its not actually setting the id, otherwise the window would move out of the tab group. I emailed Tim A for help.

@Noitidart
Copy link

This guy here does IShellLink and IPropertyStore via ctypes in java to change the icon of an exe. Real cool: https://github.com/FoxyCorndog/WorkspaceOld/blob/db920ee0628f0f6f1ef07f45ad86c4de97905f3d/SWT/src/org/eclipse/swt/widgets/TaskBar.java#L212

This also shows a case where I would need to QueryInterface IPropertyStore from IShellLink. Which I had but undid as I didn't need it for this purpose. I had it like this in rev95 here: https://github.com/Noitidart/_scratchpad/blob/f8ac49ffcaebee19ed5a2e8e29230d2e34bea899/IPropertyStore%20COM%20jsctypes.js#L668

Here is a copy paste of the code from that repo (just in case he deletes the repo or something):
It's in java:

int /*long*/ createShellLink (MenuItem item, String directory) {
    int style = item.getStyle ();
    if ((style & SWT.CASCADE) != 0) return 0;
    int /*long*/ [] ppv = new int /*long*/ [1];
    int hr = OS.CoCreateInstance (CLSID_ShellLink, 0, OS.CLSCTX_INPROC_SERVER, IID_IShellLinkW, ppv);
    if (hr != OS.S_OK) error (SWT.ERROR_NO_HANDLES);
    int /*long*/ pLink = ppv [0];

    int /*long*/ hHeap = OS.GetProcessHeap ();
    int /*long*/ pv = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, OS.PROPVARIANT_sizeof());
    int /*long*/ titlePtr = 0;
    PROPERTYKEY key;
    if ((style & SWT.SEPARATOR) != 0) {
        OS.MoveMemory (pv, new short [] {OS.VT_BOOL}, 2);
        OS.MoveMemory (pv + 8, new short [] {OS.VARIANT_TRUE}, 2);
        key = PKEY_AppUserModel_IsDestListSeparator;
    } else {
        String text = item.getText ();
        int length = text.length ();
        char [] buffer = new char [length + 1];
        text.getChars (0, length, buffer, 0);
        titlePtr = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, buffer.length * 2);
        OS.MoveMemory (titlePtr, buffer, buffer.length * 2);
        OS.MoveMemory (pv, new short [] {OS.VT_LPWSTR}, 2);
        OS.MoveMemory (pv + 8, new int /*long*/ [] {titlePtr}, OS.PTR_SIZEOF);
        key = PKEY_Title;

        /*IShellLink::SetPath*/
        String exePath = (String)item.getData (EXE_PATH_KEY);
        if (exePath != null) {
            length = exePath.length ();
            buffer = new char [length + 1];
            exePath.getChars (0, length, buffer, 0);
        } else {
            buffer = EXE_PATH;
        }
        hr = OS.VtblCall (20, pLink, buffer);
        if (hr != OS.S_OK) error (SWT.ERROR_INVALID_ARGUMENT);

        text =  (String)item.getData (EXE_ARGS_KEY);
        if (text == null) text = Display.LAUNCHER_PREFIX + Display.TASKBAR_EVENT + item.id;
        length = text.length ();
        buffer = new char [length + 1];
        text.getChars (0, length, buffer, 0);
        /*IShellLink::SetArguments*/
        hr = OS.VtblCall (11, pLink, buffer);
        if (hr != OS.S_OK) error (SWT.ERROR_INVALID_ARGUMENT);

        /* This code is intentionally commented */
//      String tooltip = item.tooltip;
//      if (tooltip != null) {
//          length = tooltip.length ();
//          buffer = new char [length + 1];
//          tooltip.getChars (0, length, buffer, 0);
//          /*IShellLink::SetDescription*/
//          hr = OS.VtblCall (7, pLink, buffer);
//          if (hr != OS.S_OK) error (SWT.ERROR_INVALID_ARGUMENT);
//      }

        String icon = (String)item.getData (ICON_KEY);
        int index = 0;
        if (icon != null) {
            text = (String)item.getData (ICON_INDEX_KEY);
            if (text != null) index = Integer.parseInt (text);
        } else {
            Image image = item.getImage ();
            if (image != null && directory != null) {
                icon = directory + "\\menu" + item.id + ".ico" ;
                ImageData data;
                if (item.hBitmap != 0) {
                    Image image2 = Image.win32_new (display, SWT.BITMAP, item.hBitmap);
                    data = image2.getImageData ();
                } else {
                    data = image.getImageData ();
                }
                ImageLoader loader = new ImageLoader ();
                loader.data = new ImageData [] {data};
                loader.save (icon, SWT.IMAGE_ICO);
            }
        }
        if (icon != null) {
            length = icon.length ();
            buffer = new char [length + 1];
            icon.getChars (0, length, buffer, 0);
            /*IShellLink::SetIconLocation*/
            hr = OS.VtblCall (17, pLink, buffer, index);
            if (hr != OS.S_OK) error (SWT.ERROR_INVALID_ARGUMENT);
        }
    }

    /*IUnknown::QueryInterface*/
    hr = OS.VtblCall (0, pLink, IID_IPropertyStore, ppv);
    if (hr != OS.S_OK) error (SWT.ERROR_NO_HANDLES);
    int /*long*/ pPropStore = ppv [0];
    /*IPropertyStore::SetValue*/
    hr = OS.VtblCall (6, pPropStore, key, pv);
    if (hr != OS.S_OK) error (SWT.ERROR_INVALID_ARGUMENT);
    /*IPropertyStore::Commit*/
    OS.VtblCall (7, pPropStore);
    /*IUnknown::Release*/
    OS.VtblCall (2, pPropStore);

    OS.HeapFree (hHeap, 0, pv);
    if (titlePtr != 0) OS.HeapFree (hHeap, 0, titlePtr);
    return pLink;
}

@Noitidart
Copy link

Here's how to QI IPropertyStore from ShelLink to set properties on a shortcut I would think:

https://github.com/dlunch/foo_alsong_lyric/blob/aba6c9061213d37980c010025263ffae31a752de/src/foo_alsong_lyric/UIWnd.cpp#L211

int UIWnd::AddTaskList(std::wstring command, std::wstring display, std::wstring appid)
{
    ICustomDestinationList *destlist = NULL;
    CoCreateInstance(CLSID_DestinationList, NULL, CLSCTX_INPROC_SERVER, IID_ICustomDestinationList, (void **)&destlist);
    if(destlist) //main window list
    {
        UINT MinSlot;
        IObjectArray *removed;
        if(appid.size())
            destlist->SetAppID(appid.c_str());
        destlist->BeginList(&MinSlot, IID_IObjectArray, (void **)&removed);
        IObjectCollection *tasks;
        CoCreateInstance(CLSID_EnumerableObjectCollection, NULL, CLSCTX_INPROC_SERVER, IID_IObjectCollection, (void **)&tasks);

        IShellLink *link;
        CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void **)&link);
        wchar_t name[255];
        GetModuleFileName(GetModuleHandle(L"foobar2000.exe"), name, 255);
        link->SetPath(name);
        link->SetArguments((std::wstring(L"/command:\"") + command + L"\"").c_str());

        IPropertyStore *propstore;
        link->QueryInterface(IID_IPropertyStore, (void **)&propstore);
        PROPVARIANT pv;
        InitPropVariantFromString(display.c_str(), &pv);
        propstore->SetValue(PKEY_Title, pv);
        propstore->Commit();
        propstore->Release();

        tasks->AddObject(link);
        IObjectArray *arr;
        tasks->QueryInterface(IID_IObjectArray, (void **)&arr);
        destlist->AddUserTasks(arr);
        destlist->CommitList();

        destlist->Release();
        tasks->Release();
        arr->Release();
        link->Release();
        removed->Release();

        return 1;
    }

    return 0;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment