4
votes

I have some VB6 code that can't be modified easily that looks like this:

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

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

The method it calls is currently a VB6 component that we've migrated to .NET with one issue. We're not sure what type the result() is looking for since it's a variant type. We've tried object, object[], object[][], string, string[], etc, none of which have worked.

Here's an example:

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

I've even tried setting the third param to VariantWrapper since it will add ByRef as necessary:

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

Any ideas what I can set the incoming result to be so that I don't have an unhandled exception?

I've created a test Interface (for COM), test Class, and test VB6 app to ensure it was an issue with the Variant. So, it's defined like so:

.NET Interface:

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

VB 6 method:

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

Same issue as described above. In VB6, it just throws me out. If I compile and run it, I get a generic .NET exception and it throws me out.

4
You can see how result() is declared and passed, but not how it is used after GetList returns?Jay
So as is, when the Variant() type is assigned to result and passes into GetList, .NET bombs out giving an error.Jason N. Gaylord
Adding VariantWrapper gives me an Error 5 Illegal procedure call.Jason N. Gaylord
Take a look at reflection in .NET to do what used to be done using late binding in VB6.onedaywhen
I've submitted my issue to the VB team. Last I knew, they thought what I'm doing should work. So I submitted a full project to them. I'll post back their response here.Jason N. Gaylord

4 Answers

4
votes

Your C# declaration is wrong. A VB6 "Long" is 32-bits for historical reasons. That's an int on the C# side. With the stack frame wrong, you have no odds of getting the "result" argument passed correctly.

It ought to be a SafeArray of Variants, object[] in C#.

1
votes

Put a breakpoint on the mx.GetList(rCount,cCount,result) line. Once hit, add a "quick watch" expression of mx.GetList(rCount,cCount,result). The toolwindow should show you what the resulting runtime-type is. Most likely it is a "comresult" and will not provide much information, but it may provide a hint to the return type.

1
votes

I think the ref keyword may be causing some trouble here. The types have to match exactly for that to work.

However, if your method simply accepts a reference to any object by value, (instead of by ref), it can get passed anything, since everything derives from object in .NET.

How well this translates to VB6/COM interop, I don't know. But it seems that this is at least worth a shot:

C# code:

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

VB6 code:

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

MsgBox mx.GetTypeName(result)

Does that give you anything?


Here's an idea. I could be dead wrong here -- I've not much experience in migrating VB6 apps to .NET -- but it seems to me that if you can get as far as (the C# equivalent of) this line...

Set mx = CreateObject("Component.Class")

...then you're golden. You can use reflection to figure out what parameters the GetList method wants.

First get the System.Type object representing the type of mx:

Type mxType = mx.GetType();

Then find the GetList method(s) for that type:

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

This will give you a MethodInfo[] array of all the public overloads of GetList taking 3 parameters. From here the possible types of result will be:

Type[] possibleResultTypes = getListMethods
    .Select(m => m.GetParameters()[2].ParameterType)
    .ToArray();
0
votes

I know only how it's used in .Net where you pass a reference to a Variant like this:

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

After this, put a breakpoint and check the variant type if you're not sure about it.

The main thing is using VariantWrapper so the dll understands.