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

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