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.