2016-02-04 67 views
10

我有(遗留)VB6代码,我想从C#代码消耗。C#中使用VB6字符串数组

这有点类似于this question,但它是指从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 

我包裹这些2倍的DLL与tlbimp.exe是并试图调用从C#的功能。

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

调用foo.GetErrors()引起SafeArrayRankMismatchException。我认为这表示安全数组部分here中所述的编组问题。

该建议似乎是使用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 

我做了两个接口和实现相同的函数签名的变化。该过程描述为here。但是,它没有在函数中指定返回值(它使用“in”引用),也不使用接口。当我运行我的代码,并从C#调用,我得到的错误没有发现

方法:“System.Array的MyDll.cImplementationClass.GetErrors()”。

这似乎是在我编辑的IL中出了什么问题,虽然我不知道从哪里出发。

我怎么能消耗从C#此功能不改变VB6代码?

- 编辑 - “msErrors”的重新定义,它初始化被返回的私有数组。

ReDim Preserve msErrors(1 To mlErrorCount) 

如果我理解正确的话,在该“1”表示该阵列由1改为0索引,这是例外,我看到抛出的原因。

+1

我明白你想要得到它第一个工作日,但是编辑IL似乎并不像一个长期的解决方案。 –

+0

也许是这样,但它的变化编组中提到的建议的做法[这里](https://msdn.microsoft.com/en-us/library/ek1fb3c6(V = VS.100)的.aspx#cpconeditingmicrosoftintermediatelanguagemsilanchor4)。 FWIW,/ sysarray标志似乎具有相同的净效应,包括产生的错误。 – ayers

+0

您还没有表现出你是如何声明,你从VB6代码返回数组。它是否具有等级1和下限0,即声明为“Dim msErrors(0到N)As String”之类的东西?另外,如果mbCacheErrors为false,你当前的实现似乎返回一个未初始化的数组。 – Joe

回答

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 

我也做了相同的代码,你,而且基本上你分配给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]); 
+0

这绝对是正确的答案。这IL正是得到由TLBIMP.EXE使用/ sysArray标志制作,不过,我想我的问题可能是由一对夫妇的事情复杂化。 为别人遇到类似问题,看来声明结果为'Array'(而不是'var')是很重要的。 我还需要从GAC清除原有的组件,并与新的替换它们。看来我的原始实现在编译时可能是正确的,但运行时IIS在GAC中使用了陈旧的程序集。 – ayers