当只有CLSID不同时,创建对两个COM组件的.NET项目引用



简介:

如何在.NET项目中创建对两个除CLSID外完全相同的ummanaged COM组件的两个引用?它们具有相同的ProgID和相同的TypeLib。

当我尝试创建第二个引用时,VisualStudio会抱怨TypeLib

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

提前感谢。


详细信息:

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

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

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

当我在WPF项目上使用"添加引用"时,我得到以下错误:无法添加对"XXX类型库"的引用。已存在对此类型库的引用。在添加此引用之前,必须删除引用"XXX">

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

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

目前,我一直在努力将版本2集成到我的WPF应用程序中。我看过很多关于extern alias以及如何手动编辑项目文件以引入别名的文章,但它们似乎都是从托管程序集而不是非托管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(我想是Interop的)作为对我的测试项目的引用。我使用"浏览"选项卡来查找它们
  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);

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

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

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

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

然后,您将在对Activator.CreateInstance的调用中使用该Type(因为所有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对象的类型时,它会创建CLR支持的System.__ComObject(所有COM互操作实例的根)(就像在强制转换时调用IUnkown::QueryInterface,就像在上面的代码中一样)。

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

相关内容

最新更新