COM - 在C++中使用 IEnumerable

COM - use IEnumerable in C++

本文关键字:IEnumerable C++ COM      更新时间:2023-10-16

我正在尝试公开一个返回IEnumerable的方法,但我无法通过C++代码使用它。我读了这个 SO 问题,但它是关于IEnumerator而不是IEnumerable.

[ComVisible(true),
Guid("myguid")]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface IDbReader
{
IEnumerable GetUsers();
}
[ComVisible(true),
Guid("myguid2")]
[ProgId("MyDbReader.DbReader")]
internal class DbReader : IDbReader
{
IEnumerable GetUsers() => new List<string>() { "User1", "User2" };
}

在C++:

#import "MyDbReader.tlb" named_guids raw_interfaces_only
...
CComPtr<IDbReader> myreader; // created through a factory
CComPtr<IEnumerable> users;
myreader->GetUsers(&users);
users->GetEnumerator(); // The compiler says: pointer to incomplete class is not allowed 

此外,编译器看到的是IDbReader::IEnumerable,而不是使用标准IEnumerable

我错过了什么?

谢谢

目前还不清楚您是如何得出C++代码的,您通常会使用 #import 指令导入 C# 项目生成的类型库。 它应该类似于:

#import "c:windowsmicrosoft.netframeworkv2.0.50727mscorlib.tlb"
using namespace mscorlib;
#import "CSharpLibrary.tlb"
using namespace CSharpLibrary;

假设 C# 项目名称为"CSharpLibrary"。 这将生成派生自_com_ptr_t的智能指针类型,它们与附加了Ptr接口具有相同的名称。 优点是可以处理引用计数,并且接口函数调用失败报告为C++异常,而不是难以处理的 HRESULT 错误代码。

额外的细节,实际上很难从不稳定的编译错误消息中诊断,但从IntelliSense中可以看到,.NET类型库导出器将System.Collections.IEnumerator转换为IEnumVARIANT*。 所以基本代码应该看起来像:

try {
IEnumerablePtr users = myreader->GetUsers();
IEnumVARIANTPtr enumusers = users->GetEnumerator();
_variant_t vuser;
while (SUCCEEDED(enumusers->Next(1, &vuser, NULL))) {
BSTR user = vuser.bstrVal;
// etc...
}
}
catch (_com_error& ex) {
// Handle or report runtime error...
}

未经测试,应该在球场上。 可以通过让 C# 接口公开 IEnumerator 而不是 IEnumerable,避免对 GetEnumerator() 的额外调用,如链接的问题中所述。 您可以在这篇 MSDN 文章中阅读有关正在使用的C++编译器支持类的详细信息。

尝试公开DbReader.GetUsers(...)实现,以便它正确实现IDbReader接口。 例如:

internal class DbReader : IDbReader
{
public IEnumerable GetUsers() => new List<string>() { "User1", "User2" };
}

此外,请确保除了 C# 代码中的using System.Collections.Generic(对于List<T>)之外,还using System.Collections(对于IEnumerable)。