The prototype of ITaskHandler::Start
is the following:
HRESULT ( STDMETHODCALLTYPE Start )(
__RPC__in ITaskHandler * This,
/* [in] */ __RPC__in_opt IUnknown *pHandlerServices,
/* [in] */ __RPC__in BSTR data)
How come pHandlerServices
is optional - and if I get a NULL (as it's my case) - how can I notify the task scheduler that I've completed the task.
OK - here is the deal I implemented QueryInterface
of the class to always return the same object thinking - ITaskHandler
will immediately be queried. However this wasn't the case - the first query was for IClassFactory
and the function signature of CreateInstance
had the second parameter pUnkOuter
NULL which overlaps with the second parameter of my implementation of ITaskHandler_Start
. Nevertheless it's weird that pHandlerServices
is marked as optional.
Here is my current implementation of the handler which is still not working (the last run result is No such interface supported (0x80004002)) - my interface ITaskHandler
is never queried for. I even went so far implementing ICallFactory
but alias with no luck (CreateCall
is never called) - here is the code:
#define COBJMACROS
#include <windows.h>
#include <objbase.h>
#include <unknwn.h>
// {179D1704-49C5-4111-B3CF-C528ABB014D0}
DEFINE_GUID(CLSID_IRmouseHandler,
0x179d1704, 0x49c5, 0x4111, 0xb3, 0xcf, 0xc5, 0x28, 0xab, 0xb0, 0x14, 0xd0);
#define wstringCLSID_IRmouseHandler L"{179D1704-49C5-4111-B3CF-C528ABB014D0}"
static const GUID CLSID_IRmouseHandler =
{ 0x179d1704, 0x49c5, 0x4111, { 0xb3, 0xcf, 0xc5, 0x28, 0xab, 0xb0, 0x14, 0xd0 } };
// {D363EF80-5C42-46D8-847B-B3A27A3BD0E3}
DEFINE_GUID(IID_IRmouseHandler,
0xd363ef80, 0x5c42, 0x46d8, 0x84, 0x7b, 0xb3, 0xa2, 0x7a, 0x3b, 0xd0, 0xe3);
static const GUID IID_IRmouseHandler =
{ 0xd363ef80, 0x5c42, 0x46d8, { 0x84, 0x7b, 0xb3, 0xa2, 0x7a, 0x3b, 0xd0, 0xe3 } };
#include <taskschd.h>
#include <ObjIdl.h>
#define stub(x)\
\
STDMETHODCALLTYPE x() {\
MessageBox(\
NULL,\
"ITaskHandler_" #x,\
"Account Details",\
MB_ICONWARNING | MB_CANCELTRYCONTINUE | MB_DEFBUTTON2\
);}
extern ITaskHandler tskhandler; extern IClassFactory factory; extern ICallFactory callfactory;
stub(CreateCall)
HRESULT ( STDMETHODCALLTYPE CreateInstance )(
IClassFactory * This,
/* [annotation][unique][in] */
_In_opt_ IUnknown *pUnkOuter,
/* [annotation][in] */
_In_ REFIID riid,
/* [annotation][iid_is][out] */
_COM_Outptr_ void **ppvObject) { return QueryInterface(This, riid, ppvObject);}
HRESULT STDMETHODCALLTYPE QueryInterface(
__RPC__in ITaskHandler * This,
/* [in] */ __RPC__in REFIID riid,
/* [annotation][iid_is][out] */
_COM_Outptr_ void **ppvObject) {
if(!ppvObject) return E_POINTER;
if(!memcmp(riid, &IID_ITaskHandler, sizeof *riid) || !memcmp(riid, &IID_IUnknown, sizeof *riid))*ppvObject = &tskhandler;
else if(!memcmp(riid, &IID_ICallFactory, sizeof *riid))*ppvObject = &callfactory;
else if(!memcmp(riid, &IID_IClassFactory, sizeof *riid))*ppvObject = &factory;
else return E_NOINTERFACE;
return S_OK;}
ULONG STDMETHODCALLTYPE AddRef(){}
stub(Release)
HRESULT ( STDMETHODCALLTYPE Start )(
__RPC__in ITaskHandler * This,
/* [in] */ __RPC__in_opt IUnknown *pHandlerServices,
/* [in] */ __RPC__in BSTR data) {ITaskHandlerStatus *pHandlerStatus;
IUnknown_QueryInterface(pHandlerServices,&IID_ITaskHandlerStatus,&pHandlerStatus),
ITaskHandlerStatus_TaskCompleted(pHandlerStatus,S_OK);return S_OK;}
stub(Stop)
stub(Pause)
stub(Resume)
ITaskHandler tskhandler = {.lpVtbl = &(struct ITaskHandlerVtbl){.QueryInterface=QueryInterface,.Resume=Resume,
.AddRef=AddRef,.Release=Release,.Start=Start,.Stop=Stop,.Pause=Pause}};
IClassFactory factory = {.lpVtbl = &(struct IClassFactoryVtbl){.QueryInterface=QueryInterface,
.AddRef=AddRef,.Release=Release,.CreateInstance=CreateInstance}};
ICallFactory callfactory = {.lpVtbl = &(struct ICallFactoryVtbl){.QueryInterface=QueryInterface,
.AddRef=AddRef,.Release=Release,.CreateCall=CreateCall}};
int WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nShowCmd
) { DWORD dwToken; //AddVectoredExceptionHandler(1,PvectoredExceptionHandler);
CoInitializeEx(NULL,0), CoRegisterClassObject(&CLSID_IRmouseHandler,&tskhandler,CLSCTX_LOCAL_SERVER,REGCLS_MULTIPLEUSE,&dwToken),Sleep(INFINITE);}
There are 3 issues:
1) AddRef and stub must have 1 parameter, like IUnknown* This (or LPVOID This). It would does not matter if your function were __cdecl but COM works only with __stdcall functions. And system expects functions clean parameters from stack by own.
2) AddRef must return not 0 at least, and stub function must return S_OK or 0. Maybe it's not large issue..
3) Investigations shows system queries IUnknown for class factory, maybe it performs QueryInterface instead AddRef. But anyway it's not correct to return taskhandler for every IUnknown query. It's even better to return "factory". However it's better to return This on IUnknown, anyway every interface is descendant of IUnknown.
Bonus issue, if you compile under Unicode message box shows strange Chinese text, because there is needed to make text parameters Unicode, or use MessageBoxA explicitly
#define COBJMACROS
#include <windows.h>
#include <objbase.h>
#include <unknwn.h>
// {179D1704-49C5-4111-B3CF-C528ABB014D0}
DEFINE_GUID(CLSID_IRmouseHandler,
0x179d1704, 0x49c5, 0x4111, 0xb3, 0xcf, 0xc5, 0x28, 0xab, 0xb0, 0x14, 0xd0);
#define wstringCLSID_IRmouseHandler L"{179D1704-49C5-4111-B3CF-C528ABB014D0}"
static const GUID CLSID_IRmouseHandler =
{ 0x179d1704, 0x49c5, 0x4111,{ 0xb3, 0xcf, 0xc5, 0x28, 0xab, 0xb0, 0x14, 0xd0 } };
// {D363EF80-5C42-46D8-847B-B3A27A3BD0E3}
DEFINE_GUID(IID_IRmouseHandler,
0xd363ef80, 0x5c42, 0x46d8, 0x84, 0x7b, 0xb3, 0xa2, 0x7a, 0x3b, 0xd0, 0xe3);
static const GUID IID_IRmouseHandler =
{ 0xd363ef80, 0x5c42, 0x46d8,{ 0x84, 0x7b, 0xb3, 0xa2, 0x7a, 0x3b, 0xd0, 0xe3 } };
#include <taskschd.h>
#include <ObjIdl.h>
// Vano101: Make This parameter, Return S_OK, Use MessageBoxA or L before text
#define stub(x)\
\
STDMETHODCALLTYPE x(IUnknown* This) {\
MessageBoxA(\
NULL,\
"ITaskHandler_" #x,\
"Account Details",\
MB_ICONWARNING | MB_CANCELTRYCONTINUE | MB_DEFBUTTON2\
); \
return S_OK; \
}
extern ITaskHandler tskhandler;
extern IClassFactory factory;
extern ICallFactory callfactory;
stub(CreateCall)
HRESULT(STDMETHODCALLTYPE CreateInstance)(
IClassFactory * This,
/* [annotation][unique][in] */
_In_opt_ IUnknown *pUnkOuter,
/* [annotation][in] */
_In_ REFIID riid,
/* [annotation][iid_is][out] */
_COM_Outptr_ void **ppvObject) {
return QueryInterface(This, riid, ppvObject);
}
HRESULT STDMETHODCALLTYPE QueryInterface(
__RPC__in ITaskHandler * This,
/* [in] */ __RPC__in REFIID riid,
/* [annotation][iid_is][out] */
_COM_Outptr_ void **ppvObject) {
if (!ppvObject) return E_POINTER;
if (!memcmp(riid, &IID_ITaskHandler, sizeof *riid)) *ppvObject = &tskhandler;
else if (!memcmp(riid, &IID_IUnknown, sizeof *riid)) *ppvObject = &factory; // Vano101: Return factory on IUnknown
else if (!memcmp(riid, &IID_ICallFactory, sizeof *riid))*ppvObject = &callfactory;
else if (!memcmp(riid, &IID_IClassFactory, sizeof *riid))*ppvObject = &factory;
else return E_NOINTERFACE;
return S_OK;
}
// Vano101: Return 1 on AddRef!, Make This parameter
ULONG STDMETHODCALLTYPE AddRef(IUnknown* This) { return 1; }
stub(Release)
HRESULT(STDMETHODCALLTYPE Start)(
__RPC__in ITaskHandler * This,
/* [in] */ __RPC__in_opt IUnknown *pHandlerServices,
/* [in] */ __RPC__in BSTR data) {
ITaskHandlerStatus *pHandlerStatus;
IUnknown_QueryInterface(pHandlerServices, &IID_ITaskHandlerStatus, &pHandlerStatus),
ITaskHandlerStatus_TaskCompleted(pHandlerStatus, S_OK); return S_OK;
}
stub(Stop)
stub(Pause)
stub(Resume)
ITaskHandler tskhandler = { .lpVtbl = &(struct ITaskHandlerVtbl) {
.QueryInterface = QueryInterface,.Resume = Resume,
.AddRef = AddRef,.Release = Release,.Start = Start,.Stop = Stop,.Pause = Pause
} };
IClassFactory factory = { .lpVtbl = &(struct IClassFactoryVtbl) {
.QueryInterface = QueryInterface,
.AddRef = AddRef,.Release = Release,.CreateInstance = CreateInstance
} };
ICallFactory callfactory = { .lpVtbl = &(struct ICallFactoryVtbl) {
.QueryInterface = QueryInterface,
.AddRef = AddRef,.Release = Release,.CreateCall = CreateCall
} };
int WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nShowCmd
) {
DWORD dwToken; //AddVectoredExceptionHandler(1,PvectoredExceptionHandler);
CoInitializeEx(NULL, 0), CoRegisterClassObject(&CLSID_IRmouseHandler, &tskhandler, CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE, &dwToken), Sleep(INFINITE);
}