请求对象以获取接口

前面我们了解到,一个对象可以实现多个接口。 Common Item Dialog 对象是一个实际示例。 为了支持最典型的用法,对象实现 IFileOpenDialog 接口。 此接口定义用于显示对话框和获取有关所选文件的信息的基本方法。 但是,为了更高级地使用,对象还实现名为 IFileDialogCustomize 的接口。 程序可以使用此接口通过添加新的 UI 控件来自定义对话框的外观和行为。

回想一下,每个 COM 接口都必须直接或间接继承自 IUnknown 接口。 下图显示了公共项对话框对象的继承。

显示公共项对话框对象公开的接口的关系图

如图中所示, IFileOpenDialog 的直接上级是 IFileDialog 接口,后者又继承 了 IModalWindow。 从 IFileOpenDialogIModalWindow 的继承链上行时,接口定义越来越通用化的窗口功能。 最后, IModalWindow 接口继承 IUnknown。 Common Item Dialog 对象还实现 IFileDialogCustomize,该 IFileDialogCustomize 存在于单独的继承链中。

现在假设你有一个指向 IFileOpenDialog 接口的指针。 如何获取指向 IFileDialogCustomize 接口的 指针?

显示指向同一对象上接口的两个接口指针的关系图

仅将 IFileOpenDialog 指针强制转换为 IFileDialogCustomize 指针将不起作用。 如果没有某种形式的运行时类型信息 (RTTI) (一种高度依赖语言的功能),则无法跨继承层次结构进行“交叉转换”。

COM 方法是 要求 对象提供 IFileDialogCustomize 指针,使用第一个接口作为进入对象的管道。 这是通过从第一个接口指针调用 IUnknown::QueryInterface 方法完成的。 可以将 QueryInterface 视为 C++ 中dynamic_cast关键字 (keyword) 的独立于语言的版本。

QueryInterface 方法具有以下签名:

HRESULT QueryInterface(REFIID riid, void **ppvObject);

根据你对 CoCreateInstance 的了解,你可能能够猜测 QueryInterface 的工作原理。

  • riid 参数是标识要请求的接口的 GUID。 数据类型 REFIID 是 的 const GUID&typedef。 请注意,不需要 CLSID) (类标识符,因为对象已创建。 只需要接口标识符。
  • ppvObject 参数接收指向 接口的指针。 此参数的数据类型为 void**,原因与 CoCreateInstance 使用此数据类型的原因相同: QueryInterface 可用于查询任何 COM 接口,因此不能强类型化参数。

下面介绍如何调用 QueryInterface 以获取 IFileDialogCustomize 指针:

hr = pFileOpen->QueryInterface(IID_IFileDialogCustomize, 
    reinterpret_cast<void**>(&pCustom));
if (SUCCEEDED(hr))
{
    // Use the interface. (Not shown.)
    // ...

    pCustom->Release();
}
else
{
    // Handle the error.
}

与往常一样,检查 HRESULT 返回值,以防方法失败。 如果方法成功,则必须在使用指针后调用 Release ,如 管理对象的生存期中所述。

下一步

COM 中的内存分配