我正在努力从Windows Task Scheduler检索一些信息。
MSDN表示有几种类型的动作。我想分别处理。我试过了:

IAction* pAction = NULL;
pActionCollection->get_Item(_variant_t(i), &pAction);
if (IExecAction* pExecAction = dynamic_cast<IExecAction*>(pAction)) { /*my work...*/ }
if (IComHandlerAction* pComHandlerAction = dynamic_cast<IComHandlerAction*>(pAction)) { /*my work...*/ }
if (IEmailAction* pEmailAction = dynamic_cast<IEmailAction*>(pAction)) { /*my work...*/ }
if (IShowMessageAction* pShowMessageAction = dynamic_cast<IShowMessageAction*>(pAction)) { /*my work...*/ }


但是此程序在第一个dynamic_cast处引发异常。

Exception thrown at 0x00007FFB516365A5 (vcruntime140d.dll) in myProgram.exe: 0xC0000005: Access violation reading location 0x00000130BAFEDB04.


taskschd.h中的定义显示IExecActionIAction的派生类。

这很好用:

if (IExecAction* pExecAction = ((IExecAction*)pAction)) { /*my work...*/ }


但是,如果我想进行某种类型检查怎么办?
如何正确使用?

最佳答案

为了从同一对象上的另一个com接口获取com接口的指针,我们只需要使用QueryInterface方法,该方法始终由任何接口实现。因此,您需要的代码应为下一个:

    IAction* pAction;
    IExecAction* pExecAction;
    IEmailAction* pEmailAction;

    HRESULT hr;

    if (SUCCEEDED(hr = pAction->QueryInterface(IID_PPV_ARGS(&pExecAction))))
    {
        // use pExecAction
        pExecAction->Release();
    }

    if (SUCCEEDED(hr = pAction->QueryInterface(IID_PPV_ARGS(&pEmailAction))))
    {
        // use pExecAction
        pEmailAction->Release();
    }


即使一个接口从另一个接口继承使用c / c ++强制转换总是错误的。例如

    pExecAction = static_cast<IExecAction*>(pAction);
    pEmailAction = static_cast<IEmailAction*>(pAction);


此代码从c ++语法是正确的,因为IExecAction : IActionIEmailAction : IAction都从IAction继承。并且此强制转换(如果考虑到这3个接口的布局)会为您提供pExecActionpEmailAction相等的二进制值。但是pExecAction的二进制值不能与pEmailAction相同。一定是

assert((void*)pEmailAction != (void*)pExecAction);


为什么呢?因为pEmailActionpExecAction在vtable中的同一位置具有不同的虚函数。例如,在IExecAction表中的第10位必须是指向get_Path方法的指针。在IEmailAction表的第十个位置的另一侧,必须指向get_Server方法的指针。如果(void*)pEmailAction == (void*)pExecAction-它们将具有与vtable相同的指针。但是指向哪个功能的指针-get_Pathget_Server将位于第10位?结果指向这两个接口的指针不能相同(指向相同的内存)。因此,这里的一个最小static_cast(可能是两个)都给出了错误的结果。为了了解QueryInterface的工作原理以及为什么指向pExecActionpEmailAction的指针会有所不同-我们需要寻找实现方法。接口的实现-这是一些类,该类通常从所有这些接口继承并像这样实现:

class CAction : IExecAction, IEmailAction
{
    virtual ULONG STDMETHODCALLTYPE AddRef( );

    virtual HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void **ppvObject)
    {
        PVOID pvObject;

        if (riid == __uuidof(IAction))
        {
            pvObject = static_cast<IExecAction*>(this);
            // or can be
            pvObject = static_cast<IEmailAction*>(this);
        }
        else if (riid == __uuidof(IExecAction))
        {
            pvObject = static_cast<IExecAction*>(this);
        }
        else if (riid == __uuidof(IEmailAction))
        {
            pvObject = static_cast<IExecAction*>(this);
        }
        else
        {
            *ppvObject = 0;
            return E_NOINTERFACE;
        }

        *ppvObject = pvObject;
        AddRef();
        return S_OK;
    }
};


看起来static_cast<IExecAction*>(this);总是会给出另一个二进制值,与static_cast<IEmailAction*>(this);进行比较-CAction将包含2个不同的vtables-一个用于IExecAction,另一个用于IEmailAction。它们具有相同的初始部分(9个条目),但随后有所不同。 static_cast<IExecAction*>(this);static_cast<IEmailAction*>(this);返回2个(总是)指向这2个不同vtable的指针。当IAction*时,我们选择return或第一个或第二个vtable指针。两者都是正确的。什么指针返回实现-我们不知道(实现IExecActionIEmailAction的实际类的布局对我们来说是未知的)

07-24 09:44