COM - 在 C++ 中使用 IEnumerable
COM - use IEnumerable in C++
我正在尝试公开一个 returns IEnumerable 的方法,但我无法通过 C++ 代码使用它。我阅读了 this 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
。
我错过了什么?
谢谢,
尝试使您的 DbReader.GetUsers(...)
实现 public 以便它正确地实现 IDbReader
接口。例如:
internal class DbReader : IDbReader
{
public IEnumerable GetUsers() => new List<string>() { "User1", "User2" };
}
除了 C# 代码中的 using System.Collections.Generic
(对于 List<T>
)之外,还要确保您是 using System.Collections
(对于 IEnumerable
)。
不太清楚您是如何获得该 C++ 代码的,您通常会使用 #import 指令导入 C# 项目生成的类型库。应该类似于:
#import "c:\windows\microsoft.net\framework\v2.0.50727\mscorlib.tlb"
using namespace mscorlib;
#import "CSharpLibrary.tlb"
using namespace CSharpLibrary;
假设 C# 项目名称是 "CSharpLibrary"。这会生成从 _com_ptr_t
派生的智能指针类型,它们与附加了 Ptr
的接口同名。优点是引用计数得到处理,接口函数调用失败报告为 C++ 异常,而不是难以处理的 HRESULT 错误代码。
.NET 类型库导出器将 System.Collections.IEnumerator 转换为 IEnumVARIANT*。其他细节确实很难从不稳定的编译错误消息中诊断出来,但可以从 IntelliSense 中看到。所以基本代码应该是这样的:
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() 的额外调用,如链接问题中所述。您可以在 this MSDN article.
中阅读更多关于 C++ 编译器支持 类 的信息
我正在尝试公开一个 returns IEnumerable 的方法,但我无法通过 C++ 代码使用它。我阅读了 this 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
。
我错过了什么?
谢谢,
尝试使您的 DbReader.GetUsers(...)
实现 public 以便它正确地实现 IDbReader
接口。例如:
internal class DbReader : IDbReader
{
public IEnumerable GetUsers() => new List<string>() { "User1", "User2" };
}
除了 C# 代码中的 using System.Collections.Generic
(对于 List<T>
)之外,还要确保您是 using System.Collections
(对于 IEnumerable
)。
不太清楚您是如何获得该 C++ 代码的,您通常会使用 #import 指令导入 C# 项目生成的类型库。应该类似于:
#import "c:\windows\microsoft.net\framework\v2.0.50727\mscorlib.tlb"
using namespace mscorlib;
#import "CSharpLibrary.tlb"
using namespace CSharpLibrary;
假设 C# 项目名称是 "CSharpLibrary"。这会生成从 _com_ptr_t
派生的智能指针类型,它们与附加了 Ptr
的接口同名。优点是引用计数得到处理,接口函数调用失败报告为 C++ 异常,而不是难以处理的 HRESULT 错误代码。
.NET 类型库导出器将 System.Collections.IEnumerator 转换为 IEnumVARIANT*。其他细节确实很难从不稳定的编译错误消息中诊断出来,但可以从 IntelliSense 中看到。所以基本代码应该是这样的:
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() 的额外调用,如链接问题中所述。您可以在 this MSDN article.
中阅读更多关于 C++ 编译器支持 类 的信息