首页 > 解决方案 > 尝试实现任务调度程序 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的第二个参数pUnkOuterNULL 与我实现的第二个参数重叠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);}

标签: cwinapicomtaskscheduler

解决方案


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>

推荐阅读