在C++中铸造COM类型

本文关键字:COM 类型 C++ | 更新日期: 2023-09-27 17:58:30

我有以下COM类型:ProjectContainerItemNodeProject具有带有Append函数的集合属性,该函数接受ContainerItem s。

在C#中,使用类型库,我可以向Append函数发送一个Node对象,并且库按预期工作:

var prj = new Project();
var node = new Node();
prj.collection.Append(node);

在C++中,我尝试了一个直接的指针强制转换,期望这就是C#所做的,但最终出现了一个错误:

ProjectPtr prj;
prj.CreateInstance(__uuidof(Project));
NodePtr node;
node.CreateInstance(__uuidof(Node));
prj->collection->Append((ContainerItem**)&node.GetInterfacePtr());

在C++中,有没有一种特定的方法来处理这些类型的COM指针类型转换?我错过了什么?

在C++中铸造COM类型

COM强制转换是使用QueryInterface()方法完成的。查询对象是否支持接口(基于GUID),如果支持接口,则会增加内部引用计数器(请参阅AddRef()),并返回指向接口的指针。MSDN提供了更多关于内部工作的详细信息。

C++不像C#那样直接支持"COM强制转换"的代码生成,但它的实现足够简单。

struct bad_com_cast : std::runtime_error {
    bad_com_cast() : std::runtime_error("COM interface not supported") {}
};
template <class To, class From>
To* qi_cast(From* iunknown)
{
    HRESULT hr  = S_OK;
    To* ptr = NULL;
    if (iunknown) {
        hr = iunknown->QueryInterface(__uuidof(To), (void**)(&ptr));
        if (hr != S_OK) {
            throw bad_com_cast(); // or return NULL
        }
    }
    return ptr;
}

使用上面的"cast",可以按如下方式实现示例;

ContainerItem* ci = qi_cast<ContainerItem>(node);
prj->collection->Append(&ci);

如果正在使用ATL库,则可以直接使用ATL::CComQIPtr<>来获得等效语义;

auto ci = CComQIPtr<ContainerItem>(node);
if (ci) {
    // ...
}

就像@HansPassant评论的那样,我不得不使用QueryInterface函数:

ContainerItem* ci = nullptr;
node.QueryInterface(__uuidof(ContainerItem), ci);
prj->collection->Append(&ci);