views:

120

answers:

3

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.

+1  A: 

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.

AMissico
I've modified the code so that as soon as it hits the method, it returns a true boolean value. If I switch the result variable to a different type and test it in a quick VB6 test app, it works just fine.
Jason N. Gaylord
+1  A: 

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();
Dan Tao
That's not going to work because the VB6 app is passing into C#. It's not a question as to what types I need to use in VB6 as it is what type to set the COM method in C#. The VB6 type is of Variant(). The C# type should be object[] or VariantWrapper neither of which work.
Jason N. Gaylord
@Jason: I think I'm confused. Are you saying it's your VB6 code that's throwing the exception? And you simply want to figure out how to define your C# method so that you can call it from VB6? Or is that backwards?
Dan Tao
I want to figure out how to define my C# method so I can call it from VB6. However, VB6 isn't throwing the exception, it's .NET. The call goes to the class library in Interop, but because it's a .NET assembly, .NET generates the exception.
Jason N. Gaylord
@Jason: I posted another idea. Let me know if that works/accomplishes anything for you. I probably shouldn't even be offering me help as I know next to nothing about VB6 or COM interop, but I'm intrigued by the problem so I'm just throwing my thoughts out there.
Dan Tao
+2  A: 

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#.

Hans Passant
I've switched it to bool GetList(ref int rCount, ref int cCount, ref object[] result);and it still doesn't work. I get a .NET Exception right away.
Jason N. Gaylord
Please don't make me guess at the exception.
Hans Passant
Hans, the exception that is thrown is "Visual Basic has generated an exception..." It only happens with the result Variant() type. I created a new .NET method to test (below is my signature form the Interface): string Test(ref object[] value); Then, I created a test VB6 app like so: Private Sub Command1_Click() Set mx = CreateObject("Component.Class") Dim result() MsgBox mx.Test(result) End Sub Same issue as described above. As I originally described, it doesn't like the Variant type.
Jason N. Gaylord
I've updated the stem above for your review.
Jason N. Gaylord
It's going to take me a while to jumpstart that old machine. Bleh. I'd recommend a bonus.
Hans Passant