在现代软件开发中,经常需要将新的.NET代码与旧的COM组件进行互操作。本文将介绍如何使用C#创建可以与COM互操作的.NET组件。将从.NET接口和类的基础讲起,然后展示如何设计.NET组件以实现与COM的无缝互操作。
.NET类是访问.NET对象的基础,无论是从其他.NET代码还是从非托管代码。.NET类封装了程序员希望暴露给其他代码的功能(方法和属性)。.NET接口则是方法和属性的抽象声明,实现该接口的类需要在其实现中提供接口定义中声明的每个方法和属性的代码。声明.NET接口本身不生成任何代码,.NET接口也不能直接调用。但是,任何实现(“继承”)接口的类都必须提供实现接口定义中声明的每个方法和属性的代码。
微软意识到.NET的第一个版本需要一种方式来与过去8年中用于开发应用程序的现有Windows技术(COM)一起工作。因此,微软在.NET运行时中添加了与COM互操作的支持——简称为“COM互操作”。这种支持是双向的:.NET代码可以调用COM组件,COM代码也可以调用.NET组件。
要创建一个托管的.NETC#COM对象,请按照以下步骤操作:
using System.Runtime.InteropServices;
using System.Windows.Forms;
[Guid("03AD5D2D-2AFD-439f-8713-A4EC0705B4D9")]
interface IMyDotNetInterface
{
void ShowCOMDialog();
}
[ClassInterface(ClassInterfaceType.None)]
[Guid("0490E147-F2D2-4909-A4B8-3533D2F264D0")]
class MyDotNetClass : IMyDotNetInterface
{
public MyDotNetClass() {}
public void ShowCOMDialog()
{
System.Windows.Forms.MessageBox.Show("I am a Managed DotNET C# COM Object Dialog");
}
}
编译解决方案后,将在项目目录的obj/debug目录下看到生成的“MyInterop.dll”文件。
为了让COM类在运行时可被客户端访问,COM基础设施必须知道如何定位实现COM类的代码。COM本身不知道.NET类,但.NET提供了一个通用的“代理”DLL——mscoree.dll,它充当COM客户端和.NET类之间的包装器和中介。
在AssemblyInfo.cs文件中硬编码一个特定的版本号:
[assembly: AssemblyVersion("1.0.0.0")]
为程序集创建一个强名称密钥对,并通过AssemblyInfo.cs文件中的AssemblyKeyFile属性指向它:
sn -k TestKeyPair.snk
[assembly: AssemblyKeyFile("TestKeyPair.snk")]
使用以下命令将程序集添加到GAC:
gacutil /i MyInterop.dll
使用REGASM命令注册程序集,并使用“/tlb”选项生成COM类型库:
REGASM MyInterop.dll /tlb:com.MyInterop.tlb
关闭C#项目。
要创建一个非托管C++应用程序来调用.NET托管C# COM,请按照以下步骤操作:
#import "\com.MyInterop.tlb" named_guids raw_interfaces_only
编译解决方案后,它将在项目->debug目录下生成一个“com.myinterop.tlh”文件。可以打开此文件查看内容。这基本上是C# COM代码的代理代码。
现在,可以编写代码来调用.NET托管COM。在调用COM导出函数之前,请添加以下代码:
CoInitialize(NULL);
MyInterop::IMyDotNetInterfacePtr pDotNetCOMPtr;
HRESULT hRes = pDotNetCOMPtr.CreateInstance(MyInterop::CLSID_MyDotNetClass);
if (hRes == S_OK)
{
BSTR str;
pDotNetCOMPtr->ShowCOMDialog();
}
CoUninitialize();