【问题标题】:How do I call a VB6 COM object from C# with dynamic when it has a ref parameter?当它具有 ref 参数时,如何从 C# 动态调用 VB6 COM 对象?
【发布时间】:2011-12-16 03:07:14
【问题描述】:

我有以下要从 C# 调用的旧版 VB6 函数。

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 仍然为空,而不是包含应该传递出去的对象。


更新

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

使用以下 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 与将miscRepayment 标记为ref 参数的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 互操作代码必须阻塞。

在我的 Google 搜索中,我遇到了一些使用 Type.InvokeMember 的示例,但 ref 参数始终用于简单类型,例如整数和字符串。

【问题讨论】:

  • 如果将dyanmic miscRepayment 更改为object miscRepayment 会怎样? (如果可行,则稍后将其视为dynamic ...但我不使用dynamic或C#4。此外,有没有办法获得“动态方法组”?例如强制Action / Function /委派出去?)
  • @pst:我尝试按照您的建议从dynamic 更改为object,但它仍然抛出相同的异常。不确定你的最后一点。
  • 我想知道是否可以((MyDelegate)it.method)(...);,但它不是——至少因为它是一个表达式——它出现了。你能用“常规”反射让它工作吗?这至少意味着“嘿,它以某种方式起作用!” (或不)。
  • 或许有一些提示:stackoverflow.com/questions/2475310/…
  • 自从我处理 COM 对象以来已经有一段时间了,但您不应该能够获得 MiscRepayment 的定义而不是动态的。我也可以看看你是如何引用 vb6ComObject 的,我相信它会是标准的,只是想确定一下。

标签: c# dynamic vb6 com-interop late-binding


【解决方案1】:

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

我在 Microsoft 工作过 filed a bug。如果这个问题也影响到您,请投票it

2013 年 4 月 30 日更新

Microsoft 的错误报告中有一条评论说它已得到修复。不过没有提及哪个版本的 .NET 受到了影响。

【讨论】:

  • 不太可能是这样。虽然我没有用 COM 对象尝试过这个,但我已经通过 Activator.CreateInstance 在构造函数中使用 ref 参数调用了一个类。实际上没有必要注意它是一个参考,因为它是自动考虑的。如果您正在处理方法参数,您可以使用 typeof(TheType).MakeByRefType()。然后我又可能错了:)
  • 我确信它可以很好地反映 .NET 对象。但它对 COM 不起作用(对我来说)。很高兴被证明是错误的,如果有人可以展示如何使它起作用,很高兴改变接受的答案。
  • 关于此的任何消息。我正在努力解决同样的问题,并且没有明确的解决方法。 MS 有通常的废话...
【解决方案2】:

实际上不是答案,而是一种解决方法。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-06-19
    • 2013-10-04
    • 1970-01-01
    • 2014-03-05
    • 2011-11-15
    • 2016-07-02
    • 2011-02-17
    • 2011-09-25
    相关资源
    最近更新 更多