[英]Consuming VB6 string array in C#
I have (legacy) VB6 code that I want to consume from C# code. 我有(遗留)VB6代码,我想从C#代码中使用。
This is somewhat similar to this question , but it refers to passing an array from VB6 consuming a C# dll. 这有点类似于这个问题 ,但它指的是从VB6传递一个消耗C#dll的数组。 My problem is the opposite.
我的问题恰恰相反。
In VB, there is an interface in one dll, and an implementation in another. 在VB中,一个dll中有一个接口,另一个中有一个实现。
Interface: 接口:
[
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)* );
};
Implementation (fragment) in cMyImplementationClass: cMyImplementationClass中的实现(片段):
Private Function IMyInterface_GetErrors() As String()
If mbCacheErrors Then
IMyInterface_GetErrors = msErrors
End If
End Function
I wrapped these 2 dlls with tlbimp.exe and attempt to call the function from C#. 我用tlbimp.exe包装了这两个dll,并尝试从C#调用该函数。
public void UseFoo()
{
cMyImplementationClass foo;
...
var result = foo.GetErrors();
...
}
Calling foo.GetErrors() causes a SafeArrayRankMismatchException . 调用foo.GetErrors()会导致SafeArrayRankMismatchException 。 I think this indicates a marshaling problem as described in the Safe Arrays section here .
我认为,这表明在安全数组一节中描述的编组问题在这里 。
The recommendation seems to be to use the /sysarray parameter of tlbimp.exe or to manually edit the IL produced, which I tried. 建议似乎是使用tlbimp.exe的/ sysarray参数或手动编辑我生成的IL,我试过。
The original IL looks like this: 最初的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
While the updated version is: 虽然更新版本是:
.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
I made identical function signature changes in both the interface and implementation. 我在接口和实现中都进行了相同的函数签名更改。 This process is described here .
此处描述了此过程。 However, it doesn't specify a return value in the function (it uses an "in" reference) and also doesn't use an interface.
但是,它没有在函数中指定返回值(它使用“in”引用),也不使用接口。 When I run my code and call from C#, I get the error
当我运行我的代码并从C#调用时,我收到错误
Method not found: 'System.Array MyDll.cImplementationClass.GetErrors()'.
找不到方法:'System.Array MyDll.cImplementationClass.GetErrors()'。
It seems to be that something is wrong in the IL that I edited, though I don't know where to go from here. 我编辑的IL似乎有些不对劲,但我不知道从哪里开始。
How can I consume this function from C# without changing the VB6 code? 如何在不更改VB6代码的情况下从C#中使用此函数?
--Edit-- Redefinition of "msErrors", which initializes the private array that gets returned. --Edit--重新定义“msErrors”,初始化返回的私有数组。
ReDim Preserve msErrors(1 To mlErrorCount)
If I understand correctly, the "1" in that means that the array is indexed from 1 instead of 0, which is the cause of the exception I see get thrown. 如果我理解正确,那么“1”意味着数组从1而不是0索引,这是我看到抛出的异常的原因。
I followed all your steps, except using TlbImp.exe. 我遵循了所有步骤,除了使用TlbImp.exe。 Instead, I directly added the DLLs into the C# project reference.
相反,我直接将DLL添加到C#项目引用中。 Doing this, I get IL which is a cross between both of the samples you give:
这样做,我得到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
I have done the same code as you, and essentially you are assigning to a variable of type Array
. 我和你做了相同的代码,基本上你要分配一个
Array
类型的变量。 Whilst the CLR supports arrays with lower bounds other than 0, AFAIK, no language, even VB.NET, supports it instrinsically in the language. 虽然CLR支持的数组的下限不是0,但是AFAIK,没有语言,甚至VB.NET,在语言中支持它。
My test code becomes: 我的测试代码变为:
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]);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.