在 C# 中实现 DISPID_VALUE 并从 C++ 中调用它

Posted

技术标签:

【中文标题】在 C# 中实现 DISPID_VALUE 并从 C++ 中调用它【英文标题】:Implement DISPID_VALUE in C# and call it from C++ 【发布时间】:2012-12-15 15:58:05 【问题描述】:

我有一个接受 IDispatch 接口的 C++ COM 模块,并在某些情况下使用 DISPID_VALUE 调用它们。此方法在 C++ 中运行良好。现在我有一个 C# 客户端,我想实现一个实现 IDispatch 的对象,并有一个 DISPID = 0(DISPID_VALUE) 的方法。我已经试过了:

// This will generate invalid cast
[ComVisible(true)]
class Callback1

    [DispId(0)]
    void Execute(object arg) ...


// This also generate invalid cast
[ComVisible(true)]
[Guid("163AC24E-90DB-47D4-8580-EBB21E981FBF"),
    InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
interface ICallback2

    [DispId(0)]
    void Execute(object arg) ;

[Guid("842A7754-7CE6-4991-9E12-3FAB2367591A"),
    ClassInterface(ClassInterfaceType.None),
    ComSourceInterfaces(typeof(ICallback2))]
class Callback2 : ICallback2

    public void Execute(object arg) 

我也不记得是怎么做的,但我也写了一个成功转换但什么都不调用的代码。现在我想知道我应该如何编写一个实现IDispatch 的类,并在DISPID = 0 时调用一个特定的方法。

投射异常是:

System.InvalidCastException was unhandled
  Message=Specified cast is not valid.
  Source=mscorlib
  StackTrace:
   at System.StubHelpers.InterfaceMarshaler.ConvertToNative(Object objSrc, IntPtr itfMT, IntPtr classMT, Int32 flags)
   at nmclientLib.INMAsyncOperation.AddCallback(Object pCallback)
   at NMTools.RecorderRegistration.BeginConnection(OperationDoneHandler h) in D:\Programming\Version 0.9\A_Project\NMTools\RecorderRegistration.cs:line 166
   at NMTools.ConnectionManager.NoRequestRegisterConnection(RecorderRegistration r, Boolean bConnect) in D:\Programming\Version 0.9\A_Project\NMTools\ConnectionManager.cs:line 396
   at NMTools.ConnectionManager.<InitializeFromDatabase>b__0(Object s, EventArgs e) in D:\Programming\Version 0.9\A_Project\NMTools\ConnectionManager.cs:line 339
   at System.Windows.Forms.Timer.OnTick(EventArgs e)
   at System.Windows.Forms.Timer.TimerNativeWindow.WndProc(Message& m)
   at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
   at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
   at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData)
   at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
   at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
   at System.Windows.Forms.Application.Run(Form mainForm)
   at TestApp1.Program.Main() in D:\Programming\Version 0.9\A_Project\TestApp1\Program.cs:line 18

内部异常:

【问题讨论】:

代码看起来不错。您需要更好地解释您在何处以及如何看到“无效演员表”。 DISPID_INVOKE 没有其他任何意义,dispid 值 0 是 DISPID_VALUE 并且总是用于 COM 对象的默认属性。 Execute() 不是属性。 @HansPassant 好吧,我对代码很生气,写了DISPID_INVOKE 而不是DISPID_VALUE。 DISPID 0 也将用于默认属性和默认函数,例如在IE 中,所有 javascript 函数都是具有执行它们的 DISPID = 0 方法的对象,这是我的类接受实现具有 DISPID 的函数的接口的主要原因= 0, 是支持 IE 中的 JavaScript! [ComVisible] 接口和类必须是公开的才能使用。这是另一个错字吗? @HansPassant 非常感谢,我不知道,你想把它写成答案让我接受吗! 那条评论真的解决了问题吗? 【参考方案1】:

好的,从 cmets 可以清楚地看出问题所在。您必须声明 [ComVisible] 接口和类public。 CLR 尊重可访问性,当 .NET 程序也无法使用内部类型时,COM 客户端无法使用内部类型。

一个更好的异常消息会很好,但这是 COM 错误处理课程的标准。它没有任何与可访问性约束类似的东西,因此没有比 E_NOINTERFACE 更具体的错误代码。哪个被翻译成 InvalidCastException。

值得注意的是,这种情况非常罕见,在 .NET 应用程序中使用 [ComVisible] .NET 类没有多大意义。只需通过添加对程序集的引用来直接使用该类。您将摆脱注册要求、笨拙的错误消息和方法调用中的大量开销。对某种你无法摆脱的 COM 层进行取模,这种情况会发生。

【讨论】:

以上是关于在 C# 中实现 DISPID_VALUE 并从 C++ 中调用它的主要内容,如果未能解决你的问题,请参考以下文章

在 C# 中实现 IDisposable [重复]

如何在 C# 中实现 sdbm 哈希函数?

如何在 C# 中实现 ORM

在 C# 中实现加密和解密的最佳实践 [关闭]

在 C# 中实现 runas

如何在 C# 中实现 Base64 URL 安全编码?