C ++ кастинг Windows IAction - PullRequest
       13

C ++ кастинг Windows IAction

0 голосов
/ 25 сентября 2018

Я работаю над получением некоторой информации из планировщика задач Windows. 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 показывает, что IExecAction является производным классом от IAction.

Это хорошо работает:

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

Но что еслиЯ хочу сделать проверку типов?Как я могу использовать это правильно?

1 Ответ

0 голосов
/ 25 сентября 2018

для получения указателя 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 ++ cast всегда неверно.например

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

этот код корректен из синтаксиса c ++, потому что оба IExecAction : IAction и IEmailAction : IAction наследуются от IAction.и это приведение (если принять во внимание расположение этих 3 интерфейсов) даст вам равные двоичные значения для pExecAction и pEmailAction.но pExecAction не может иметь такое же двоичное значение, как pEmailAction.должно быть

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

почему?потому что pEmailAction и pExecAction имеют разные виртуальные функции в одной позиции в vtable .например на 10-й позиции в таблице IExecAction должен быть указатель на метод get_Path.с другой стороны на 10-ю позицию в таблице IEmailAction должен быть указатель на метод get_Server.если (void*)pEmailAction == (void*)pExecAction - они будут иметь одинаковые указатели на vtable.а указатель на какую функцию - get_Path или get_Server будет в 10-й позиции?в результате указатель на эти 2 интерфейса не может быть одинаковым (указывать на одну и ту же память).так как минимум один static_cast здесь (может быть и то и другое) дают неверный результат.Чтобы понять, как работает QueryInterface и почему указатели на pExecAction и pEmailAction будут разными - нам нужно искать реализацию.реализация интерфейсов - это некоторый класс, который наследует (обычно) от всех этих интерфейсов и реализует его следующим образом:

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 разные таблицы.когда для IAction* мы выбираем return или первый или второй vtable указатель.оба будут правильными.какой указатель возвращает реализацию - мы не можем знать (расположение фактического класса, который реализует IExecAction, IEmailAction нам неизвестно)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...