如何使用动态方式从C#调用具有ref参数的VB6 COM对象?

4
我有一个遗留的VB6函数,我想从C#中调用它。
Public Function CreateMiscRepayment(ByRef objMiscRepayment As MiscRepayment) As Variant
   ' Code that sets objMiscRepayment here
End Function

我在使用以下C#代码时出现异常:

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

异常情况如下:
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()

我尝试将ref改成out,但是得到了相同的错误。如果我省略ref,方法就会执行而不出错,但是miscRepayment仍然是null,而不是包含应该传递出去的对象。

更新

我尝试了一些其他方式,包括使用VB.NET(因为它一直比C#更加友好)。

以下是VB.NET代码:

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

它会抛出以下类似但不同的异常:
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()

有趣的是,如果我在C#或VB.NET示例代码中更改调用,使用null/Nothing而不是miscRepayment,则代码将执行而不会抛出异常。我甚至在VB6 COM对象的代码中设置了断点,并确认代码已经正确地在那一端执行。显然,将miscRepayment参数设置为null/Nothing后,.NET就没有办法接收创建的对象。问题必须与参数的封送有关。
我还尝试使用Type.InvokeMember和一个标记miscRepaymentref参数的ParameterModifier参数,但会出现以下异常:
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()

最后,我尝试了以下VB.NET代码:
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})

它会抛出以下异常:
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()

在所有抛出异常的代码中,VB6 COM对象从未被调用。当尝试传递ref参数时,COM交互操作代码可能会出现问题。

在我的谷歌搜索中,我发现了一些使用Type.InvokeMember的示例,但是ref参数总是针对简单类型,如整数和字符串。


@pst:我尝试按照你建议的将类型从dynamic改为object,但仍然抛出相同的异常。不太确定你最后一句话的意思。 - John Mills
2
我已经有一段时间没有处理COM对象了,但是你不应该能够获得MiscRepayment的定义而不是动态的吗?另外,我可以看一下你如何引用vb6ComObject吗?我相信这将是标准的,只是想确保。 - Adam
@pst:我尝试使用反射,但没有成功。type.GetMethod("CreateMiscRepayment") 返回 null。type.GetMethods() 返回 7 个方法,但这些是 MarshalByRefObject 上的公共方法。 - John Mills
@Adam:我通过调用Activator.CreateInstance(Type.GetTypeFromProgID(progId)实例化了vb6ComObject。我没有引用任何互操作程序集;希望使用动态方式可以避免这样做。 - John Mills
@Adam:当Pst建议使用反射时,我尝试过了,但它没有显示VB 6代码中定义的任何方法。我的猜测是我们只有一个IDispatch接口,因此只能使用后期绑定。我也会尝试查询由GetTypeFromProgID(progId)返回的类型,看看是否包含这些方法。 - John Mills
显示剩余4条评论
2个回答

2

在.NET中似乎没有一种方法可以调用一个带有复杂类型ref参数的COM对象上的方法。

我已经向微软报告了这个问题。如果这个问题也影响到你,请投票支持它。

更新于2013年4月30日

微软在错误报告中发表评论说已经修复了此问题。但没有提及哪个版本的.NET受到了影响。


1
不太可能的情况。虽然我没有尝试过使用COM对象,但我已经通过Activator.CreateInstance调用了一个带有ref参数的类构造函数。实际上,没有必要注明它是ref,因为它会自动计算。如果您正在处理方法参数,则可以使用typeof(TheType).MakeByRefType()。再次声明,我可能是错的 :) - Adam
我相信它可以很好地反射.NET对象。但是对于COM,它对我来说不起作用。然而,如果有人能够展示如何使其工作,我非常乐意被证明是错误的,并且很高兴更改已接受的答案。 - John Mills
有关此事有任何消息吗?我正在与同样的问题苦苦挣扎,但似乎没有明确的解决方法。微软还是老一套... - MoonKnight

0

其实并不是一个答案,而是一个解决方法。

在我看来,当你将访问COM对象的方式从dynamic更改为静态时,问题就会消失。通过静态方式,我指的是使用C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\TlbImp.exe为COM对象准备一个dll。我猜这是早期绑定。


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