尝试实现任务计划程序COM处理程序

时间:2019-06-08 11:53:49

标签: c winapi com taskscheduler

ITaskHandler::Start的原型如下:

HRESULT ( STDMETHODCALLTYPE Start )( 
            __RPC__in ITaskHandler * This,
            /* [in] */ __RPC__in_opt IUnknown *pHandlerServices,
            /* [in] */ __RPC__in BSTR data)

pHandlerServices是可选的-如果我得到NULL(这是我的情况)-如何通知任务调度程序我已经完成任务。

好-这是我实现的类QueryInterface的处理,以始终返回相同的对象思维-ITaskHandler将立即被查询。但是,情况并非如此-第一个查询是针对IClassFactory的,并且CreateInstance的函数签名具有第二个参数pUnkOuter NULL,它与我的{{ 1}}。不过,ITaskHandler_Start被标记为可选是很奇怪的。

这是我当前对处理程序的实现,该实现仍不起作用(上次运行结果是不支持此类接口(0x80004002))-从未查询过我的接口pHandlerServices。我什至到目前为止都实现了ITaskHandler,但是没有运气的别名(从未调用过ICallFactory)-这是代码:

CreateCall

2 个答案:

答案 0 :(得分:1)

@HansPassant指出的关键是,任务计划程序将仅在进程外运行COM对象。为此,您需要注册COM对象才能使用系统提供的DllSurrogate。

这是我的示例的COM注册键

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

有效的示例代码(对不起的C ++)-它基本上只是默认的COM实现,并带有ITaskHandler。它至少调用Start。如果要使用自己的代码,建议您在担心任务计划程序之前,先在简单的测试程序中测试COM对象的加载。

// {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;
}

任务注册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>

答案 1 :(得分:1)

有3个问题:

1)AddRef和stub必须具有1个参数,例如IUnknown * This(或LPVOID This)。函数是否为__cdecl无关紧要,但COM仅与__stdcall函数一起使用。 系统期望函数从堆栈中自己清除参数。

2)AddRef必须至少不返回0,并且存根函数必须返回S_OK或0。也许这不是大问题。

3)调查显示针对类工厂的系统查询IUnknown,也许它执行QueryInterface而不是AddRef。但是无论如何,为每个IUnknown查询返回taskhandler都是不正确的。最好返回“工厂”。但是最好在IUnknown上返回This,无论如何,每个接口都是IUnknown的后代。

奖金问题,如果在Unicode消息下编译会显示奇怪的中文文本,因为需要使文本参数成为Unicode,或者显式使用MessageBoxA

#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);
}