从非托管 C++ 代码调用托管 .NET C# COM 对象






4.50/5 (47投票s)
2006 年 1 月 11 日
4分钟阅读

574355

7206
一篇关于从非托管 C++ 代码调用托管 .NET C# COM 对象 Thus article is an article.
前言
COM 互操作是 Microsoft .NET 的一项功能,它允许托管 .NET 代码使用 Microsoft 的组件对象模型语义与非托管代码进行交互。
本文档面向熟悉开发 COM 组件并理解接口概念的 C# 程序员。我将回顾一些 COM 背景知识,解释 C# 如何与 COM 交互,然后展示如何设计 .NET 组件以实现与 COM 的顺畅交互。
对于那些骨灰级的 COM 专家来说,本文档中的一些内容可能会被过度简化,但对于那些用 .NET 组件来补充自己 COM 代码的开发者来说,本文档所呈现的概念是重要的知识点。
引言
.NET 接口和类
从其他 .NET 代码或非托管代码访问 .NET 对象的基础是类。 .NET 类封装了程序员希望向其他代码公开的功能(方法和属性)。 .NET 接口是声明实现该接口的类应该在其实现中提供的方法和属性的抽象声明。声明 .NET 接口不会生成任何代码,并且 .NET 接口不能直接调用。但是,任何实现(“继承”)接口的类都必须提供实现接口定义中声明的每个方法和属性的代码。
Microsoft 意识到,.NET 的第一个版本就需要一种方法来处理过去 8 年多来用于开发应用程序的现有 Windows 技术:COM。考虑到这一点,Microsoft 在 .NET 运行时中添加了与 COM 互操作的支持 - 简单地称为“COM 互操作”。这种支持是双向的:.NET 代码可以调用 COM 组件,COM 代码也可以调用 .NET 组件。
使用代码
创建托管 .NET C# COM 对象的步骤
- 打开 VS.NET2003->新建项目->Visual C# 项目->类库。
- 项目名称:MyInterop。
- 创建 MyDoNetClass.cs 文件,并添加以下代码行
using System.Runtime.InteropServices; using System.Windows.Forms;
- 创建一个接口
IMyDotNetInterface
。 - 创建一个类
MyDoNetClass
。 - 为
MyDoNetClass
添加以下行[ClassInterface(ClassInterfaceType.None)]
虽然 .NET 类不能从非托管代码直接调用,但 Microsoft 提供了将 .NET 接口包装在非托管代码层中的能力,该层将 .NET 类的所有方法和属性公开,就好像该类是一个 COM 对象一样。要使 .NET 类作为 COM 对象对非托管代码可见,有两个要求:
要求 1
您必须通过 GUID 工具为接口和类分别在代码中添加 GUID - 全局唯一标识符。
- 现在,为接口创建一个 GUID,并为接口添加以下行
[Guid("03AD5D2D-2AFD-439f-8713-A4EC0705B4D9")]
- 现在,为类创建一个 GUID,并为类添加以下行
[Guid("0490E147-F2D2-4909-A4B8-3533D2F264D0")]
- 您的代码将如下所示
using System; using System.Runtime.InteropServices; using System.Windows.Forms; namespace MyInterop { [Guid("03AD5D2D-2AFD-439f-8713-A4EC0705B4D9")] interface IMyDotNetInterface { void ShowCOMDialog(); } [ClassInterface(ClassInterfaceType.None)] [Guid("0490E147-F2D2-4909-A4B8-3533D2F264D0")] class MyDotNetClass : IMyDotNetInterface { // Need a public default constructor for COM Interop. public MyDotNetClass() {} public void ShowCOMDialog() { System.Windows.Forms.MessageBox.Show(“I am a" + " Managed DotNET C# COM Object Dialog”); } } }
- 编译解决方案。
- 编译后,您将在项目目录->obj->debug 目录中看到生成的“MyInterop.dll”文件。
要求 2
COM 类和接口的注册
为了使 COM 类在运行时能够被客户端访问,COM 基础结构必须知道如何定位实现 COM 类的代码。COM 不了解 .NET 类,但 .NET 提供了一个通用的“代理”DLL - mscoree.dll -- 它充当 COM 客户端和 .NET 类之间的包装器和中介。
- 在项目中的 AssemblyInfo.cs 文件中,在
AssemblyVersion
属性中硬编码一个特定的版本号。示例
[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 的步骤
- 打开 VS.NET2003->新建项目->Visual C++ 项目->Win32->Win32 控制台项目。
- 名称:DotNet_COM_Call。
- 在您的 DoNet_COM_Call.cpp 文件中包含以下行
#import “<Full Path>\com.MyInterop.tlb" named_guids raw_interfaces_only
- 编译解决方案。
- 这将在您的项目->debug 目录中生成一个“com.myinterop.tlh”文件。
- 您可以打开此文件查看其内容。这基本上是 C# COM 代码的代理代码。
- 现在,您可以编写代码来调用 .NET 托管 COM。
- 在调用 COM 导出的函数之前,请添加以下代码行
CoInitialize(NULL); //Initialize all COM Components // <namespace>::<InterfaceName> MyInterop::IMyDotNetInterfacePtr pDotNetCOMPtr; // CreateInstance parameters // e.g. CreateInstance (<namespace::CLSID_<ClassName>) HRESULT hRes = pDotNetCOMPtr.CreateInstance(MyInterop::CLSID_MyDotNetClass); if (hRes == S_OK) { BSTR str; pDotNetCOMPtr->ShowCOMDialog (); //call .NET COM exported function ShowDialog () } CoUninitialize (); //DeInitialize all COM Components
- 运行此控制台应用程序。
- 预期结果:将出现一个托管代码 (C#) 对话框,其中包含字符串“I am a Managed DotNET C# COM Object Dialog”。
关注点
在创建 COM 导出的函数的接口、为接口和类创建 GUID 以及注册类是必需的步骤,而完成所有这些总是很有趣的。调用带参数的导出函数也很有趣。