如何从C#调用COM对象

7

首先,我意识到这里有很多帖子都讨论了这个问题。我肯定已经阅读了超过20篇,但它们都没有给出我需要的答案。

我编写了一个微小的C#测试COM DLL,其中只有一个方法,在消息框中打印“我活着了!”。使用管理员身份运行VStudio,我可以构建和注册COM对象。我已成功从VBA调用该对象并运行该方法。而且我可以在VStudio的添加引用/ COM对话框中看到COM接口的名称。这使我认为对象已经正确构建、注册和可用。

现在,我正在尝试从控制台C#应用程序中调用它。与许多其他人一样,我正在尝试找出在C#中获取对象的VBA“CreateObject(”DLLName.ClassName“)”代码的等效方式。

一种方法是仅将DLL添加到我的控制台应用程序项目的引用中。我通过添加引用对话框的项目部分指向程序集,而不是通过对话框的COM部分指向程序集。然后我可以简单地说var o = new MyComImplementationClass();并像处理任何其他类一样处理它。那样可以工作,但这意味着我的控制台应用程序是作弊的,并没有通过通常的COM GAC接口使用COM对象。

另一种方法(虽然不起作用,但我希望它能够起作用)是通过添加引用对话框上的COM选项卡添加引用。我可以看到它,但VS抗议说“XXX.tlb文件是从.NET程序集导出的。应该添加对程序集的引用。”这使我回到上面的解决方案,我认为这意味着我的应用程序在作弊。(例如,我不必在VBA测试应用程序中添加引用。)

另一种方法是使用下面代码片段所示的Type.GetTypeFromProgId。但我也无法让它工作。我一定是传入了不正确的ProgID字符串 - 我有一点感觉它与注册表信息有关,而不是我在VBA中向CreateObject()提供的相同的“DLLName.ClassName”字符串。

  public static dynamic ComObjectGet () {
    const string progID = "ComExampleDLLName.ComImplementationClassName";
    Type foo = Type.GetTypeFromProgID (progID);
    dynamic COMobject = Activator.CreateInstance (foo);
    return COMobject;
  }

更糟的是,在这个MSDN示例页面上,它说:“此方法是为COM支持提供的。在Microsoft .NET Framework中不使用程序ID,因为它们已被命名空间的概念取代。”所以可能我根本不应该使用 GetTypeFromProgID
如果有帮助的话,我可以在C#中使用VSTO调用MSOffice主要互操作程序集。但它们从“添加引用”对话框的COM选项卡加载(这也是我想从中加载我的COM库的地方)。
为了清晰起见,我的COM DLL名称为ComExampleLibrary.dll。默认命名空间是ComExampleNamespace。接口名称为IComInterface,实现类名为ComImplementation。内部方法名称为Run。
是否有人能给我提供指令或代码片段,以便从C#中调用COM对象(不仅仅是我编写的对象)的正确和批准方法?谢谢。

1
你可以使用Type.GetTypeFromCLSID代替Type.GetTypeFromProgID - Klaus Gütter
我理解您想要将COM部署到GAC并能够从VS>引用>COM>ComExampleLibrary.dll中添加它,是这样吗? - Clint
1
使用dynamic将起作用。否则,您可以使用ProgIdAttribute强制指定progid,否则它会自动生成:https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.progidattribute?view=netframework-4.8但是,为什么要首先使用COM从C#对象调用C#对象呢? - Simon Mourier
@KlausGütter,谢谢您。GetTypeFromCLSID之所以有效是因为我有我编写的COM实现类的GUID。但这也算作弊吗?我想像其他任何COM对象一样,在AddReference对话框的COM部分中添加对COM对象的引用。 - Kevin
嘿,@Kevin, 你的托管 DLL 名称为:"COMExampleLibrary.dll" 所以你尝试过从 Visual Studio 命令提示符中执行 gacutil -i COMExampleLibrary.dll 吗? - Clint
显示剩余3条评论
1个回答

11

感谢那些帮助我的人,这是答案。 GetTypeFromProgIDGetTypeFromCLSID 都能像下面展示的那样工作。我的问题是,在调用 GetTypeFromProgID 时我使用了 "AssemblyName.ClassName" 而不是 "Namespace.ClassName"。

 public static dynamic ComObjectGet () {
    const string progID = "ComExampleNamespace.ComImplementation";
    Type foo = Type.GetTypeFromProgID (progID);

    //var bar = Guid.Parse ("99929AA7-0334-4B2D-AC74-5E282A12D06C");
    //Type foo = Type.GetTypeFromCLSID (bar);

    dynamic COMobject = Activator.CreateInstance (foo);
    return COMobject;
  }

所以我的原始代码是正确的,但我传入了错误的参数。这段代码等同于VBA的CreateObject("Namespace.ClassName")调用。

我仍然不知道为什么我不能像对待其他COM对象一样在“添加引用”对话框的COM选项卡中添加对COM项目的引用。我想这是一个不同的问题。


我在网上找到了一条评论,解决了COM引用问题。这对我来说听起来很有说服力。“据我所知,这没有解决方法。我猜微软认为让托管程序集(我的EXE文件)通过托管包装程序集(.tlb文件)访问另一个托管程序集(我的COM DLL),再通过COM接口包装器(我的IComExampleInterface)实现第二个托管程序集(我的ComExampleImplementation)是一种不正当的方式。也许他们不希望它成为绕过私有/全局程序集绑定规则的常见方法。” - Kevin

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