Question

I have the following legacy VB6 function that I want to call from C#.

Public Function CreateMiscRepayment(ByRef objMiscRepayment As MiscRepayment) As Variant
   ' Code that sets objMiscRepayment here
End Function

I'm using the following code in C# but getting an exception:

dynamic vb6ComObject = Activator.CreateInstance(Type.GetTypeFromProgID(progId));
dynamic miscRepayment = null;
dynamic result = vb6ComObject.CreateMiscRepayment(ref miscRepayment);

The exception is:

System.ArgumentException: Could not convert argument 0 for call to CreateMiscRepayment.
at System.Dynamic.ComRuntimeHelpers.CheckThrowException(Int32 hresult, ExcepInfo& excepInfo, UInt32 argErr, String message)
at CallSite.Target(Closure , CallSite , ComObject , Object& )
at CallSite.Target(Closure , CallSite , ComObject , Object& )
at CallSite.Target(Closure , CallSite , Object , Object& )
at CallSite.Target(Closure , CallSite , Object , Object& )
Application\ApplicationClasses.cs(65,0): at ApplicationClasses.CanInstantiateMiscRepayment()

I've tried changing ref to out but get the same error. If I omit ref, the method executes without error, but of course miscRepayment is still null rather than containing the object that was supposed to be passed out.


Update

I've tried some other ways, including using VB.NET (since it has always been more COM friendly than C#).

With the following VB.NET code:

Dim vb6ComObject = Activator.CreateInstance(System.Type.GetTypeFromProgID(progId))
Dim miscRepayment = Nothing
Dim result = vb6ComObject.CreateMiscRepayment(miscRepayment)

It throws the following similar, but different exception:

System.Runtime.InteropServices.COMException: Type mismatch. (Exception from HRESULT: 0x80020005 (DISP_E_TYPEMISMATCH))
    at Microsoft.VisualBasic.CompilerServices.LateBinding.LateGet(Object o, Type objType, String name, Object[] args, String[] paramnames, Boolean[] CopyBack)
    at Microsoft.VisualBasic.CompilerServices.NewLateBinding.LateGet(Object Instance, Type Type, String MemberName, Object[] Arguments, String[] ArgumentNames, Type[] TypeArguments, Boolean[] CopyBack)
    UnitTest1.vb(19,0): at TestProject1.UnitTest1.TestMethod1()

Interestingly, if I change the call in either the C# or VB.NET example code to use null/Nothing instead of miscRepayment then the code executes without throwing an exception. I've even set a breakpoint in the code of the VB6 COM object and can confirm that the code has executed properly on that end. Obviously, with setting the miscRepayment parameter to null/Nothing, there is then no way in .NET to receive the created object. The problem must be to do with the marshalling of parameters.

I've also tried using Type.InvokeMember with a ParameterModifier argument that marks miscRepayment as being a ref parameter, but get the following exception:

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.Runtime.InteropServices.COMException: Type mismatch. (Exception from HRESULT: 0x80020005 (DISP_E_TYPEMISMATCH))

     --- End of inner exception stack trace ---
    at System.RuntimeType.InvokeDispMethod(String name, BindingFlags invokeAttr, Object target, Object[] args, Boolean[] byrefModifiers, Int32 culture, String[] namedParameters)
    at System.RuntimeType.InvokeMember(String name, BindingFlags bindingFlags, Binder binder, Object target, Object[] providedArgs, ParameterModifier[] modifiers, CultureInfo culture, String[] namedParams)
    UnitTest1.vb(18,0): at TestProject1.UnitTest1.TestMethod1()

Lastly, I've tried the following VB.NET code:

Dim vb6ComObject = Activator.CreateInstance(System.Type.GetTypeFromProgID(progId))
Dim args(0) As Object
Microsoft.VisualBasic.CompilerServices.LateBinding.LateCall(vb6ComObject, type, "CreateMiscRepayment", args, Nothing, New Boolean() {True})

It throws the following exception:

System.Runtime.InteropServices.COMException: Type mismatch. (Exception from HRESULT: 0x80020005 (DISP_E_TYPEMISMATCH))
    at Microsoft.VisualBasic.CompilerServices.LateBinding.InternalLateCall(Object o, Type objType, String name, Object[] args, String[] paramnames, Boolean[] CopyBack, Boolean IgnoreReturn)
    at Microsoft.VisualBasic.CompilerServices.LateBinding.LateCall(Object o, Type objType, String name, Object[] args, String[] paramnames, Boolean[] CopyBack)
    UnitTest1.vb(17,0): at TestProject1.UnitTest1.TestMethod1()

With all the code that throws an exception, the VB6 COM object is never invoked. The COM interop code must be choking when trying to marshall the ref parameter.

In my Google searches I've come across some examples using Type.InvokeMember, but the ref parameters are always for simple types such as integers and strings.

Was it helpful?

Solution

There doesn't seem to be a way in .NET to call a method on a COM object that takes a ref parameter with a complex type.

I've filed a bug with Microsoft. Vote it up if this problem is also affecting you.

Update 30/4/2013

There is a comment on the bug report from Microsoft saying that it has been fixed. No mention of which version of .NET has been affected though.

OTHER TIPS

Not actually an answer, but a workaround.

Seems to me, that when you change the way of accessing COM objects from dynamic to static, the problem goes away. By static way I mean to prepare a dll for the COM object using C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\TlbImp.exe. I guess this is early binding.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top