c - 尝试实现任务调度程序 COM 处理程序
问题描述
的原型ITaskHandler::Start
如下:
HRESULT ( STDMETHODCALLTYPE Start )(
__RPC__in ITaskHandler * This,
/* [in] */ __RPC__in_opt IUnknown *pHandlerServices,
/* [in] */ __RPC__in BSTR data)
为什么pHandlerServices
是可选的 - 如果我得到一个 NULL (因为它是我的情况) - 我如何通知任务调度程序我已经完成了任务。
好的 - 这是我实现QueryInterface
的类的交易,总是返回相同的对象思考 -ITaskHandler
将立即被查询。然而事实并非如此——第一个查询是 forIClassFactory
并且函数签名CreateInstance
的第二个参数pUnkOuter
NULL 与我实现的第二个参数重叠ITaskHandler_Start
。然而,它pHandlerServices
被标记为可选是很奇怪的。
这是我当前的处理程序实现,它仍然无法正常工作(最后运行结果是不支持此类接口(0x80004002)) - 我的接口ITaskHandler
从未被查询。我什至到目前为止实现 ICallFactory
但没有运气的别名(CreateCall
从未调用过) - 这是代码:
#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);}
解决方案
The key to this as @HansPassant notes is that Task Scheduler will only run COM objects out of process. In order to do this you need register your COM object to use the system supplied DllSurrogate.
Here are the COM registration keys for my example
HKEY_CLASSES_ROOT\AppID\{6B9279D0-D220-4288-AFDF-E424F558FEF2}
DllSurrogate REG_SZ ""
Computer\HKEY_CLASSES_ROOT\CLSID\{36A976F4-698B-4B50-BE2C-83F815575199}
Default REG_SZ Path\To\your_com.dll
AppID REG_SZ {6B9279D0-D220-4288-AFDF-E424F558FEF2}
ThreadingModel REG_SZ Both
Working example code (sorry C++) - it basically just a default COM implementation with the addition of ITaskHandler. It at least calls Start. If you want to use you own code, I suggest that you test the COM object loads in a simple test program before worrying about the Task Scheduler.
// {36A976F4-698B-4B50-BE2C-83F815575199}
DEFINE_GUID(CLSID_TestObject,
0x36a976f4, 0x698b, 0x4b50, 0xbe, 0x2c, 0x83, 0xf8, 0x15, 0x57, 0x51, 0x99);
int main()
{
CoInitialize(nullptr);
ITaskHandler* handler = nullptr;
HRESULT hr = CoCreateInstance(CLSID_TestObject, nullptr, CLSCTX_LOCAL_SERVER, IID_ITaskHandler, (LPVOID*)&handler);
fprintf(stderr, "CoCreateInstance %08x\r\n", hr);
return 0;
}
DllMain.cpp
extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
return TRUE;
}
// {36A976F4-698B-4B50-BE2C-83F815575199}
DEFINE_GUID(CLSID_TestObj,
0x36a976f4, 0x698b, 0x4b50, 0xbe, 0x2c, 0x83, 0xf8, 0x15, 0x57, 0x51, 0x99);
long g_nComObjsInUse;
STDAPI DllGetClassObject(const CLSID& clsid,
const IID& iid,
void** ppv)
{
OutputDebugStringW(L"DllGetClassObject");
if (IsEqualGUID(clsid, CLSID_TestObj))
{
TestClassFactory *pAddFact = new TestClassFactory();
if (pAddFact == NULL)
return E_OUTOFMEMORY;
else
{
return pAddFact->QueryInterface(iid, ppv);
}
}
return CLASS_E_CLASSNOTAVAILABLE;
}
STDAPI DllCanUnloadNow()
{
OutputDebugStringW(L"DllCanUnloadNow");
if (g_nComObjsInUse == 0)
{
return S_OK;
}
else
{
return S_FALSE;
}
}
TestObj.h
extern long g_nComObjsInUse;
class CTestObj :
public ITaskHandler
{
public:
CTestObj();
virtual ~CTestObj();
//IUnknown interface
HRESULT __stdcall QueryInterface( REFIID riid,void **ppObj) override;
ULONG __stdcall AddRef() override;
ULONG __stdcall Release() override;
//IAdd interface
HRESULT __stdcall Start(IUnknown* handler, BSTR data) override;
HRESULT __stdcall Stop(HRESULT* retCode) override;
HRESULT __stdcall Pause() override;
HRESULT __stdcall Resume() override;
private:
long m_nRefCount; //for managing the reference count
};
TestObj.cpp
HRESULT __stdcall CTestObj::Start(IUnknown* handler, BSTR data)
{
OutputDebugStringW(L"Start");
return S_OK;
}
HRESULT __stdcall CTestObj::Stop(HRESULT* retCode)
{
OutputDebugStringW(L"Stop");
return S_OK;
}
HRESULT __stdcall CTestObj::Pause()
{
OutputDebugStringW(L"Pause");
return S_OK;
}
HRESULT __stdcall CTestObj::Resume()
{
OutputDebugStringW(L"Resume");
return S_OK;
}
CTestObj::CTestObj()
{
InterlockedIncrement(&g_nComObjsInUse);
}
CTestObj::~CTestObj()
{
InterlockedDecrement(&g_nComObjsInUse);
}
HRESULT __stdcall CTestObj::QueryInterface(REFIID riid, void **ppObj)
{
OutputDebugStringW(L"QueryInterface");
if (IsEqualGUID(riid,IID_IUnknown))
{
*ppObj = static_cast<IUnknown*>(this);
AddRef();
return S_OK;
}
if (IsEqualGUID(riid,IID_ITaskHandler))
{
*ppObj = static_cast<ITaskHandler*>(this);
AddRef();
return S_OK;
}
*ppObj = NULL;
return E_NOINTERFACE;
}
ULONG __stdcall CTestObj::AddRef()
{
OutputDebugStringW(L"AddRef");
return InterlockedIncrement(&m_nRefCount);
}
ULONG __stdcall CTestObj::Release()
{
OutputDebugStringW(L"Release");
long nRefCount = 0;
nRefCount = InterlockedDecrement(&m_nRefCount);
if (nRefCount == 0) delete this;
return nRefCount;
}
TestClassFactory.h
class TestClassFactory : IClassFactory
{
public:
TestClassFactory();
~TestClassFactory();
HRESULT __stdcall QueryInterface(
REFIID riid,
void **ppObj) override;
ULONG __stdcall AddRef() override;
ULONG __stdcall Release() override;
HRESULT __stdcall CreateInstance(IUnknown* pUnknownOuter,
const IID& iid,
void** ppv) override;
HRESULT __stdcall LockServer(BOOL bLock) override;
private:
long m_nRefCount;
};
TestClassFactory.cpp
extern long g_nComObjsInUse;
TestClassFactory::TestClassFactory()
{
InterlockedIncrement(&g_nComObjsInUse);
}
TestClassFactory::~TestClassFactory()
{
InterlockedDecrement(&g_nComObjsInUse);
}
HRESULT __stdcall TestClassFactory::CreateInstance(IUnknown* pUnknownOuter,
const IID& iid,
void** ppv)
{
OutputDebugStringW(L"CreateInstance");
if (pUnknownOuter != NULL)
{
return CLASS_E_NOAGGREGATION;
}
CTestObj* pObject = new CTestObj();
if (pObject == NULL)
{
return E_OUTOFMEMORY;
}
return pObject->QueryInterface(iid, ppv);
}
HRESULT __stdcall TestClassFactory::LockServer(BOOL bLock)
{
OutputDebugStringW(L"LockServer");
return E_NOTIMPL;
}
HRESULT __stdcall TestClassFactory::QueryInterface(
REFIID riid,
void **ppObj)
{
OutputDebugStringW(L"QueryInterface");
if (IsEqualGUID(riid, IID_IUnknown))
{
*ppObj = static_cast<IUnknown*>(this);
AddRef();
return S_OK;
}
if (IsEqualGUID(riid, IID_IClassFactory))
{
*ppObj = static_cast<IClassFactory*>(this);
AddRef();
return S_OK;
}
*ppObj = NULL;
return E_NOINTERFACE;
}
ULONG __stdcall TestClassFactory::AddRef()
{
OutputDebugStringW(L"AddRef");
return InterlockedIncrement(&m_nRefCount);
}
ULONG __stdcall TestClassFactory::Release()
{
OutputDebugStringW(L"Release");
long nRefCount = 0;
nRefCount = InterlockedDecrement(&m_nRefCount);
if (nRefCount == 0) delete this;
return nRefCount;
}
Task Registration XML
<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.6" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
<RegistrationInfo>
<Date>2006-11-10T14:29:55.5851926</Date>
<Author>a</Author>
<URI>\b</URI>
<SecurityDescriptor>D:(A;;FA;;;BA)(A;;FA;;;SY)(A;;FRFX;;;WD)</SecurityDescriptor>
</RegistrationInfo>
<Triggers>
<LogonTrigger id="06b3f632-87ad-4ac0-9737-48ea5ddbaf11">
<Enabled>false</Enabled>
<Delay>PT1H</Delay>
</LogonTrigger>
</Triggers>
<Principals>
<Principal id="AllUsers">
<GroupId>S-1-1-0</GroupId>
<RunLevel>LeastPrivilege</RunLevel>
</Principal>
</Principals>
<Settings>
<MultipleInstancesPolicy>Parallel</MultipleInstancesPolicy>
<DisallowStartIfOnBatteries>true</DisallowStartIfOnBatteries>
<StopIfGoingOnBatteries>true</StopIfGoingOnBatteries>
<AllowHardTerminate>false</AllowHardTerminate>
<StartWhenAvailable>true</StartWhenAvailable>
<RunOnlyIfNetworkAvailable>true</RunOnlyIfNetworkAvailable>
<IdleSettings>
<StopOnIdleEnd>true</StopOnIdleEnd>
<RestartOnIdle>false</RestartOnIdle>
</IdleSettings>
<AllowStartOnDemand>true</AllowStartOnDemand>
<Enabled>true</Enabled>
<Hidden>false</Hidden>
<RunOnlyIfIdle>false</RunOnlyIfIdle>
<DisallowStartOnRemoteAppSession>false</DisallowStartOnRemoteAppSession>
<UseUnifiedSchedulingEngine>true</UseUnifiedSchedulingEngine>
<WakeToRun>false</WakeToRun>
<ExecutionTimeLimit>PT1H</ExecutionTimeLimit>
<Priority>7</Priority>
</Settings>
<Actions Context="AllUsers">
<ComHandler>
<ClassId>{36A976F4-698B-4B50-BE2C-83F815575199}</ClassId>
</ComHandler>
</Actions>
</Task>
推荐阅读
- python - 替换或修剪由 Selenium 和 Python 抓取的 Google 表格中的字符
- firebase - 无法升级到 Firebase Blaze 计划
- swagger - CDK 部署后 CloudFormation 模板未更新
- javascript - 如何在不刷新 Django Javascript 中每个帖子的页面的情况下打开编辑部分?
- python - 如何使用发布请求 python 将照片上传到 Instagram
- c# - 无法更新实体框架条目
- java - REST:具有不同条件参数的资源的相同端点
- swiftui - SwiftUi - 为 MapKit 构建微调器
- python - 如何在 Swagger UI 中对 FastAPI 端点进行分组?
- java - 优先级队列作为最大优先级队列未按预期工作