Попытка реализовать планировщик задач COM-обработчик - PullRequest
0 голосов
/ 08 июня 2019

Прототип 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, который перекрывается со вторым параметром моей реализации 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);}

Ответы [ 2 ]

1 голос
/ 13 июня 2019

Существует 3 проблемы:

1) AddRef и заглушка должны иметь 1 параметр, например IUnknown * This (или LPVOID This).Не имеет значения, если бы ваша функция была __cdecl, но COM работает только с функциями __stdcall.И система ожидает, что функции очистят параметры из стека самостоятельно.

2) AddRef должен возвращать не 0, по крайней мере, а функция-заглушка должна возвращать S_OK или 0. Возможно, это не большая проблема ..

3) Investigations показывает системные запросы IUnknown для фабрики классов, возможно, он выполняет QueryInterface вместо AddRef.Но в любом случае не правильно возвращать обработчик задач для каждого запроса IUnknown.Еще лучше вернуть «фабрику».Однако лучше вернуть это в IUnknown, в любом случае каждый интерфейс является потомком 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);
}
1 голос
/ 12 июня 2019

Ключ к этому, как отмечает @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. Это как минимум вызывает Старт. Если вы хотите использовать свой собственный код, я предлагаю вам проверить загрузки 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>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...