在.NET中调用COM枚举器的正确方法是什么?

时间:2014-03-07 03:01:18

标签: c# c++ .net com com-interop

我正在调用一个外部提供的COM DLL,我为此生成了一个COM互操作包装器。为了论证,让我们调用我想要调用IEnumFoo的接口。

IEnumFoo具有典型的COM枚举器模式:

HRESULT Next ( 
   ULONG        celt,
   IFoo**       rgelt,
   ULONG*       pceltFetched
);

其中第一个参数是所需结果的数量,第二个参数是写入结果的缓冲区,最后一个参数描述实际写入的结果数。

当我选择“添加引用”并将Visual Studio指向此DLL时,它会生成一个带有以下签名的COM Interop程序集:

void Next(uint, out IFoo, out uint)

这只允许.NET代码一次请求一个对象,这可能会增加使用这些API的大量开销。

我是否可以使用某种机制来生成Next的版本,这样我可以提供更多IFoo个“插槽”,以便让编组人员满意? (我不反对在互操作程序集中编辑IL :))

3 个答案:

答案 0 :(得分:4)

适当的签名就像这样:

void Next(
    uint celt,
    [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] IFoo[] rgelt,
    out uint pceltFetched);

根据MSDN,至少,没有机制自动生成它。即使接口的原始IDL已length_is应用于rgelt,该信息也会在类型库中丢失。因此,您需要手动编辑互操作程序集。

另一个选择是在主程序集中完全手动定义此特定接口,并忽略生成的互操作版本。请记住,在RCW上进行强制转换时,任何具有匹配GUID的接口(即QueryInterface成功的接口)都可以工作,因此您实际上可以使用多个不同的托管接口来呈现同一COM接口的不同视图。

答案 1 :(得分:2)

不回答您的问题,而是建议尝试不同的方法。我将创建一个C ++ / CLI包装器,通过COM接口枚举非托管代码(从而避免编组开销),然后构建一个托管List或其他容器,在其中返回对象。

这几乎可以保证比手动调整互操作程序集的IL更容易,您也可以轻松调试它。非托管C ++代码将非常简单,就像围绕它的托管包装器一样。

答案 2 :(得分:1)

如果实现此接口的对象是本机的,那么只需在代码中重新定义接口,确保在接口上使用相同的ComImport和Guid属性。然后获取对象并转换为您的界面。您可以通过该界面进行调用。

请记住:互操作程序集不是魔术,您可以随时手动定义接口。