Files
Obsidian-Main/03. 資料收集/Programming/COM/20210726 - COM Interface.md
Awin Huang 2f312a4575 vault backup: 2022-09-26 18:39:43
Affected files:
.obsidian/workspace
03. 資料收集/HTTP Server/Nginx.md
03. 資料收集/Hobby/RC.md
03. 資料收集/Hobby/模型/Traxxas Sledge.md
03. 資料收集/Hobby/模型/舊化作例.md
03. 資料收集/Hobby/軍武/虎式.md
03. 資料收集/Programming/COM/20210726 - COM Interface.md
03. 資料收集/Programming/DB/MySQL.md
03. 資料收集/Programming/DB/sqlite.md
03. 資料收集/Programming/Design Pattern.md
03. 資料收集/Programming/FFMPEG/00. Introduction.md
03. 資料收集/Programming/FFMPEG/01. Setup.md
03. 資料收集/Programming/FFMpeg.md
03. 資料收集/Programming/Flask.md
03. 資料收集/Programming/Media Foundation/20210604 - Windows media foundation.md
03. 資料收集/Programming/OpenCV.md
03. 資料收集/Programming/OpenGL.md
03. 資料收集/Programming/Python/argparse.ArgumentParser.md
03. 資料收集/Programming/Python/decorator.md
03. 資料收集/Programming/Python/logging.md
03. 資料收集/Programming/Python/opencv.md
03. 資料收集/Programming/Python/subprocess.md
03. 資料收集/Programming/Python/threading.md
03. 資料收集/Programming/Python/tkinter.md
03. 資料收集/Programming/Python/檢測工具.md
03. 資料收集/Programming/QT/Dropdown button.md
03. 資料收集/Programming/QT/QVariant.md
03. 資料收集/Programming/QT/Qt.md
03. 資料收集/Programming/Qt.md
03. 資料收集/Programming/UML.md
03. 資料收集/Programming/演算法.md
03. 資料收集/架站/03. Trojan.md
03. 資料收集/架站/Gitea.md
03. 資料收集/架站/HTTP Server/Apache.md
03. 資料收集/架站/HTTP Server/Nginx/Reverse Proxy(Layer4).md
03. 資料收集/架站/Pelican blog.md
03. 資料收集/架站/Proxmox VE.md
03. 資料收集/架站/SWAG Reverse proxy.md
03. 資料收集/架站/Storj.md
03. 資料收集/架站/Trojan.md
03. 資料收集/科技/802.11.md
03. 資料收集/科技/HDR Sensor.md
03. 資料收集/科技/量子電腦.md
03. 資料收集/科技/鋰電池.md
03. 資料收集/軟體工具/IPFS.md
03. 資料收集/軟體工具/MkDocs.md
03. 資料收集/軟體工具/Obsidian.md
03. 資料收集/軟體工具/docker.md
03. 資料收集/軟體工具/git/apply.md
03. 資料收集/軟體工具/git/submodule.md
2022-09-26 18:39:43 +08:00

2.0 KiB
Raw Blame History

IID_PPV_ARGS Marco

我们已经知道 CoCreateInstance 和 QueryInterface 函数的最后一个参数都是 void** 类型,这就会带来潜在的类型不匹配的安全问题,考虑下面的代码段:

// Wrong!

IFileOpenDialog *pFileOpen;

hr = CoCreateInstance(
    __uuidof(FileOpenDialog),
    NULL,
    CLSCTX_ALL,
    __uuidof(IFileDialogCustomize),       // The IID does not match the pointer type!
    reinterpret_cast<void**>(&pFileOpen)  // Coerce to void**.
    );

在这段代码中,想要获取的接口类型是 IFileDialogCustomize但是传入的却是一个 IFileOpenDialog 类型的指针变量。reinterpret_cast 表达式会绕过 C++ 类型系统的检测,所以编译器并不会主动抛出错误。

如果这段代码被运行,好的结果是函数返回失败,无法找到该接口;而坏的结果是,函数会返回成功,而你将获得一个类型结果不匹配的指针。换句话说,这个指针类型没有匹配到内存中真实的虚函数表,如果你所想不会有啥好事发生。

一个虚函数表virtual method table是一个函数指针表它被用来实现 COM 组件运行期间的动态绑定。这种方式也是大多数 C++ 编译器用来实现动态绑定(多态)的方法。

IID_PPV_ARGS 宏是一个帮助你避免类型错误的方法,使用方法如下:

__uuidof(IFileDialogCustomize), reinterpret_cast<void**>(&pFileOpen)

替换为

IID_PPV_ARGS(&pFileOpen)

这个宏会自动将 __uuidof(IFileOpenDialog) 参数插入。它可以保证你返回的接口指针类型的正确性。这是修改后的代码:

// Right.
IFileOpenDialog *pFileOpen;
hr = CoCreateInstance(__uuidof(FileOpenDialog), NULL, CLSCTX_ALL,
    IID_PPV_ARGS(&pFileOpen));

你可以在 QueryInterface 函数中使用一样的宏:

IFileDialogCustomize *pCustom;
hr = pFileOpen->QueryInterface(IID_PPV_ARGS(&pCustom));

參考資料