0

I'm using Visual Studio 2010 on Windows 7 SP1 x64.

I need to make a custom deskband that communicates with a win32 application, compiled as 32bit app.

I used this CodeProject article as a starting point: http://www.codeproject.com/Articles/39189/Shell-Extensibility-Explorer-Desk-Band-Tray-Notifi

The deskband dll (compiled for x64) works fine - I see it in the taskbar, etc.

It is registered using regsvr32 from a cmd.exe that was "Run as administrator". The rgs file is this:

HKCR
{
    NoRemove CLSID
    {
        ForceRemove {8D330AEA-90D1-4632-804B-4710F2358A18} = s 'my deskbar'
        {
            InprocServer32 = s '%MODULE%'
            {
                val ThreadingModel = s 'Apartment'
            }
        }
    }
}

Based on this stackoverflow answer: How to add and implement a new interface to an Inproc COM server

I've created an interface in the IDL file:

import "oaidl.idl";
import "ocidl.idl";

[
object,
uuid(35662650-07D0-4F31-B5F0-3C21FCA4BDA2),
version(1.0),
oleautomation
//dual,
//pointer_default(unique)
]
interface IGEDeskBand : IDispatch
{
        HRESULT BadgeSet([in] long arg);
        HRESULT BadgeClear(void);
        HRESULT BadgeBlink(void);
};

[
    uuid(33EB8C00-F66A-493C-8607-529060FAA378),
    version(1.0),
    helpstring("GamEffective DeskBand Library")
]
library GEDeskBandLib
{
  importlib("stdole32.tlb");

    [
        uuid(8D330AEA-90D1-4632-804B-4710F2358A18),
        helpstring("GEDeskBand Class")
    ]
    coclass GEDeskBand
    {
        [default] interface IGEDeskBand;
    };

  cpp_quote("const size_t MAX_GUID_STRING_LEN = 39;")
};

, visual studio compiles it using midl producing the generated headers, I include the generated code: #include "GEDeskBand_i.h", and I made my class inherit from

public IDispatchImpl<IGEDeskBand, &__uuidof(IGEDeskBand), &LIBID_GEDeskBandLib, /* wMajor = */ 1, /* wMinor = */ 0>

, added a COM_INTERFACE_ENTRY with interface to the COM MAP and added the methods:

STDMETHOD(BadgeSet)(/* [in] */ long arg);
STDMETHOD(BadgeClear)();
STDMETHOD(BadgeBlink)();

(well, I used the visual studio wizard: class view -> right click class, add -> implement interface), currently the methods' implementation is just return E_NOTIMPL;, and I hope that's not a problem. This is all that I've done with the code on the server, regarding my interface and trying to communicate with this object from another process.

Using #define _ATL_DEBUG_QI from this article (http://msdn.microsoft.com/en-us/library/ezs95xhx%28v=vs.80%29.aspx), I see in the Output window of Visual Studio when attached to explorer.exe (where the deskbar dll is loaded and my COM object lives) that QueryInterface is queried with the right interface and doesn't say "failed".

This is when the deskbar is loaded:

CComClassFactory - IClassFactory
CGEDeskBand - IDeskBand
CGEDeskBand - IPersistStreamInit
CGEDeskBand - IDeskBand2
CGEDeskBand - IDeskBand
CGEDeskBand - {EA5F2D61-E008-11CF-99CB-00C04FD64497} - failed
CGEDeskBand - IObjectWithSite
CGEDeskBand - {7FE80CC8-C247-11D0-B93A-00A0C90312E1} - failed
CGEDeskBand - IOleCommandTarget - failed
CGEDeskBand - IDeskBandInfo - failed
CGEDeskBand - IDeskBand
CGEDeskBand - IOleWindow
CGEDeskBand - IPersist
CGEDeskBand - IDeskBand2

and this is when I run the tester that demonstrates the problem:

CGEDeskBand - IDeskBand
CGEDeskBand - {00000003-0000-0000-C000-000000000046} - failed
CGEDeskBand - {00000003-0000-0000-C000-000000000046} - failed
CGEDeskBand - {0000001B-0000-0000-C000-000000000046} - failed
CGEDeskBand - IUnknown
CGEDeskBand - {00000018-0000-0000-C000-000000000046} - failed
CGEDeskBand - {00000019-0000-0000-C000-000000000046} - failed
CGEDeskBand - {4C1E39E1-E3E3-4296-AA86-EC938D896E92} - failed
CGEDeskBand - IDeskBand
CGEDeskBand - IDeskBand
CGEDeskBand - IPersist
CGEDeskBand - IPersist
CGEDeskBand - {1C733A30-2A1C-11CE-ADE5-00AA0044773D} - failed
CGEDeskBand - {35662650-07D0-4F31-B5F0-3C21FCA4BDA2}

The last one is my IID and it doesn't say failed.

This is a test code that demonstrates the problem:

#include <Shobjidl.h>
#include "GEDeskBand_i.h"
#include "GEDeskBandGuids.h"

int _tmain(int argc, _TCHAR* argv[])
{
    HRESULT hr;
    CComPtr<IUnknown> spBandService;
    CComPtr<IBandSite> spBandSite;
    CoInitialize(NULL);
    hr = spBandService.CoCreateInstance(CLSID_TrayBandSiteService);
    if (SUCCEEDED(hr)) {
        hr = spBandService->QueryInterface(&spBandSite);
        if (SUCCEEDED(hr)) {
            DWORD dwBandId = 0;
            UINT num_bands = spBandSite->EnumBands((UINT)-1, &dwBandId);
            for (UINT uBand = 0; uBand < num_bands; uBand++) {
                if (SUCCEEDED(spBandSite->EnumBands(uBand, &dwBandId))) {
                    IDeskBand *band;
                    if (SUCCEEDED(spBandSite->GetBandObject(dwBandId, IID_IDeskBand, (void**)&band))) {
                        IPersist* pPersist = NULL;
                        if (SUCCEEDED(band->QueryInterface(IID_IPersist, (void**)&pPersist))) {
                            CLSID clsid = CLSID_NULL;
                            if (SUCCEEDED(pPersist->GetClassID(&clsid))) {
                                if (clsid == CLSID_GEDeskBand) {
                                    // reaches here fine
                                    IGEDeskBand *pGEDeskBand = NULL;
                                    hr = band->QueryInterface(IID_IGEDeskBand, (void**)&pGEDeskBand);
                                    if (SUCCEEDED(hr)) {
                                        // never reaches here, always get E_NOINTERFACE
                                        pGEDeskBand->BadgeSet(1);
                                    }
                                    break;
                                }
                            }
                        }
                    }
                }
            }
            spBandSite.Release();
        }
        spBandService.Release();
    }
    CoUninitialize();
    return 0;
}

The problem is that although I see the IDeskBand or the IPersist of my DeskBand object (the CLSID matches, I know it's my object, and I know it implements that interface), QueryInterface using my interface IID_IGEDeskBand never succeeds.

QueryInterface does find the IID, but it does not reach my client. Because of marshaling, I suppose.

I've tried compiling the tester as x64, but that didn't work.

Raymond Chen wrote about this: http://blogs.msdn.com/b/oldnewthing/archive/2004/12/13/281910.aspx

All I understood from that blog post is that I need to deal with the marshaling. I thought it was supposed to be automatic?

I saw that one way to fix this is to build a midl generated proxy-stub: CoCreateInstance returning E_NOINTERFACE even though interface is found

I've built and registered a proxy-stub dll, a 32bit one, by getting midl to create 32bit proxy code and the recipe from here: http://msdn.microsoft.com/en-us/library/aa366768%28VS.85%29.aspx

This didn't work either - when running the tester, I do not see the proxy-stub DLL loaded in the tester process. Should I? It seems to be registered correctly.

I don't know COM or even general win32 all that well, but I've been googling for hours and I don't understand what I'm doing wrong.

Community
  • 1
  • 1
Z.T.
  • 939
  • 8
  • 20
  • You don't show how it's made on COM server side, the problem is perhaps there. The call should be reaching your COM object and fails from there. – Roman R. Nov 13 '12 at 08:04
  • I added some details about the code on the COM server side and yes, the call reaches QueryInterface, succeeds there and fails from there, exactly the way it should if marshaling doesn't work. – Z.T. Nov 13 '12 at 16:23
  • This should be something simple. You create inproc server, marshaling is real cause however can only be in question if your COM class is registered with "free" apartment model. I would say it's something still in COM class definition, `COM_INTERFACE_xxx` map or bad reference to type library, or missing/unregistered type library. You are not supposed to get into this trouble, nuless you already made a mistake on the way. – Roman R. Nov 13 '12 at 16:38
  • I agree that I thought it should be simple. I think it's not the COM map because the QueryInterface part works. I didn't change any references to tlb. In fact, I see under property pages -> configuration properties -> midl -> output -> generate type library is set to "no". But the DLL is created and registered fine (as I said, the deskband does work). – Z.T. Nov 13 '12 at 17:30

0 Answers0