zoukankan      html  css  js  c++  java
  • C++如何调用C#开发的dll

    转载  http://www.cnblogs.com/huangmianwu/p/6145044.html

    前言 C
    ++编写的程序为非托管代码,C#编写的程序为托管代码。托管代码虽然提供了其他开发平台没有的许多优势,但由于前期系统及历史版本很多使用的是非托管代码编写的程序,所以CLR提供了一些机制,允许在应用程序中同时包含托管和非托管代码。具体说分为以下三种: 托管代码能调用DLL中的非托管函数。通过P/Invoke(Platform Invoke)机制调用DLL中的函数,如Kernel32.dll等。 托管代码可以使用现有COM组件(服务器)。许多公司都已经实现了大量非托管COM组件。利用来自这些组件的类型库,可创建一个托管程序集来描述COM组件。托管代码可像访问其他任何类型一样访问托管程序集中的类型。 非托管代码可以使用托管类型(服务器)。许多现有的非托管代码要求提供COM组件来确保代码正确工作。使用托管代码可以更简单地实现这些组件,避免所有代码都不得不和引用计数和接口打交道。比如C++调用C#开发的dll。 以上部分文字摘自《CLR via C#》,会比较难懂点。刚好工作中有通过C++调用C#开发的dll的经验,也就是上述第3点。所以想借此文记录下开发的步骤和思路。后续有时间再把上述的1、2点补上,形成一个系列文章。 正文 1、用C#编写dll 该dll只简单实现两个功能:字符串拼接和两个数相加。先创建方法接口:Add和Join。代码如下: 复制代码 [Guid("254D1FBC-416B-422F-AE39-C923E8803396")] public interface ICalc { [DispId(1)] bool Add(string a, string b, out int c); [DispId(2)] void Join(string a, string b, out string c); } 复制代码 为了更全面地介绍调用的方法类型,在这里专门把Add方法返回值定义为bool类型,结果通过输出参数输出,为int类型; Join方法无返回值(void类型),结果通过输出参数输出,为string类型。 其中DispId特性和GUID特性是必须的。DispId按顺序编号即可。GUID的创建步骤为工具-->创建GUID-->选择第5项,复制(针对VS2013)如下图所示: 接下来创建继承ICalc接口的Calc类,实现Add和Join方法,代码如下。也需要创建GUID,步骤同上。 复制代码 [Guid("F963B111-39FA-499D-9172-6102C79BB6E5")] [ClassInterface(ClassInterfaceType.None)] public class Calc : ICalc { public bool Add(string a, string b, out int c) { int int_a; int int_b; if (!Int32.TryParse(a, out int_a)) { c = 0; return false; } if (!Int32.TryParse(b, out int_b)) { c = 0; return false; } c = int_a + int_b; return true; } public void Join(string a, string b, out string c) { c = a + b; return ; } } 复制代码 此外还需要设置“使程序集COM可见”和“为COM互操作注册” “使程序集COM可见”步骤为:项目属性-->“应用程序”项-->"程序集信息"-->勾选“使程序集COM可见”,如下图所示: “为COM互操作注册”设置步骤为:项目属性-->“生成”项-->勾选“为COM互操作注册”,如下图所示: 注意:此项操作需要提供系统管理员权限,启动VS时请以“管理员身份运行”,否则生成解决方案时会出现对注册表项XXX的访问被拒绝的错误。 生成解决方案后,会生成dll和tlb两个文件。到此则已经完成C#端的工作了。 接下来介绍通过regasm.exe生成注册表文件供使用者将dll注册为COM组件。 2、注册dll为COM组件 在本机开发时因为勾选了勾选“为COM互操作注册”选项,所以生成解决方案时已经在本机将该dll注册为COM组件,所以运行时不需再注册, 但如果是在其他机器上运行时,需要将dll注册为COM组件后才可使用。在这里我们通过regasm.exe生成注册表文件供使用者将dll注册为COM组件(其实就是把GUID导入注册表)。 脚本文件如下: regasm E:博客园UnmanagecodeCallManagecodeCalcClassCalcClassinDebugCalcClass.dll regasm E:博客园UnmanagecodeCallManagecodeCalcClassCalcClassinDebugCalcClass.dll /tlb: CalcClass.tlb regasm E:博客园UnmanagecodeCallManagecodeCalcClassCalcClassinDebugCalcClass.dll /regfile: CalcClass.reg 注意使用的regasm.exe版本与开发dll所使用的.NET Framework版本最好保持一致。 运行该脚本生成CalcClass.reg文件。在其他机器上运行该文件,即可注册该COM组件,才能正常使用。 接下来是如何将其封装成COM组件的问题了。 3、将dll封装成COM组件 新建工作空间,选择Win32 Dynamic-Link Library,类型为简单DLL工程。 将上述生成的dll和tlb两个文件拷贝至工作空间文件路径下。 在StdAfx.h头文件下增加以下两行代码导入dll:(内容需要根据tlb文件名和命名空间做更改) #import "CalcClass.tlb" using namespace CalcClass; 在cpp文件中添加以下方法声明(声明为C编译连接方式的外部函数),也可创建头文件后包含进来。 extern "C"_declspec(dllexport)BOOL Add(char* a,char* b,long* c); extern "C"_declspec(dllexport)void Join(char* a,char* b,char* c); 实现声明的两个方法: 复制代码 BOOL Add (char* a,char* b,long* c) { CoInitialize(NULL); CalcClass::ICalcPtr CalcPtr(__uuidof(Calc));//获取Calc所关联的GUID VARIANT_BOOL ret = CalcPtr->Add(_bstr_t(a),_bstr_t(b),c); CalcPtr->Release(); CoUninitialize(); if( ret == -1 ) return 1; else return ret; } void Join (char* a,char* b,char* c){ CoInitialize(NULL); CalcClass::ICalcPtr CalcPtr(__uuidof(Calc));//获取Calc所关联的GUID BSTR temp; CalcPtr->Join(_bstr_t(a),_bstr_t(b),&temp); strcpy(c , _com_util::ConvertBSTRToString(temp)); CalcPtr->Release(); CoUninitialize(); } 复制代码 这里做两点说明: 1、对于VARIANT_BOOL类型做个简单介绍:-1表示true,0表示false。(这点确实颠覆了我们对bool值的常规理解) 2、C#的out参数转换为C++时必须传指针变量,也就是说传参时须对变量进行取指操作,这也是输出参数的本质。(可以通过tlb文件参考调用,或者生成后参考查看tli或tlh文件) 编译成功后则完成了dll封装为COM组件的任务。至此,C++即可调用C#编写的dll了。下面将展示一个调用的DEMO示例。 4、调用DEMO示例 新建工作空间,选择Win32 exe,类型为对话框。设计界面如下所示,添加按钮事件OnAddbtn和OnJoinbtn 声明方法,代码如下: typedef BOOL (* Add)(char* a,char* b,long* c); typedef void (* Join)(char* a,char* b,char* c); OnAddbtn事件响应代码如下: 复制代码 void CCalcComDemoDlg::OnAddbtn() { // TODO: Add your control notification handler code here BOOL ret; long result; char A[255]; char B[255]; CString str_A; CString str_B; GetDlgItem(IDC_EDIT1)->GetWindowText(str_A); GetDlgItem(IDC_EDIT2)->GetWindowText(str_B); strcpy(A,str_A); strcpy(B,str_B); HINSTANCE calc; calc = LoadLibrary(TEXT("CalcCom.dll")); if (NULL == calc) { MessageBox("cant't find dll"); return; } Add _Add=(Add)::GetProcAddress(calc,"Add"); if (NULL == _Add) { MessageBox("cant't find function"); return; } else { ret = _Add(A,B,&result); CString boxMsg; boxMsg.Format("Reslut: %d Message:%ld ",ret,result); MessageBox(boxMsg); } } 复制代码 OnJoinbtn事件响应代码如下: 复制代码 void CCalcComDemoDlg::OnJoinbtn() { // TODO: Add your control notification handler code here char A[255]; char B[255]; CString str_A; CString str_B; GetDlgItem(IDC_EDIT1)->GetWindowText(str_A); GetDlgItem(IDC_EDIT2)->GetWindowText(str_B); strcpy(A,str_A); strcpy(B,str_B); char result[255]; HINSTANCE calc; calc = LoadLibrary(TEXT("CalcCom.dll")); if (NULL == calc) { MessageBox("cant't find dll"); return; } Join _Join=(Join)::GetProcAddress(calc,"Join"); if (NULL == _Join) { MessageBox("cant't find function"); return; } else { _Join(A,B,result); CString boxMsg; boxMsg.Format("Message:%s ",result); MessageBox(boxMsg); } } 复制代码 这里用的是LoadLibrary(TEXT("CalcCom.dll")),默认为exe执行路径下的dll。所以编译完成后将上述生成的COM组件dll拷贝到exe执行路径下。当然也可直接指定dll的路径。 运行程序即可验证是否成功调用C#编写的dll。如下图所示。 Add方法调用结果 Join方法调用结果 附件为程序源代码。仅供参考。 http://files.cnblogs.com/files/huangmianwu/UnmanagecodeCallManagecode.rar
  • 相关阅读:
    leetcode 1 Two sum
    hdu1099
    hdu1098
    函数执行顺序
    浏览器滚动条
    2048的制作
    JavaScript--对象-检查一个对象是否是数组
    JavaScript--格式化当前时间
    JavaScript--模拟验证码
    JavaScript--模拟网络爬虫
  • 原文地址:https://www.cnblogs.com/profession/p/7427390.html
Copyright © 2011-2022 走看看