如何从 .NET 对 .NET 中定义的 COM 对象执行后期绑定调用(通过 DispID)?

发布于 2025-01-05 03:37:29 字数 2913 浏览 0 评论 0原文

我想使用 DispID 从另一个 .NET 应用程序调用在 .NET 中定义的 COM 对象上的方法。

我在 .NET 项目中定义了以下类和接口:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;

namespace TestComObject
{
   [ComVisible(true)]
   [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
   [Guid("99476c04-574a-4271-abc8-f1d2615c3d93")]
   public interface IKnowEverything
   {
      [DispId(1030)]
      string GetTheTruth();
   }

   [ComVisible(true)] 
   [Guid("9B869BAF-622A-458B-BF80-2CD6D8A3C541")]
   [ClassInterface(ClassInterfaceType.None)]   
   public class AllKnowing : IKnowEverything
   {
      public string GetTheTruth()
      {
         return "I am King!";
      }
   }
}

我构建它并将其设置为注册 COM 互操作,并使用强密钥进行签名。

现在我想使用另一个 .NET 应用程序的 DispID 调用此方法。我编写了以下测试方法:

static void Main(string[] args)
{
   Guid clsid = new Guid("9B869BAF-622A-458B-BF80-2CD6D8A3C541");
   Type type = Type.GetTypeFromCLSID(clsid, true);
   object instance = Activator.CreateInstance(type);

   string methodName = "GetTheTruth";
   string methodDispIdName = "[DispID=1030]";

   string s1 = (string)type.InvokeMember(methodName, BindingFlags.InvokeMethod, null, instance, null);
   Console.WriteLine("Via name: {0}", s1);

   string s2 = (string)type.InvokeMember(methodDispIdName, BindingFlags.InvokeMethod, null, instance, null);
   Console.WriteLine("Via DispID: {0}", s2);
}

第一次调用成功,应用程序打印“Via name: I am King!”。

但在第二次调用(使用 DispID)时,出现异常:

未处理的异常:System.MissingMethodException:未找到方法“TestComObject.AllKnowing.[DispID=1030]”。

在 OLE-COM 对象查看器中查看类型库时,我看起来没问题:

// Generated .IDL file (by the OLE/COM Object Viewer)
// 
// typelib filename: TestComObject.tlb

[
  uuid(139DAA99-BF76-4057-BCC8-DCB35C153920),
  version(1.0),
  custom(90883F05-3D28-11D2-8F17-00A0C9A6186D, "TestComObject, Version=1.0.0.0, Culture=neutral, PublicKeyToken=58b6bd22fbd98427")

]
library TestComObject
{
    // TLib :     // TLib : mscorlib.dll : {BED7F4EA-1A96-11D2-8F08-00A0C9A6186D}
    importlib("mscorlib.tlb");
    // TLib : OLE Automation : {00020430-0000-0000-C000-000000000046}
    importlib("stdole2.tlb");

    // Forward declare all types defined in this typelib
    dispinterface IKnowEverything;

    [
      uuid(99476C04-574A-4271-ABC8-F1D2615C3D93),
      version(1.0),
      custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, "TestComObject.IKnowEverything")    

    ]
    dispinterface IKnowEverything {
        properties:
        methods:
            [id(0x00000406)]
            BSTR GetTheTruth();
    };

    [
      uuid(9B869BAF-622A-458B-BF80-2CD6D8A3C541),
      version(1.0),
      custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, "TestComObject.AllKnowing")
    ]
    coclass AllKnowing {
        interface _Object;
        [default] dispinterface IKnowEverything;
    };
};

我在这里做错了什么?

I want to invoke a method on a COM object that is defined in .NET from another .NET application using the DispID.

I have the following class and interface defined in a .NET project:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;

namespace TestComObject
{
   [ComVisible(true)]
   [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
   [Guid("99476c04-574a-4271-abc8-f1d2615c3d93")]
   public interface IKnowEverything
   {
      [DispId(1030)]
      string GetTheTruth();
   }

   [ComVisible(true)] 
   [Guid("9B869BAF-622A-458B-BF80-2CD6D8A3C541")]
   [ClassInterface(ClassInterfaceType.None)]   
   public class AllKnowing : IKnowEverything
   {
      public string GetTheTruth()
      {
         return "I am King!";
      }
   }
}

I build it and it is set to register for COM interop, it is signed with a strong key.

Now I want to invoke this method using its DispID from another .NET application. I wrote the following test method:

static void Main(string[] args)
{
   Guid clsid = new Guid("9B869BAF-622A-458B-BF80-2CD6D8A3C541");
   Type type = Type.GetTypeFromCLSID(clsid, true);
   object instance = Activator.CreateInstance(type);

   string methodName = "GetTheTruth";
   string methodDispIdName = "[DispID=1030]";

   string s1 = (string)type.InvokeMember(methodName, BindingFlags.InvokeMethod, null, instance, null);
   Console.WriteLine("Via name: {0}", s1);

   string s2 = (string)type.InvokeMember(methodDispIdName, BindingFlags.InvokeMethod, null, instance, null);
   Console.WriteLine("Via DispID: {0}", s2);
}

The first invocation is successful and the application prints "Via name: I am King!".

But on the second invocation (using the DispID) I get an exception:

Unhandled Exception: System.MissingMethodException: Method 'TestComObject.AllKnowing.[DispID=1030]' not found.

The type library looks okay to me when looking at it in the OLE-COM Object Viewer:

// Generated .IDL file (by the OLE/COM Object Viewer)
// 
// typelib filename: TestComObject.tlb

[
  uuid(139DAA99-BF76-4057-BCC8-DCB35C153920),
  version(1.0),
  custom(90883F05-3D28-11D2-8F17-00A0C9A6186D, "TestComObject, Version=1.0.0.0, Culture=neutral, PublicKeyToken=58b6bd22fbd98427")

]
library TestComObject
{
    // TLib :     // TLib : mscorlib.dll : {BED7F4EA-1A96-11D2-8F08-00A0C9A6186D}
    importlib("mscorlib.tlb");
    // TLib : OLE Automation : {00020430-0000-0000-C000-000000000046}
    importlib("stdole2.tlb");

    // Forward declare all types defined in this typelib
    dispinterface IKnowEverything;

    [
      uuid(99476C04-574A-4271-ABC8-F1D2615C3D93),
      version(1.0),
      custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, "TestComObject.IKnowEverything")    

    ]
    dispinterface IKnowEverything {
        properties:
        methods:
            [id(0x00000406)]
            BSTR GetTheTruth();
    };

    [
      uuid(9B869BAF-622A-458B-BF80-2CD6D8A3C541),
      version(1.0),
      custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, "TestComObject.AllKnowing")
    ]
    coclass AllKnowing {
        interface _Object;
        [default] dispinterface IKnowEverything;
    };
};

What am I doing wrong here?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

若有似无的小暗淡 2025-01-12 03:37:29

这不起作用,因为您在 .NET 中实现了 COM 服务器。 instance成员是一个实际的.NET对象,而不是RCW。从调试器中可以看出,它不是 System.__ComObject,而是实际 .NET 类型的对象。

因此,当您使用 InvokeMember() 时,您将获得它的 System.Reflection 版本,而不是 IDispatch 版本。另一种查看它的方法是使用“getTheTruth”。请注意第一次尝试现在也失败了。 IDispatch 不区分大小写,而 System.Reflection 则不区分大小写。 “[DispID=1030]”技巧不起作用,因为 System.Reflection 不知道有关 disp-ids 的任何信息。

功能,而不是错误,当 CLR 可以直接创建 .NET 对象时,它创建 RCW 是没有意义的。这也不容易实施。

This doesn't work because you implemented the COM server in .NET. The instance member is an actual .NET object, not an RCW. The can tell from the debugger, it is not a System.__ComObject but an object of the actual .NET type.

So when you use InvokeMember(), you get the System.Reflection version of it, not the IDispatch version. Another way to see it is to use "getTheTruth". Note how the first attempt fails now as well. IDispatch is case-insensitive, System.Reflection is not. The "[DispID=1030]" trick doesn't work because System.Reflection doesn't know anything about disp-ids.

Feature, not a bug, it doesn't make sense for the CLR to create the RCW when it can create the .NET object directly. Nor would that be easy to implement.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文