从VB.Net调用C ++方法,字符串处理为字符串数组

时间:2010-11-17 12:57:56

标签: c++ vb.net interop marshalling

我有一个带有此签名的C ++方法:

STDMETHODIMP ClassName::FunctionName(long number, BSTR* names, long* status)

在方法内部,名称变量作为字符串数组访问,即

char *  tempString = NULL;

for (int n = 0; n < number; n++)
{
    tempString = OLE2T(names[n]);
...

我编译了生成dll的C ++项目,然后我注册了这个dll并在VB项目中添加了对它的引用。 当我添加引用时,会自动生成Interop程序集,Interop程序集中方法的签名如下:

FunctionName (number as Integer, ByRef names as String) As Integer

从VB.Net我调用这样的方法:

result = FunctionName (number, names(0))

其中names是一个包含多个元素的字符串数组,number和result是Integers。

问题是,当C ++代码试图访问names数组中的其余元素(名称[1]和前面)时,它开始在这些字段上获得“垃圾”。

我的问题是,如何发送整个字符串数组而不是第一个值。

C ++代码是一个我无法修改的库,因此我所做的任何更改都必须在VB.Net代码上。

我在想,也许使用PInvoke调用该方法可能会起到作用(声明一个正确的签名),但我希望有更好的东西。

有什么想法吗?

谢谢!

编辑:

我不是Interop / Marshaling的专家,但是我检查了方法的IDL定义,它如下:

[id(60), helpstring("method FunctionName")]
        HRESULT FunctionName(
                    [in] long number, 
                    [in, size_is(number)] BSTR* names,
                    [out, retval] long* status);

size_is不应该表明names参数是一个数组,因此,当Interop程序集生成时会相应地采取行动吗?

再次感谢

1 个答案:

答案 0 :(得分:2)

不,[size_is]是只有midl.exe知道如何使用的属性。它在为接口生成代理/存根时会这样做,当您想要跨进程边界进行调用时使用它。

在类型库中无法表达。该参数作为“指向BSTR的指针”发出,该指针可以指示通过引用传递的BSTR或BSTR的数组。 Tlbimp.exe无法区分,它挑选了前者。它必须,它不能合理推断数组大小。您在运行时遇到垃圾,因为CLR互操作层只传递数组的单个元素。任何COM客户端使用此方法都有一个大问题,它不是特定于.NET的。 .NET pinvoke marshaller有针对此问题的解决方法,请注意[MarshalAs]属性的SizeParamIndex属性。

如果您无法修改C ++代码,则需要手动编辑互操作库以注入[MarshalAs]属性。这非常难看,你必须用ildasm.exe反编译库,编辑.il并将它与ilasm.exe一起放回去。你不想经常这样做。

如果可以的话,您应该使用自动化兼容的方式传递数组。使用SAFEARRAY。类型库和CLR互操作管道完全支持这一点,管理端不需要您的工作。另请注意,您现在不再需要 number 参数,安全数组知道它们有多长。与托管数组不同。