在C#中使用VB6字符串数组

我有(遗留)VB6代码,我想从C#代码中使用。

这有点类似于这个问题 ,但它指的是从VB6传递一个消耗C#dll的数组。 我的问题恰恰相反。

在VB中,一个dll中有一个接口,另一个中有一个实现。

接口:

[ odl, uuid(339D3BCB-A11F-4fba-B492-FEBDBC540D6F), version(1.0), dual, nonextensible, oleautomation, helpstring("Extended Post Interface.") ] interface IMyInterface : IDispatch { [id(...),helpstring("String array of errors.")] HRESULT GetErrors([out, retval] SAFEARRAY(BSTR)* ); }; 

cMyImplementationClass中的实现(片段):

 Private Function IMyInterface_GetErrors() As String() If mbCacheErrors Then IMyInterface_GetErrors = msErrors End If End Function 

我用tlbimp.exe包装了这两个dll,并尝试从C#调用该函数。

 public void UseFoo() { cMyImplementationClass foo; ... var result = foo.GetErrors(); ... } 

调用foo.GetErrors()会导致SafeArrayRankMismatchException 。 我认为这表明了一个编组问题,如安全arrays部分所述。

建议似乎是使用tlbimp.exe的/ sysarray参数或手动编辑我生成的IL,我试过。

原始的IL看起来像这样:

 .method public hidebysig newslot virtual instance string[] marshal( safearray bstr) GetErrors() runtime managed internalcall { .override [My.Interfaces]My.Interface.IMyInterface::GetErrors } // end of method cImplementationClass::GetErrors 

虽然更新版本是:

 .method public hidebysig newslot virtual instance class [mscorlib]System.Array marshal( safearray) GetErrors() runtime managed internalcall { .override [My.Interfaces]My.Interface.IMyInterface::GetErrors } // end of method cImplementationClass::GetErrors 

我在接口和实现中都进行了相同的函数签名更改。 此处描述了此过程。 但是,它没有在函数中指定返回值(它使用“in”引用),也不使用接口。 当我运行我的代码并从C#调用时,我收到错误

找不到方法:’System.Array MyDll.cImplementationClass.GetErrors()’。

我编辑的IL似乎有些不对劲,但我不知道从哪里开始。

如何在不更改VB6代码的情况下从C#中使用此函数?

–Edit–重新定义“msErrors”,初始化返回的私有数组。

 ReDim Preserve msErrors(1 To mlErrorCount) 

如果我理解正确,那么“1”意味着数组从1而不是0索引,这是我看到抛出的exception的原因。

我遵循了所有步骤,除了使用TlbImp.exe。 相反,我直接将DLL添加到C#项目引用中。 这样做,我得到IL,这是你给出的两个样本之间的交叉:

 .method public hidebysig newslot abstract virtual instance class [mscorlib]System.Array marshal( safearray bstr) GetErrors() runtime managed internalcall { .custom instance void [mscorlib]System.Runtime.InteropServices.DispIdAttribute::.ctor(int32) = ( 01 00 00 00 03 60 00 00 ) // .....`.. } // end of method _IMyInterface::GetErrors 

我和你做了相同的代码,基本上你要分配一个Array类型的变量。 虽然CLR支持的数组的下限不是0,但是AFAIK,没有任何语言,甚至是VB.NET,在语言中都支持它。

我的测试代码变为:

 cMyImplementationClass myImpClass = new cMyImplementationClass(); IMyInterface myInterface = myImpClass as IMyInterface; myImpClass.CacheErrors = true; // Retrieve the error strings into the Array variable. Array test = myInterface.GetErrors(); // You can access elements using the GetValue() method, which honours the array's original bounds. MessageBox.Show(test.GetValue(1) as string); // Alternatively, if you want to treat this like a standard 1D C# array, you will first have to copy this into a string[]. string[] testCopy = new string[test.GetLength(0)]; test.CopyTo(testCopy, 0); MessageBox.Show(testCopy[0]);