2017-04-10 79 views
0

我想公开一个返回IEnumerable的方法,但是我无法通过C++代码来使用它。我读this SO的问题,但它是关于IEnumerator而不是IEnumerableCOM - 在C++中使用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

我错过了什么?

感谢,

回答

1

你如何到达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错误代码。

附加的细节,确实很难诊断从wonky编译错误消息,但从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()的额外调用。您可以阅读更多关于this MSDN article中使用的C++编译器支持类的信息。

+0

不幸的是,我需要使用'#import“CSharpLibrary.tlb”named_guids raw_interfaces_only'指令(没有标志我有> 60错误)。这样智能指针不会生成,但您仍然可以使用CComPtr 等。我将更新问题。 – peval27

+0

顺便说一句,我错过了'使用命名空间mscorlib;'并且出于任何原因它无法正常工作。这是一个奇怪的错误消息,不能帮助用户解决问题。标记为答案 – peval27

+0

Hmya,这是一种当编译错误不明确时最终会出现的下降螺旋。而且你有一个以上的错误,让它变得更加复杂。你不需要使用它。 –

0

尝试使你的DbReader.GetUsers(...)执行公开,使其正确实现IDbReader接口。例如:

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

另外,还要确保你using System.Collections(为IEnumerable)除了using System.Collections.Generic(为List<T>)在C#代码。