C++调C#代码

 
转自:http://www.codeproject.com/csharp/ManagedCOM.asp

 

Calling Managed .NET C# COM Objects from Unmanaged C++ Code

Preface

COM Interoperability is the feature of Microsoft .NET that allows managed .NET code to interact with unmanaged code using Microsoft's Component Object Model semantics.

This article is geared towards C# programmers who are familiar with developing COM components and familiar with the concept of an interface. I'll review some background on COM, explain how C# interacts with COM, and then show how to design .NET components to smoothly interact with COM.

For those die-hard COM experts, there will be some things in this article that are oversimplified, but the concepts, as presented, are the important points to know for those developers supplementing their COM code with .NET components.

Introduction

.NET Interfaces and Classes

The basis for accessing .NET objects either from other .NET code or from unmanaged code is the Class. A .NET class represents the encapsulation of the functionality (methods and properties) that the programmer wants to expose to other code. A .NET interface is the abstract declaration of the methods and properties that classes which implement the interface are expected to provide in their implementations. Declaring a .NET interface doesn't generate any code, and a .NET interface is not callable directly. But any class which implements ("inherits") the interface must provide the code that implements each of the methods and properties declared in the interface definition.

Microsoft realized that the very first version of .NET needed a way to work with the existing Windows technology used to develop applications over the past 8+ years: COM. With that in mind, Microsoft added support in the .NET runtime for interoperating with COM - simply called "COM Interop". The support goes both ways: .NET code can call COM components, and COM code can call .NET components.

Using the code

Steps to create a Managed .NET C# COM Object:

  1. Open VS.NET2003->New Project->Visual C# Projects->Class Library.
  2. Project name: MyInterop.
  3. Create MyDoNetClass.cs file, and add the following lines of code:
    using System.Runtime.InteropServices;
    using System.Windows.Forms;
  4. Create an Interface IMyDotNetInterface.
  5. Create a class MyDoNetClass.
  6. Add the following line for MyDotNetClass:
    [ClassInterface(ClassInterfaceType.None)]

Although a .NET class is not directly invokable from unmanaged code, Microsoft has provided the capability of wrapping a .NET interface in an unmanaged layer of code that exposes the methods and properties of the .NET class as if the class were a COM object. There are two requirements for making a .NET class visible to unmanaged code as a COM object:

Requirement 1:

You have to add GUIDs - Globally Unique Identifiers - into your code for the interface and the class separately, through a GUID tool.

  1. Now, create a GUID for the Interface, and add the following line for the interface:
    [Guid("03AD5D2D-2AFD-439f-8713-A4EC0705B4D9")]
  2. Now, create a GUID for the class, and add the following line for the class:
    [Guid("0490E147-F2D2-4909-A4B8-3533D2F264D0")]
  3. Your code will look like:
    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”);
            }
        }
    }
  4. Compile the solution.
  5. You will see inside the project directory->obj->debug directory, the file “MyInterop.dll” generated after compilation.

Requirement 2:

Registration of the COM Class and Interfaces

For a COM class to be accessible by the client at runtime, the COM infrastructure must know how to locate the code that implements the COM class. COM doesn't know about .NET classes, but .NET provides a general "surrogate" DLL - mscoree.dll -- which acts as the wrapper and intermediary between the COM client and the .NET class.

  1. Hard-code a specific version number in your AssemblyVersion attribute in the AssemblyInfo.cs file which is in your project.

    Example:

    [assembly: AssemblyVersion("1.0.0.0")]
  2. Create a strong-name key pair for your assembly and point to it via the AssemblyKeyFile attribute in the AssemblyInfo.cs file which is in your project. Example:
    sn -k TestKeyPair.snk
    [assembly: AssemblyKeyFile("TestKeyPair.snk")]
  3. Add your assembly to the GAC using the following command:
    gacutil /i MyInterop.dll
  4. Register your assembly for COM by using the REGASM command along with the "/tlb" option to generate a COM type library.
    REGASM MyInterop.dll /tlb:com.MyInterop.tlb
  5. Close the C# project.

Steps to create an Unmanaged C++ application to call a .NET Managed C# COM

  1. Open VS.NET2003->New Project->Visual C++ Projects->Win32->Win32 Console Project.
  2. Name: DotNet_COM_Call.
  3. Include the following line in your DoNet_COM_Call.cpp file:
    #import<Full Path>/com.MyInterop.tlb" named_guids raw_interfaces_only
  4. Compile the solution.
  5. It will generate a “com.myinterop.tlh” file into your project->debug directory.
  6. You can open this file and see the contents. This is basically the proxy code of the C# COM code.
  7. Now, you can write the code to call the .NET Managed COM.
  8. Please add the following lines of code before calling the COM exported functions:
    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
  9. Run this console application.
  10. Expected result: a managed code (C# ) dialog should appear with the string “I am a Managed DotNET C# COM Object Dialog”.

Points of Interest

While creating an Interface for COM exported functions, creating GUIDs for the Interface and the class and registering the class are required steps, and doing all this is always interesting and fun. Calling parameterized exported functions also is very interesting.

一 组件基础 1 软件开发的阶段 1.1 结构化编程 采用自顶向下的编程方式,划分模块 和功能的一种编程方式。 1.2 面向对象编程 采用对象的方式,将程序抽象成类, 模拟现实世界,采用继承、多态的方式 设计软件的一种编程方式。 1.3 面向组件编程 将功能和数据封装成二进制代码,采用 搭积木的方式实现软件的一种编程方式。 2 组件和优点 2.1 组件 - 实际是一些可以执行的二进 制程序,它可以给其他的应用程序、操 作系统或其他组件提供功能 2.2 优点 2.2.1 可以方便的提供软件定制机制 2.2.2 可以很灵活的提供功能 2.2.3 可以很方便的实现程序的分布式 开发。 3 组件的标准 - COM(Component Object Model ) 3.1 COM是一种编程规范,不论任何开发语言 要实现组件都必须按照这种规范来实现。 组件和开发语言无关。 这些编程规范定义了组件的操作、接口的 访问等等。 3.2 COM接口 COM接口是组件的核心,从一定程度上 讲"COM接口是组件的一切". COM接口给用户提供了访问组件的方式. 通过COM接口提供的函数,可以使用组件 的功能. 4 COM组件 4.1 COM组件-就是在Windows平台下, 封装在动态库(DLL)或者可执行文件(EXE) 中的一段代码,这些代码是按照COM的 规范实现. 4.2 COM组件的特点 4.2.1 动态链接 4.2.2 与编程语言无关 4.2.3 以二进制方式发布 二 COM接口 1 接口的理解 DLL的接口 - DLL导出的函数 类的接口 - 类的成员函数 COM接口 - 是一个包含了一组函数指针 的数据结构,这些函数是由组件实现的 2 C++的接口实现 2.1 C++实现接口的方式,使用抽象类 定义接口. 2.2 基于抽象类,派生出子类并实现 功能. 2.3 使用 interface 定义接口 interface ClassA { }; 目前VC中,interface其实就是struct 3 接口的动态导出 3.1 DLL的实现 3.1.1 接口的的定义 3.1.2 接口的实现 3.1.3 创建接口的函数 3.2 DLL的使用 3.2.1 加载DLL和获取创建接口的函数 3.2.2 创建接口 3.2.3 使用接口的函数 4 接口的生命期 4.1 问题 在DLL中使用new创建接口后,在用户 程序使用完该接口后,如果使用delete 直接删除,会出现内存异常. 每个模块有自己的内存堆(crtheap) EXE - crtheap DLL - crtheap new/delete/malloc/free默认情况 下都是从自己所在模块内存堆(crtheap) 中分配和施放内存.而各个模块的 这个内存堆是各自独立.所以在DLL中 使用new分配内存,不能在EXE中delete. 4.2 引用计数和AddRef/Release函数 引用计数 - 就是一个整数,作用是 表示接口的使用次数 AddRef - 增加引用计数 +1 Release - 减少引用计数 -1, 如果 当引用计数为0,接口被删除 4.3 使用 4.3.1 创建接口 4.3.2 用AddRef,增加引用计数 4.3.3 使用接口 4.3.4 用Release,减少引用计数 4.4 注意 4.4.1 在用Release之后,接口指针 不能再使用 4.4.2 多线程情况下,接口引用计数 要使用原子锁的方式进行加减 5 接口的查询 5.1 每个接口都具有唯一标识 GUID 5.2 实现接口查询函数 QueryInterface 6 IUnknown 接口 6.1 IUnknown是微软定义的标准接口 我们实现所有接口就是继承这个接口 6.2 IUnknown定义了三个函数 QueryInterface 接口查询函数 AddRef 增加引用计数 Release 减少引用计数 7 接口定义语言 - IDL(Interface Definition Language ) 7.1 IDL和MIDL IDL - 定义接口的一种语言,与开发 语言无关. MIDL.EXE - 可以将IDL语言定义接口, 编译成C++语言的接口定义 7.2 IDL的基础 import "XXXX.idl" [ attribute ] interface A : interface_base { } 7.2.1 Import 导入,相当于C++的 #include 7.2.2 使用"[]"定义区域,属性描述 关键字 1) object - 后续是对象 2) uuid - 定义对象GUID 3) helpstring - 帮助信息 4) version - 版本 5) point_default - 后续对象 中指针的默认使用方式 比如: uniqune - 表示指针可以 为空,但是不能修改 7.2.3 对象定义 1) 父接口是IUnknown接口 2) 在对象内添加函数,函数定义必须 是返回 HRESULT. HRESULT是32位整数,返回函数是否 执行成功,需要使用 SUCCESSED和 FAILED宏来判断返回值.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值