C/C++通过COM调用.NET托管程序集的实现(转载)

在.NET托管程序集中,调用非托管的win32 dll 可以通过DllImport 或者Interop中的 P/Invoke 技术将非托管dll生成托管的dll来访问;但反过来,如何在非托管环境下(如C/C++)调用托管的.NET程序集呢?用COM技术就是该问题的一种解决方法:

(1) 首先,创建一个.NET的C# 类库解决方案,并添加两个类,IMyInterface和MyMethods:

接口类 IMyInterface.cs (用于生成COM接口)


using  System ;
using  System . Runtime . InteropServices ;

namespace  DotNet
{
  [ Guid ( "0ED08E85-A627-4894-919C-A404E3420F98" )]
  [ InterfaceType ( ComInterfaceType . InterfaceIsDual )]
  /// <summary>
  /// IMyInterface接口 [提供给C程序调用]
  /// </summary>
  public  interface  IMyInterface
  {
   int  Say ( string  str );
  }
}


Guid的生成用于确定COM的唯一性,在VS.NET2003 开发环境下->工具->创建GUID->Copy-> 替换到上面Guid的值(注意:粘贴板里 形如 0x4c05b60, 0xcda8, 0x4e50, 0xa7, 0x58, 0xcb, 0x9e, 0x5d, 0xb6, 0x9, 0xc5 的值需留到C/C++的代码文件里使用,它和上面的Guid值是一一对应的)

实现类 MyMethods.cs (要调用的托管方法,如果要调用另外的托管程序集,添加程序集引用即可)


using  System ;
using  System . Runtime . InteropServices ;

namespace  DotNet
{
  [ Guid ( "DE21C7D0-CC57-4ed6-AB55-25B4FF8E33F2" )]
  [ ClassInterface ( ClassInterfaceType . None )]
  /// <summary>
  /// 托管程序集 [内部要实现的功能]
  /// </summary>
  public  class  MyMethods : IMyInterface
  {
   public  static  int  SayHello ( string  sInput )
   {
    Console . WriteLine ( "托管程序集开始运行......" );
    Console . WriteLine ( "非托管环境C传递到托管环境.NET(C#)的字符串是:/r/n{0}" , sInput );
    return  sInput . Length ;
   }
   # region  IMyInterface 成员

   public  int  Say ( string  str )
   {
    return  SayHello ( str );
   }

   # endregion
  }
}


该类实现了接口类的相应方法,也需生成Guid值.

(2) 转到 AssemblyInfo.cs:

修改本程序集的版本号,如 [assembly: AssemblyVersion("1.0.0.0")] ,指定版本号用于确定程序集的唯一性,再添加强名称到本程序集,如 [assembly: AssemblyKeyFile("..//..//DotNet.snk")]

在.NET SDK 命令行下生成 强名称文件如下:
sn -k DotNet.snk

(3) 注册到GAC并生成类型库 供非托管程序调用:

托管程序集(DotNet.dll)成功生成后,在.NET SDK命令行下注册得到 DotNet.tlb文件:
regasm DotNet.dll  /tlb DotNet.tlb

//从GAC中注销( 注销该COM接口 )      regasm /unregister DotNet.dll

(4) 创建非托管C++项目(例子是 Win32 控制台程序 VC++ 2003)

添加刚才生成类型库文件DotNet.tlb的调用到头文件 refer.h (DotNet.tlb要复制到项目根目录下)


//
// .NET Specific Header which is included in the C Project
//
# pragma  warning  ( disable :  4278 )
# include  < string . h >

# import  < mscorlib . tlb >  raw_interfaces_only

// Ignoring the server namespace and using named guids:
# if  defined  ( USINGPROJECTSYSTEM )
# import  "../Library/DotNet.tlb"  no_namespace  named_guids
# else   // Compiling from the command line, all files in the same directory
# import  "DotNet.tlb"  no_namespace  named_guids
# endif  

using  namespace  std ;

//
// End .NET Specific Header, #include this header to the C Main 
//



编辑cpp主程序文件如下:


# include  "refer.h"

//来自托管程序集DotNet.dll里 MyMethods 类的GUID值
const  GUID  CLSID_LibraryImplementation  =
{  0xde21c7d0 ,  0xcc57 ,  0x4ed6 ,  0xab ,  0x55 ,  0x25 ,  0xb4 ,  0xff ,  0x8e ,  0x33 ,  0xf2  };
//来自托管程序集DotNet.dll里 IMyInterface 接口类的GUID值
const  GUID  IID_IManagedInterface  =
{  0xed08e85 ,  0xa627 ,  0x4894 ,  0x91 ,  0x9c ,  0xa4 ,  0x4 ,  0xe3 ,  0x42 ,  0xf ,  0x98  };

//COM调用实现过程
void  DoComMethod ()
{
  cout << "C/C++ 调用.NET COM 测试用例:" << endl ;

  IMyInterface  * test  =  NULL ;
  //Enter Single Threaded Apartment (STA)-STA Thread
  CoInitialize ( NULL );
  //Instantiate the COM object in the appropriate apartment
  HRESULT  hr  =  CoCreateInstance ( CLSID_LibraryImplementation ,  NULL ,  CLSCTX_INPROC_SERVER ,  IID_IManagedInterface ,  ( void **)& test );
  if  ( FAILED ( hr ))
  {
   cout << "无法初始化COM接口!" << endl ;
  }
  else
   cout << "COM接口初始化成功!" << endl ;

  cout << endl ;
 
  //默认传入的字符串 :)
  _bstr_t  _str = "abcdefg123" ;
 

  //Say is a function in the C# Library which inturn calls an exposed
  //function on the C# library method
  int  iOut  =  test -> Say ( _str );
  CoUninitialize ();
  test -> Release ();

  cout << endl << "COM接口调用完毕!" << endl << endl ;

  cout << "由COM接口传回的值是: " << iOut << " (即传进去的字符串长度)" << endl ;
  getchar ();
}


然后在 main() 里调用 DoComMethod() 就可以了:)

编译通过之后,将之前生成的托管程序集 DotNet.dll 拷贝到 当前项目目标执行文件的目录下,运行程序,得到如下结果:

C/C++ 调用.NET COM 测试用例:
COM接口初始化成功!

托管程序集开始运行......
非托管环境C传递到托管环境.NET(C#)的字符串是:
abcdefg123

COM接口调用完毕!

由COM接口传回的值是: 10 (即传进去的字符串长度)

由于已经在GAC里注册了该COM,故每次只需要调用 DotNet.dll  即可执行托管程序集的方法!  如果之前未注册到GAC,会引发COM调用异常!

(5) 客户机布署

将托管和非托管的 exe 或 dll 拷贝到运行目录,注册托管程序集 DotNet.dll 到GAC生成COM接口

在安装了.Net Framework 1.1 的客户机上,进入系统目录里的 .net sdk 相应目录(命令行下),如: C:/WINDOWS/Microsoft.NET/Framework/v1.1.4322

运行命令 regasm DotNet.dll /tlb:DotNet.tlb  注册到GAC生成COM接口,附带生成的 DotNet.tlb 文件可以不用它 (注意:DotNet.dll必须指定路径,上面使用的是默认当前目录下   可在命令行下用 set path="DotNet.dll所在的目录" 或 指定 绝对路径),可查看帮助  regasm  /?

同理: 在完成上面的步骤之后可选择使用 gacutil /I DotNet.dll 将 DotNet.dll 放到GAC(全局程序集缓存)中,调用 DotNet.dll 就不需要在程序运行目录中查找 该托管dll了:

因为托管dll的调用首先会使用.NET内置的查找算法自动查找GAC,如果里面没有的话,会查找程序配置文件(*.config)里特定参数的指定路径,如果还没有的话,就默认查找当前程序运行目录里的相应dll,再加上该dll已经指定了具体的版本号,相应的接口访问类指定了Guid唯一值,从而使.NET托管下的程序集调用更高效,解决了以前的Dll  Hell
 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值