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

aye*_*ers 10 c# vb6 com-interop

我有(遗留)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)* );
};
Run Code Online (Sandbox Code Playgroud)

cMyImplementationClass中的实现(片段):

Private Function IMyInterface_GetErrors() As String()

    If mbCacheErrors Then
        IMyInterface_GetErrors = msErrors
    End If

End Function
Run Code Online (Sandbox Code Playgroud)

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

public void UseFoo()
{
    cMyImplementationClass foo;
    ...
    var result = foo.GetErrors();
    ...
}
Run Code Online (Sandbox Code Playgroud)

调用foo.GetErrors()会导致SafeArrayRankMismatchException.我认为,这表明在安全数组一节中描述的编组问题在这里.

建议似乎是使用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
Run Code Online (Sandbox Code Playgroud)

虽然更新版本是:

.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
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

--Edit--重新定义"msErrors",初始化返回的私有数组.

ReDim Preserve msErrors(1 To mlErrorCount)
Run Code Online (Sandbox Code Playgroud)

如果我理解正确,那么"1"意味着数组从1而不是0索引,这是我看到抛出的异常的原因.

Mar*_*haw 1

我遵循了您的所有步骤,除了使用 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
Run Code Online (Sandbox Code Playgroud)

我已经完成了与您相同的代码,本质上您正在分配给类型为 的变量Array。虽然 CLR 支持下限不是 0 的数组,但据我所知,没有语言(甚至 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]);
Run Code Online (Sandbox Code Playgroud)