我正在努力从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
中的定义显示IExecAction
是IAction
的派生类。这很好用:
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 : IAction
和IEmailAction : IAction
都从IAction
继承。并且此强制转换(如果考虑到这3个接口的布局)会为您提供pExecAction
和pEmailAction
相等的二进制值。但是pExecAction
的二进制值不能与pEmailAction
相同。一定是assert((void*)pEmailAction != (void*)pExecAction);
为什么呢?因为
pEmailAction
和pExecAction
在vtable中的同一位置具有不同的虚函数。例如,在IExecAction
表中的第10位必须是指向get_Path
方法的指针。在IEmailAction
表的第十个位置的另一侧,必须指向get_Server
方法的指针。如果(void*)pEmailAction == (void*)pExecAction
-它们将具有与vtable相同的指针。但是指向哪个功能的指针-get_Path
或get_Server
将位于第10位?结果指向这两个接口的指针不能相同(指向相同的内存)。因此,这里的一个最小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个不同vtable的指针。当IAction*
时,我们选择return或第一个或第二个vtable指针。两者都是正确的。什么指针返回实现-我们不知道(实现IExecAction
,IEmailAction
的实际类的布局对我们来说是未知的)