Search code examples
winapifirefox-addonjsctypesipropertystorage

SetValue and Release are not functions


In summary: I searched MSDN and thought to create my own SetValue but I can't find any sourcecode for it. It's not in a DLL, its like InitPropVariantFromString, it's in a header file, but I can't find it. :( If you can please just show me htat C++ code I'll take it from there :)

Bottom stuff is to show the efforts I put on it.


This is the tiny C++ code I'm converting:

OnCreate(HWND hwnd, LPCREATESTRUCT lpcs)
{
 IPropertyStore *pps;
 HRESULT hr = SHGetPropertyStoreForWindow(hwnd, IID_PPV_ARGS(&pps));
 if (SUCCEEDED(hr)) {
  IPropertyStore_SetValue(pps, PKEY_AppUserModel_ID, L"Contoso.Scratch");
 }
}

I have translated this to:

  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'); }

  var hr = SHGetPropertyStoreForWindow(hwnd, pps.address(), ppv.address());
  if (!checkHRESULT(hr)) { throw new Error('checkHRESULT error'); }

  var pszValue = ctypes.jschar.array()('Contoso.Scratch'); // PCWSTR
  IPropertyStore_SetValue(pps.address(), PKEY_AppUserModel_ID, pszValue.address());

This IPropertyStore_SetValue I converted from this C++:

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;
}

I got it to:

var struct_PROPVARIANT = ctypes.StructType('PROPVARIANT', [
  {'fntud': struct_GUID}, // GUID
  {'pid': ctypes.unsigned_long}, // DWORD
  // comment from loomo on union :: 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"
  // so now in my case i know that InitPropVariantFromString is looking to se the pwszVal so forget all the other crap in the union and just leave this one
  {'pwszVal': new ctypes.PointerType(ctypes.jschar)}  // LPWSTR 
]);

var IPropertyStore_SetValue = function(pps /** IPopertyStore pointer **/, pkey /** PROPERTYKEY **/, pszValue /** PCWSTR **/) {
  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;
}

So this required me to write InitPropVariantFromString which I did:

function InitPropVariantFromString(string /** PCWSTR **/, propvarPtr /** PROPVARIANT pointer **/) {
    var hr = SHStrDup(string, propvarPtr.contents.pwszVal.address());
    if (!checkHRESULT(hr)) {
       //PropVariantInit(propvarPtr); //can skip this, it just does a memset
    }
    return true;                      
}

A SMALL ASIDE ON MY SHSTRDUP DILEMMA (not a question just showing i worked hard and through unexplainable weirdness got it to work)

So this required me to do SHStrDup which I thought would take two arguments of ctypes.jschar.ptr as it is first argument of LPCTSTR and second arg of LPTSTR.

For the first argument, i was passing the PCWSTR of recall ctypes.jschar.array()('Contoso.Scratch'). So when I the first arg definition set to ctypes.jschar.ptr it wouldn't work use it throws this error: expected type pointer, got ctypes.jschar.array(16).ptr(ctypes.UInt64("0x2855b560"))

So I change in the defintion from ctypes.jschar.ptr to ctypes.voidptr_t in defintion so then it worked for this argument but then threw error on second argument recall the propvarPtr.contents.pwszVal.address():

ctypes.jschar.ptr.ptr(ctypes.UInt64("0x20fdb3b4")),

So then I just ended up setting both arguments of SHStrDup to ctypes.voidptr_t and it worked. Don't know why but it worked. And propvarPtr.contents.pwszVal is definitely getting populated, because when I console.log it it shows: "propvarPtr.contents.pwszVal" CData { contents: "C" }.

So the final SHStrDup defintion looked like this:

/* 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 // should be ctypes.jschar.ptr OR ctypes.char.ptr // im trying to pass PCWSTR here, which is `ctypes.jschar.array()('blah blah').address()`
  ctypes.voidptr_t // LPTSTR // should be ctypes.jschar.ptr OR ctypes.char.ptr // im trying to pass address to struct_PROPVARIANT.pwszVal which is new ctypes.PointerType(ctypes.jschar)
);

So now all that is passing and I end up back in IPropertyStore_SetValue in this if:

  var rez = InitPropVariantFromString(pszValue, v.address());
  if (rez) {
    console.info('pps.SetValue', pps.SetValue);
    pps.SetValue(pkey, v);
  } else {
    throw new Error('failed InitPropVariantFromString');
  }

Now rez is true, so then it now tries pps.SetValue and this is where it goes kaput and I can't figure out how to fix it. :(

I searched MSDN and thought to create my own SetValue but I can't find any sourcecode for it. It's not in a DLL, its like InitPropVariantFromString, it's in a header file, but I can't find it. :( If you can please just show me htat C++ code I'll take it from there :)


Solution

  • Solved, I had to use VTBL: https://github.com/Noitidart/_scratchpad/blob/master/IPropertyStore%20COM%20jsctypes.js

    The order of the defintions in the Vtbl matter big time. So make sure its correct if you want to do something with Vtbls

    var IPropertyStoreVtbl = new ctypes.StructType('IPropertyStoreVtbl');
    var IPropertyStore = new ctypes.StructType('IPropertyStore', [{
        'lpVtbl': IPropertyStoreVtbl.ptr
    }]);
    this.IPropertyStorePtr = new ctypes.PointerType(IPropertyStore);
    
    IPropertyStoreVtbl.define(
        [{ //start inherit from IUnknown
            'QueryInterface': ctypes.FunctionType(ctypes.stdcall_abi,
                this.HRESULT, [
                    IPropertyStore.ptr,
                    this.REFIID,    // riid
                    this.VOIDPTR    // **ppvObject
                ]).ptr
        }, {
            'AddRef': ctypes.FunctionType(ctypes.stdcall_abi,
                this.ULONG, [
                    IPropertyStore.ptr
                ]).ptr
        }, {
            'Release': ctypes.FunctionType(ctypes.stdcall_abi,
                this.ULONG, [
                    IPropertyStore.ptr
                ]).ptr
        }, { //end inherit from IUnknown //start IPropertyStore
            'GetCount': ctypes.FunctionType(ctypes.stdcall_abi,
                this.HRESULT, [
                    IPropertyStore.ptr,
                    this.DWORD.ptr  // *cProps
                ]).ptr
        }, {
            'GetAt': ctypes.FunctionType(ctypes.stdcall_abi,
                this.HRESULT, [
                    IPropertyStore.ptr,
                    this.DWORD,             // iProp
                    this.PROPERTYKEY.ptr    //*pkey
                ]).ptr
        }, {
            'GetValue': ctypes.FunctionType(ctypes.stdcall_abi,
                this.HRESULT, [
                    IPropertyStore.ptr,
                    this.REFPROPERTYKEY,    // key
                    this.PROPVARIANT.ptr    // *pv
                ]).ptr
        }, {
            'SetValue': ctypes.FunctionType(ctypes.stdcall_abi,
                this.HRESULT, [
                    IPropertyStore.ptr,
                    this.REFPROPERTYKEY,    // key
                    this.REFPROPVARIANT     // propvar
                ]).ptr
        }, {
            'Commit': ctypes.FunctionType(ctypes.stdcall_abi,
                this.HRESULT, [
                    IPropertyStore.ptr
                ]).ptr
        }]
    );
    

    Then to get the IPropertyStore of a window:

    var ppsPtr = new ostypes.IPropertyStorePtr();
    var hr_SHGetPropertyStoreForWindow = _dec('SHGetPropertyStoreForWindow')(cHwnd, IID_IPropertyStore.address(), ppsPtr.address()); 
    checkHRESULT(hr_SHGetPropertyStoreForWindow, 'SHGetPropertyStoreForWindow');
    var pps = ppsPtr.contents.lpVtbl.contents;
    

    now you can use IPropertyStore :: SetValue and IPropertyStore :: Release but make sure to pass the ppsPtr as first argument to them then the remaining arguments are what you would expect: var hr = pps.SetValue(ppsPtr, firstArgOf_SetValue, secondArgOf_SetValue)


    EDIT:

    adding copy paste runnable script from scratchpad. Copy and paste this: http://pastebin.mozilla.org/8429999

    this will do the equivalent of this XPCOM:

     var win = Services.wm.getMostRecentWindow(null);
     Cc["@mozilla.org/windows-taskbar;1"].getService(Ci.nsIWinTaskbar).setGroupIdForWindow(win, 'Contoso.Scratch')
    

    Yes its long but the good thing about it is, there is no XPCOM to set RelaunchCommand and RelaunchIconResource etc. Thats why js-ctypes is needed.