3

如果这是一个愚蠢的问题,我深表歉意,但我不清楚为什么 COM 指针参数通常被转换为(void**)而不是(IUnknown**). 然后有时IUnknown实际上会使用指针,例如 with IObjectWithSite::SetSite。谁能解释一下?

4

3 回答 3

2

在“get-type”接口方法(如IObjectWithSite::QueryInterface, IObjectWithSite::GetSite, IMoniker::BindToObject, 等)中,因为它不会改变任何东西,所以无论如何你都必须强制转换,除非你确实需要IUnknown*引用,但你已经有了它,因为 . ..您正在使用它(根据COM 规则IUknown*,引用始终是相同的指针)。

IObjectWithSite::SetSite是一种“set-type”的方法,所以给你一个IUnknown*参考更有意义。

在像CoCreateInstanceCoGetObject这样的静态方法中,这可能更具争议性, 我认为他们可以把它们放在IUnknown**那里而不是,void**但是它们会有两种不同的风格。而且您将无法使用旧的IID_PPV_ARGS宏,它非常实用,建议作为编码实践来避免类型转换错误。

我建议您从 Don Box 获取权威的“Essential COM”副本,并阅读到第 60 页(至少 :-)。

于 2019-12-22T11:15:46.597 回答
0

我猜您正在谈论通过指针“返回”值的输出参数,例如:

IUnknown *u;
QueryInterface(IID_IUnknown, (void **)&u);

IDispatch *d;
QueryInterface(IID_IDispatch, (void **)&d);

注意:如果您不熟悉使用指针传递来“返回”值的概念,请参阅此处。


的原型QueryInterface是:

HRESULT QueryInterface(REFIID iid, void **ptr);

该参数&u已经具有类型IUnknown ** (因此没有理由将其转换为它已经存在的类型,正如您似乎建议的那样)。强制转换是将类型更改为函数参数的预期类型。

在标准 C++ 中,上面的代码实际上是未定义的行为(严格的别名冲突——你不能假装指针指向不同的类型对象,而不是实际指向的对象)。这个函数的正确用法是:

void *temp;
QueryInterface(IID_IFoo, &temp);
IFoo *foo = static_cast<IFoo *>(temp);

但是,Microsoft 编译器支持具有 C 样式转换的版本。它对内存位置进行相同的更改,就好像接口指针被声明为void *而不是它的真实类型一样。


为什么QueryInterfacevoid **而不取IUnknown **?好吧,如果您特别要求避免强制转换,那会很好IID_IUnknown,但是任何其他接口都需要 C 风格的强制转换。它可能会引起混淆,因为(对于其他接口)返回的指针不一定是有效值IUnknown *

在 C++ 编程中,您可以(也许应该)使用执行所有正确类型操作的模板包装类。Windows API 调用与 C 兼容,因此它们不能包含强类型泛型。

注意。所有调用都QueryInterface应该检查返回值,为简洁起见,我在这里省略了。

于 2019-12-21T23:44:26.393 回答
0

重要的外卖

这与传统或便利无关。函数签名是 COM 基础允许它工作的结果。它们必须按原样键入。如果您不想通读此答案,以下是重要的要点: C++ 中强制转换的道德等价物是QueryInterfaceCOM 中的调用。只有在实现 COM 对象的QueryInterface.

细节

COM 中期望 a void*(而不是 a IUnknown*)的地址作为输出参数的函数签名可以返回任何接口类型。如果IUnknown**在假设的实现中将其更改为 an,则使用 COM 将变得不切实际或完全不可能。

让我们进行 2 个思想实验,从使 COM 无法使用的那个开始:

让我们假设CoCreateInstance将返回 aIUnknown*而不是通过 a 请求的真实接口void*。在这种情况下,客户端必须立即调用QueryInterface返回IUnknown*的接口以接收他们要求的接口指针1。这不切实际2

这立即导致无法解决的实验。让我们假设QueryInterface返回一个IUnknown*. 要获得真正的接口指针,客户端需要调用QueryInterface. 但这只会返回一个IUnknown*!此时,COM 的可消耗接口表面已折叠为单个接口,IUnknown.

每当 COM 返回指向接口的指针时,它必须返回指向最终类型的指针。唯一匹配所有接口指针的编程语言类型确实是void*. 3 这应该解释为什么需要输入输出参数void**而不是IUnknown**.

另一方面,对于IObjectWithSite::SetSiteIUnknown* ,是一个input。该接口仍然接受任何COM 接口,但它需要作为指向 (identity-comparable)IUnknown接口的指针传递。


1 COM 不要求实现特定的对象布局。相反,它将对接口指针的请求委托给各自的QueryInterface实现。

2 即使忽略了调用的即时需要,以Release()IUnknown碰撞的引用计数作为QI调用的一部分。

3 来自组件对象模型 “COM 的唯一语言要求是,代码是用一种可以创建指针结构的语言生成的,并且可以显式或隐式地通过指针调用函数。” 故意不要求使用语言级结构来实现接口继承。即使在IFoo需要实现时,与C 这样的编程语言IUnknown之间也没有关系。IFoo*IUnknown*

于 2019-12-22T14:36:51.010 回答