1

简短的:

如何在 .NET 项目中创建两个对除了 CLSID 之外相同的非托管 COM 组件的两个引用?它们具有相同的 ProgID 和相同的类型库。

当我尝试创建第二个引用时,类型库是 Visual Studio 抱怨的:

无法添加对“XXX 类型库”的引用。已存在对此类型库的引用。在添加此之前,您必须删除引用“XXX”。

提前致谢。


详细的:

我正在创建一个 WPF 应用程序,它提供与另一个应用程序的集成。另一个应用程序有点不寻常,因为它的版本作为单独的应用程序安装。用户可以在他们的计算机上同时运行多个版本的应用程序。

我的单个 WPF 应用程序应该允许用户选择他们想要使用的其他应用程序的版本。另一个应用程序为每个安装的版本都有一个非托管 COM DLL,我用它来创建我的 WPF 应用程序和这个其他应用程序之间的集成。

我的 WPF 应用程序在使用对版本 1 的非托管 COM 组件的引用的其他应用程序的第一个版本中运行良好。当我尝试引用第二个版本的 COM 组件时,问题就开始了。

在 WPF 项目中使用“添加引用”时出现以下错误: 无法添加对“XXX 类型库”的引用。已存在对此类型库的引用。在添加此之前,您必须删除引用“XXX”。

对注册表的检查表明,两个版本的 COM 组件的注册表信息的唯一区别是 CLSD 和实际 DLL 的路径。两个组件的 ProgID 和 TypeLib 值相同。

由于其他应用程序的版本确实被视为单独的应用程序,我希望将 COM 组件安装为不同的应用程序,并且我正在与公司合作开发下一个版本。

现在我一直在尝试将第 2 版集成到我的 WPF 应用程序中。我看过很多关于 外部别名以及如何手动编辑项目文件以引入别名的帖子,但它们似乎都是从托管程序集而不是非托管 COM 组件开始的。对于引用的非托管 COM 组件,项目文件的内容是不同的,并且由于我无法创建第二个引用,因此向项目文件中的引用添加别名标记对我没有帮助。

我见过人们谈论使用 System.Addin 和反射,但我想确认在研究更复杂的方法之前没有更直接的方法。

感谢您阅读到这里!


按照 CasperOne 的建议,我做了以下事情:

  1. 使用 Visual Studio 命令提示符运行以下命令:tlbimp "c:\xxx1.dll" /out:"c:\NETxxx1.dll"
  2. 使用 Visual Studio 命令提示符运行以下命令:tlbimp "c:\xxx2.dll" /out:"c:\NETxxx2.dll"
  3. 添加了两个新创建的 DLL(我猜是互操作的)作为对我的测试项目的引用。我使用“浏览”选项卡来查找它们。
  4. 我添加了几乎遵循 casperOne 的代码示例的代码。

我对为我的 COM 接口使用什么名称感到有点恐慌。在我使用的 COM 对象的情况下,有一个主接口对象,并且我从该 COM 对象使用的所有其他对象都是从该主接口对象内实例化的(即,我进行如下调用:Foo x = y. GetNewFoo();)。

using X1 = NETXXX1; using X2 = NETXXX2; Guid clsid1 = new Guid("xxx..."); Type type1 = Type.GetTypeFromCLSID(clsid1); X1.IApplication a1 = (X1.IApplication)Activator.CreateInstance(type1); X1.FooList f1 =a1.GetFooList(); Guid clsid2 = new Guid("yyy..."); Type type2 = Type.GetTypeFromCLSID(clsid2); X2.IApplication a2 = (X2.IApplication)Activator.CreateInstance(type2); X2.FooList f2 = a2.GetFooList(); foreach(X1.Foo f in f1) Console.WriteLine(f.Name); Console.WriteLine("*****"); foreach(X2.Foo f in f2) Console.WriteLine(f.Name);

4

1 回答 1

1

在这种情况下,我建议不要通过 Visual Studio 中的“添加引用”对话框添加引用。

相反,使用类型库导入器从命令行生成引用,然后在您的项目中正常设置对它的引用(它将是一个 .NET 程序集)。

完成此操作后,您将拥有两组类型,两个类实现的接口集,以及这些接口实现的类定义之一。

您将使用类上的GetTypeFromCLSID方法来获取COM 对象的 .NET ,而不是使用构造函数来创建此类。TypeType

然后,您将Type在调用中使用它Activator.CreateInstance(因为所有 COM 对象都有默认构造函数,这将起作用)并将结果转换为接口定义,如下所示:

// The CLSID of the implementation you want to use.
Guid clsid = ...;

// Get the type.
Type type = Type.GetTypeFromCLSID(clsid);

// Create and cast to your interface.
var i = (IMyComInterface) Activator.CreateInstance(type);

Activator.CreateInstance遇到一个 COM 对象类型时,它会创建一个System.__ComObjectCLR 支持的(所有 COM 互操作实例的根)(就像IUnkown::QueryInterface在转换时调用,就像在上面的代码中一样)。

此外,如果您愿意,您可以在代码中定义接口,然后转换为这些接口,您不一定需要类型库导入器来生成它们。

于 2012-11-06T18:35:24.313 回答