客户对组件的了解是非常有限的,为知道某个组件是否支持某个特定的接口,客户可以在运行时询问组件。即使组件不支持所需要的某个接口,客户也可以在请求失败时很好地处理这种情形
COM 中所有内容最终都起于接口、又最终归于接口。所有的 COM 接口都继承于 IUnknown 接口,该接口的定义如下:
// 该接口在头文件 unknwn.h 中定义,interface 是与 struct 绑定的宏,这样所有成员默认是 public 的,刚好符合接口的封装需求
interface IUnknown {
virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv) = 0;
virtual ULONG __stdcall AddRef() = 0;
virtual ULONG __stdcall Release() = 0;
};
因为所有的 COM 接口都继承于 IUnknown 接口,所以客户只需要一个 IUnknown 接口的指针,而且无需知道它所拥有的接口指针到底是什么类型的,就可以查询其他接口
由于所有的 COM 接口都继承了 IUnknown,所以每个接口的虚函数表(vtbl)中的前三个函数都是 QueryInterface、AddRef 和 Release。这便使得所有 COM 接口都可当做 IUnknown 接口来处理
IUnknown 包含一个名为 QueryInterface 的纯虚成员函数,客户可以通过此函数来查询某个组件是否支持某个特定的接口。若支持,则 QueryInterface 将返回一个指向此接口的指针,否则返回值将是一个错误代码。然后客户可以接着查询其他接口或将组件卸载
HRESULT __stdcall QueryInterface(const IID &iid, void **ppv);
iid 是一个接口标识符(IID)结构, 用于标识客户所需的接口,从客户的角度上来说,指定一个 IID 以使得 QueryInterface 函数利用此接口标识符来查询接口
ppv 是一个二级指针,QueryInterface 函数将存放客户所请求的接口的地址到其中
HRESULT 是具有特定格式的 32 位值,此函数返回 S_OK 或者 E_NOINTERFACE。对其返回值进行判断时应该使用 SUCCEEDED 宏或 FAILED 宏,不应该直接比较
// 使用示例
void foo(IUnknown *pI) {
// 为接口定义一个指针
IX *pIX = nullptr;
// 使用 IUnknown 的 QueryInterface 函数来查询接口 IX(接口标识符是 IID_IX)
HRESULT hr = pI->QueryInterface(IID_IX, (void **)&pIX);
// 检查返回值
if (SUCCEEDED(hr)) {
// 可以使用接口了 (比如调用某个函数)
pIX->Fx();
}
}
QueryInterface 的实现其实比较简单。它只需根据某个给定的 IID(接口标识符),返回相应接口的指针。若组件支持,函数就返回 S_OK 以及相应的接口的指针,否则函数就应返回 E_NOINTERFACE,并将指针值置为 nullptr
// 一些示例接口、实现类
interface IX : IUnknown { /*...*/ };
interface IY : IUnknown { /*...*/ };
class CA : public IX, public IY { /*...*/ };
// QueryInterface 在 CA 类上的实现
HRESULT __stdcall CA::QueryInterface(const IID &iid, void **ppv) {
if (iid == IID_IUnknown) {
// 客户需要 IUnknown 接口
*ppv = static_cast<IX *>(this);
} else if (iid == IID_IX) {
// 客户需要 IX 接口
*ppv = static_cast<IX *>(this);
} else if (iid == IID_IY) {
// 客户需要 IY 接口
*ppv = static_cast<IY *>(this);
} else {
// 不支持的接口,返回 E_NOINTERFACE,并将返回指针置为 nullptr
*ppv = nullptr;
return E_NOINTERFACE;
}
// 增加引用计数(暂时先不关心)
static_cast<IUnknown *>(*ppv)->AddRef();
// 接口受支持,返回 S_OK
return S_OK;
}
// 本代码修改自《COM技术内幕》第 35 页
#include <iostream>
#include <objbase.h>
using namespace std;
void trace(const char* msg) { cout << msg << endl; }
interface IX : IUnknown {
virtual void __stdcall Fx() = 0;
};
interface IY : IUnknown {
virtual void __stdcall Fy() = 0;
};
interface IZ : IUnknown {
virtual void __stdcall Fz() = 0;
};
extern const IID IID_IX;
extern const IID IID_IY;
extern const IID IID_IZ;
class CA : public IX, public IY {
// IUnknown implementation
virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv);
virtual ULONG __stdcall AddRef() { return 0; }
virtual ULONG __stdcall Release() { return 0; }
// Interface IX implementation
virtual void __stdcall Fx() { cout << "Fx" << endl; }
// Interface IY implementation
virtual void __stdcall Fy() { cout << "Fy" << endl; }
};
HRESULT __stdcall CA::QueryInterface(const IID& iid, void** ppv) {
if (iid == IID_IUnknown) {
trace("QueryInterface: Return pointer to IUnknown");
*ppv = static_cast<IX *>(this);
}
else if (iid == IID_IX) {
trace("QueryInterface: Return pointer to IX");
*ppv = static_cast<IX *>(this);
}
else if (iid == IID_IY) {
trace("QueryInterface: Return pointer to IY");
*ppv = static_cast<IY *>(this);
}
else {
trace("QueryInterface: Interface not supported.");
*ppv = NULL;
return E_NOINTERFACE;
}
reinterpret_cast<IUnknown *>(*ppv)->AddRef();
return S_OK;
}
IUnknown* CreateInstance() {
IUnknown* pI = static_cast<IX*>(new CA);
pI->AddRef();
return pI;
}
static const IID IID_IX =
{ 0x32bb8320, 0xb41b, 0x11cf, {0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82} };
static const IID IID_IY =
{ 0x32bb8321, 0xb41b, 0x11cf, {0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82} };
static const IID IID_IZ =
{ 0x32bb8322, 0xb41b, 0x11cf, {0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82} };
int main() {
HRESULT hr;
trace("Client: Get an IUnknown pointer.");
IUnknown* pIUnknown = CreateInstance();
trace("Client: Get interface IX.");
IX *pIX = NULL;
hr = pIUnknown->QueryInterface(IID_IX, (void**)& pIX);
if (SUCCEEDED(hr)) {
trace("Client: Succeeded getting IX.");
pIX->Fx();
}
trace("Client: Get interface IY.");
IY *pIY = NULL;
hr = pIUnknown->QueryInterface(IID_IY, (void**)& pIY);
if (SUCCEEDED(hr)) {
trace("Client: Succeeded getting IY.");
pIY->Fy();
}
trace("Client: Ask for an unsupported interface.");
IZ *pIZ = NULL;
hr = pIUnknown->QueryInterface(IID_IZ, (void**)& pIZ);
if (SUCCEEDED(hr)) {
trace("Client: Succeeded getting IZ.");
pIZ->Fz();
}
else {
trace("Client: Could not get interface IZ.");
}
trace("Client: Get interface IY from interface IX.");
IY *pIYfromIX = NULL;
hr = pIX->QueryInterface(IID_IY, (void**)& pIYfromIX);
if (SUCCEEDED(hr)) {
trace("Client: Succeeded getting IY.");
pIYfromIX->Fy();
}
trace("Client: Get interface IUnknown from IY.");
IUnknown *pIUnknownFromIY = NULL;
hr = pIX->QueryInterface(IID_IUnknown, (void**)& pIUnknownFromIY);
if (SUCCEEDED(hr)) {
cout << "Are the IUnknown pointers equal? ";
if (pIUnknownFromIY == pIUnknown) {
cout << "Yes, pIUnknownFromIY == pIUnknown." << endl;
}
else {
cout << "No, pIUnknownFromIY != pIUnknown." << endl;
}
}
delete pIUnknown;
return 0;
}
若运行将有如下输出结果
Client: Get an IUnknown pointer.
Client: Get interface IX.
QueryInterface: Return pointer to IX
Client: Succeeded getting IX.
Fx
Client: Get interface IY.
QueryInterface: Return pointer to IY
Client: Succeeded getting IY.
Fy
Client: Ask for an unsupported interface.
QueryInterface: Interface not supported.
Client: Could not get interface IZ.
Client: Get interface IY from interface IX.
QueryInterface: Return pointer to IY
Client: Succeeded getting IY.
Fy
Client: Get interface IUnknown from IY.
QueryInterface: Return pointer to IUnknown
Are the IUnknown pointers equal? Yes, pIUnknownFromIY == pIUnknown.
[1] (美) Dale Rogerson , 李忠 , 王晓波 . COM 技术内幕 - 微软组件对象模型 [M] . 北京 : 清华大学出版社 , 1991.
🖊️ 本文由 Alone Café 创作,如果您觉得本文让您有所收获,请随意赞赏 🥺
⚖️ 本文以 CC BY-NC-SA 4.0,即《署名-非商业性使用-相同方式共享 4.0 国际许可协议》进行许可
👨⚖️ 本站所发表的文章除注明转载或出处外,均为本站作者原创或翻译,转载前请务必署名并遵守上述协议
🔗 原文链接:https://alone.cafe/2019/07/com学习笔记2接口查询
📅 最后更新:2021年10月17日 Sunday 16:12
Update your browser to view this website correctly. Update my browser now