VB6变量类型转换成.NET类型

4

我有一些VB6代码,很难进行修改,它看起来像这样:

Dim cCount as Long
Dim rCount as Long
Dim result()

Set mx = CreateObject("Component.Class")
Dim rtn = mx.GetList(rCount,cCount,result)

它所调用的方法目前是一个VB6组件,我们已经将其迁移到.NET,但存在一个问题。我们不确定结果()正在寻找哪种类型,因为它是一种变体类型。我们尝试过对象、对象数组、对象二维数组、字符串、字符串数组等等,但都没有成功。

以下是一个示例:

public bool GetList(ref long rCount, ref long cCount, ref object result)
{
  ...
}

我甚至尝试将第三个参数设置为VariantWrapper,因为它会根据需要添加ByRef:

public bool GetList(ref long rCount, ref long cCount, VariantWrapper result)
{
  ...
}

有什么办法可以设置传入结果,以便我不会出现未处理的异常吗?

我创建了一个测试接口(用于COM)、测试类和测试VB6应用程序,以确保这是Variant的问题。因此,它的定义如下:

.NET接口:

[DispId(1)]
[ComVisible(true)]
string Test(ref object[] value);

VB 6 方法:

Private Sub Command1_Click()
    Set mx = CreateObject("Component.Class")
    Dim result()
    MsgBox mx.Test(result)
End Sub

与上述描述的问题相同。在 VB6 中,它只是将我弹出。如果我编译并运行它,我会得到一个通用的 .NET 异常,并将我弹出。


2
你可以看到result()是如何声明和传递的,但在GetList返回后它是如何使用的呢? - Jay
就像现在这样,当将Variant()类型分配给result并传递到GetList时,.NET会出错报错。 - Jason N. Gaylord
添加 VariantWrapper 会给我一个错误 5 非法的过程调用。 - Jason N. Gaylord
在.NET中查看反射,以执行以前在VB6中使用的后期绑定。 - onedaywhen
我已经向VB团队提交了我的问题。 我最后得知,他们认为我正在做的事情应该能够工作。 因此,我向他们提交了一个完整的项目。 我将在这里发布他们的回复。 - Jason N. Gaylord
4个回答

4
您的C#声明有误。VB6中的“Long”由于历史原因是32位,这在C#中对应int类型。由于堆栈框架错误,您无法正确传递“result”参数。
应该使用安全数组的变体,即C#中的object[]。

我已经将它切换为bool GetList(ref int rCount, ref int cCount, ref object[] result);但仍然无法工作。我立即收到一个.NET异常。 - Jason N. Gaylord
1
请不要让我猜测异常。 - Hans Passant
汉斯,抛出的异常是“Visual Basic 生成了一个异常...” 它只发生在结果 Variant() 类型。我创建了一个新的 .NET 方法来测试(下面是我的接口签名表单):string Test(ref object[] value); 然后,我创建了一个测试 VB6 应用程序,如下所示:Private Sub Command1_Click() Set mx = CreateObject("Component.Class") Dim result() MsgBox mx.Test(result) End Sub 和上面描述的问题相同。正如我最初所描述的那样,它不喜欢 Variant 类型。 - Jason N. Gaylord
2
我需要一些时间来启动那台老机器。呃。我建议给予奖金。 - Hans Passant

1

我觉得“ref”关键字在这里可能会引起一些麻烦。为了让它工作,类型必须完全匹配。

然而,如果你的方法只接受按值传递的任何对象的引用(而不是按引用传递),它可以接受任何东西,因为在.NET中,所有东西都派生自“object”。

我不知道这对于VB6/COM互操作性来说是否很好,但至少值得一试:

C#代码

public string GetTypeName(object value)
{
    return value.GetType().FullName;
}

VB6 代码:

Set mx = CreateObject("Component.Class")
Dim result()

MsgBox mx.GetTypeName(result)

这个对你有帮助吗?


这里有一个想法。我可能完全错了——我在将VB6应用程序迁移到.NET方面没有太多经验——但是如果你能达到(C#等效的)这一行……

Set mx = CreateObject("Component.Class")

如果你做到了这一点,那么你就成功了。你可以使用反射来确定GetList方法需要哪些参数。

首先获取表示mx类型的System.Type对象:

Type mxType = mx.GetType();

然后找到该类型的GetList方法:

MethodInfo[] getListMethods = mxType.GetMember("GetList")
    .OfType<MethodInfo>()
    .Where(m => m.GetParameters().Length == 3)
    .ToArray();

这将为您提供一个MethodInfo[]数组,其中包含所有公共重载的GetList,需要3个参数。从这里开始,result的可能类型将是:
Type[] possibleResultTypes = getListMethods
    .Select(m => m.GetParameters()[2].ParameterType)
    .ToArray();

这不会起作用,因为VB6应用程序正在传递到C#。问题不在于我需要在VB6中使用哪些类型,而是在C#中设置COM方法的类型。VB6类型是Variant()。C#类型应该是object[]或VariantWrapper,但两者都不起作用。 - Jason N. Gaylord
@Jason:我有些疑惑。你是在说抛出异常的是你的VB6代码吗?而你只是想弄清楚如何定义你的C#方法,以便可以从VB6中调用它?还是反过来? - Dan Tao
我想弄清楚如何定义我的 C# 方法,以便我可以从 VB6 调用它。然而,VB6 没有抛出异常,是 .NET 抛出的。调用转到Interop中的类库,但由于它是一个.NET程序集,.NET生成了异常。 - Jason N. Gaylord
@Jason:我又发布了一个想法。如果这对你有用或者能够实现什么,请告诉我。虽然我对VB6或COM互操作几乎一无所知,也许不应该提供帮助,但我对这个问题很感兴趣,所以我只是随便说说我的想法。 - Dan Tao

1
mx.GetList(rCount,cCount,result) 行上设置断点。一旦命中,添加一个 "快速查看" 表达式 mx.GetList(rCount,cCount,result)。工具窗口应该会显示结果的运行时类型。很可能是 "comresult",并不提供太多信息,但它可能为返回类型提供提示。

我修改了代码,以便一旦它进入方法,就返回一个true布尔值。如果我将结果变量切换为不同的类型并在快速VB6测试应用程序中进行测试,则可以正常工作。 - Jason N. Gaylord

0

我只知道在 .Net 中如何使用,您可以像这样传递对 Variant 的引用:

 int port = 2;
 object pvPort = new System.Runtime.InteropServices.VariantWrapper(port);
 gimp.SetPort(ref pvPort);

在此之后,设置一个断点并检查变量类型,如果您不确定它。

主要是使用VariantWrapper,这样dll就能理解了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接