c++ - C++ 铸造 Windows IAction
问题描述
我正在从 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...*/ }
但是如果我想做一些类型检查呢?我怎样才能正确使用它?
解决方案
要从同一对象上的另一个 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);
为什么 ?因为在vtablepEmailAction
的同一位置具有和具有不同的虚函数。例如,表中的第 10 位必须是指向方法的指针。从表中第 10 位的另一侧必须是指向方法的指针。if - 它们将具有相同的指向 vtable 的指针。但是指向哪个函数的指针 -或者将在第 10 个位置?结果指向这两个接口的指针不能相同(指向相同的内存)。那么这里的最小值(可能是两者)如何给出错误的结果。了解如何工作以及为什么指向和pExecAction
IExecAction
get_Path
IEmailAction
get_Server
(void*)pEmailAction == (void*)pExecAction
get_Path
get_Server
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 - 一个 forIExecAction
和一个 for IEmailAction
。它们有共同的初始部分(9 个条目),但随后又有所不同。并返回 2static_cast<IExecAction*>(this);
个static_cast<IEmailAction*>(this);
不同的(总是)指向这 2 个不同 vtable 的指针。当IAction*
我们选择返回或第一个或第二个 vtable 指针时。两者都是正确的。什么指针返回实现 - 我们不知道(实现的实际类的布局对我们IExecAction
来说IEmailAction
是未知的)
推荐阅读
- javascript - 停止 onclick 事件,一旦 onblur 事件激活
- javascript - npm express 的 spuertest 安装问题
- python - discord.py 如何使命令具有权限
- database - 气流 initdb 在 AIRFLOW_HOME 以外的目录中?
- angular - 访问 Angular @ViewChildren 的子组件参考
- html - 使用多个页脚(或页眉)元素作为相邻兄弟元素是否有效?
- flutter - arrayContains 查询的 Firestore 安全规则
- .net - Blazor 托管的 wasm 未生成范围标识符
- iis - 如何在 iis 上使用 ec2 角色和 dotnetcore
- php - PHP - 添加到多维数组将新数组添加到第一个数组元素而不是正确的元素