C#COM互操作,生成了错误的签名?

时间:2018-02-23 13:38:54

标签: c# c++ com-interop

与许多人一样,我遇到了将C#数组传递给C ++中的COM客户端的问题。我的猜测是,这是由于CLR COM Interop生成了错误的签名。严肃的说法,是的,但这是交易。

C#COM服务器使用名为GenTechLib.tlb的类型库。

C ++ COM客户端期望来自COM服务器的通知具有如下签名的回调函数。这很明显。 C ++客户端需要获得多个元素,以及许多地址,值和时间戳。但是,C ++客户端只获取正确的第一个值,剩下的就是垃圾,而且由于没有正确地从C#传递数组。

HRESULT Notify(unsigned long dwCount, BSTR * psAddresses, VARIANT pvarValues,    DATE * pdtTimestamps )
{
  // let's say that received dwCount is 2, so therefore, below should work

  // this gets proper value
  BSTR addr0 = psAddresses[0];

  // this however gets pure garbage
  // result is the same for values and timestamps
  BSTR addr1 = psAddresses[1];
}

在C#端,上面函数的签名,从引用的TLB文件生成时是:

void Notify(uint dwCount, ref string psAddresses, ref object pvarValues, ref DateTime pdtTimestamps);

所以像这样的代码,虽然它编译并运行良好但不会产生预期的结果。

// all of below are arrays of 2, properly new'ed and filled
string[] arrAddresses;

string[] arrValues;
object obValues = arrValues; // function signature requires 'object'

DateTime[] arrDates;

notificationListener.Notify(2, ref arrAddresses[0], ref obValues, ref arrDates[0]);

不幸的是,我无法修改Notify()的签名。它是从TLB文件生成的。这是使用编译器生成的Interop.GenTech.dll文件的ILSpy进行反编译输出的。

[ComImport]
[Guid("F97D74BE-AC1C-404E-B463-142916096A24")]
[InterfaceType(1)]
public interface INotificationListener
{
    [MethodImpl(MethodImplOptions.InternalCall)]
    void Notify([In] uint dwCount, [In] [MarshalAs(UnmanagedType.BStr)] ref string psAddresses, [In] [MarshalAs(UnmanagedType.Struct)] ref object pvarValues, [In] ref DateTime pdtTimestamps);
}

注意C ++和C#签名之间的区别。我们可以告诉C#COM interop生成签名以传回数组(ref string []而不是ref string)?是否可以以接受数组的方式覆盖此签名?或者首先是从dll生成TLB的问题?

生成TLB(GenTechLib.tlb)的COM接口dll是在古代VS2003中用C ++创建的dll。不幸的是,我没有它的来源。

从C#语言的角度来看,可以做些什么?如果输出参数类型为:

ref string strArray

它是否可以在内存中填充字符串顺序,并通过COM互操作傻瓜C ++ COM客户端,以便它接收BSTR *形式的字符串数组? 也许使用不安全的块?但即使如此,我也有一些疑问。

如果由我决定,整个代码库将使用C ++,但它就是它。

有什么想法?提示? 感谢。

更新

我停止使用.tlb类型库的引用,而是手动更正某些函数的签名,最明显的是Notify()

查看下面这个函数签名的怪异(不再是ref关键字),在C ++ COM客户端代码中正常工作,现在接收到数组的所有元素。我的C ++ COM客户端代码只是模拟我认为真实系统的样子,但我99%确定不使用安全数组。

[ComImport]
[Guid("F97D74BE-AC1C-404E-B463-142916096A24")]
[InterfaceType(1)]
public interface INotificationListener
{
    [MethodImpl(MethodImplOptions.InternalCall)]
    void Notify([In] uint dwCount, [In] [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.BStr, SizeParamIndex = 0)] string[] psAddresses, [In] [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.Struct, SizeParamIndex = 0)] object[] pvarValues, [In] [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.R8, SizeParamIndex = 0)] double[] pdtTimestamps);
}

我还需要手动修正另一个功能的签名,但这是主要阻止程序。在C#项目中引用类型库似乎并不总是意味着您将获得正确的函数签名。也许Visual Studio类型库代码生成器只能到目前为止。

0 个答案:

没有答案