序言

本文介绍一个C++如何调用C#开发的dll实例。

前言

C++编写的程序为非托管代码,C#编写的程序为托管代码。托管代码虽然提供了其他开发平台没有的许多优势,但由于前期系统及历史版本很多使用的是非托管代码编写的程序,所以CLR提供了一些机制,允许在应用程序中同时包含托管和非托管代码。具体说分为以下三种:

  1. 托管代码能调用DLL中的非托管函数。通过P/Invoke(Platform Invoke)机制调用DLL中的函数,如Kernel32.dll等。
  2. 托管代码可以使用现有COM组件(服务器)。许多公司都已经实现了大量非托管COM组件。利用来自这些组件的类型库,可创建一个托管程序集来描述COM组件。托管代码可像访问其他任何类型一样访问托管程序集中的类型。
  3. 非托管代码可以使用托管类型(服务器)。许多现有的非托管代码要求提供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()]
bool Add(string a, string b, out int c);
[DispId()]
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 = ;
return false;
}
if (!Int32.TryParse(b, out int_b))
{
c = ;
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:\博客园\UnmanagecodeCallManagecode\CalcClass\CalcClass\bin\Debug\CalcClass.dll
regasm E:\博客园\UnmanagecodeCallManagecode\CalcClass\CalcClass\bin\Debug\CalcClass.dll /tlb: CalcClass.tlb
regasm E:\博客园\UnmanagecodeCallManagecode\CalcClass\CalcClass\bin\Debug\CalcClass.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 == - )
return ;
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[];
char B[]; 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\nMessage:%ld\n",ret,result);
MessageBox(boxMsg);
}
}

OnJoinbtn事件响应代码如下:

void CCalcComDemoDlg::OnJoinbtn()
{
// TODO: Add your control notification handler code here
char A[];
char B[]; 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[];
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\n",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

C++如何调用C#开发的dll的更多相关文章

  1. c#调用c++开发的dll const char* 返回值接收问题

    原文:c#调用c++开发的dll const char* 返回值接收问题 用c#调用视频接口相关的dll,dll使用c++开发. c++接口定义如下: PLATFORM const char* Pla ...

  2. 用C#.NET调用Java开发的WebService传递int,double问题,出现java无法获得值!

    https://www.cnblogs.com/zhbsh/archive/2013/04/22/3035477.html 用C#.NET调用Java开发的WebService时,先在客户端封装的带有 ...

  3. Thrift实现C#调用Java开发步骤详解

    概述 Thrift实现C#调用Java开发步骤详解 详细 代码下载:http://www.demodashi.com/demo/10946.html Apache Thrift 是 Facebook ...

  4. PB调用C#编写的DLL

    C#以其简单易用,功能强大深受大家喜爱.PowerBuilder作为C/S的MIS开发工具,十分简单灵活,开发时间短,开发及维护成本低,一直是中小企业信息管理系统的首选开发工具.但是PB的局限性限制了 ...

  5. 转【C#调用DLL的几种方法,包括C#调用C\C++\C#DLL】

    C#中dll调用方法   一.      DLL与应用程序 动态链接库(也称为DLL,即为“Dynamic Link Library”的缩写)是Microsoft Windows最重要的组成要素之一, ...

  6. 【转载】java调用C++写的DLL

    用java调用C++写的DLL一直以来都是一个比较麻烦但又很常见的问题. 我们知道,使用 JNI 调用 .dll/.so 共享类库是非常非常麻烦和痛苦的. 如果有一个现有的 .dll/.so 文件,如 ...

  7. C#使用CLR/C++的DLL间接调用Native C++的DLL

    C#使用CLR/C++的DLL间接调用Native C++的DLL 开发环境:win 7  VS2010 简介:C#的exe使用CLR/C++间接调用Native C++的DLL. 第一步:创建一个C ...

  8. C#调用Matlab生成的Dll

    问题描述:最近开发需要调用matlab生成的DLL,在New MWNumericArray 对象的时候报错,提示未将对象引用到对象的实例. 问题分析:因为MWArray.dll是Matlab提供的DL ...

  9. C#调用C++编写的DLL函数, 以及各种类型的参数传递 (转载)

        C#调用C++编写的DLL函数, 以及各种类型的参数传递 1. 如果函数只有传入参数,比如: C/C++ Code Copy Code To Clipboard //C++中的输出函数 int ...

随机推荐

  1. 数据库中的two phase locking

    数据库中的two phase locking 两段锁协议是指每个事务的执行可以分为两个阶段:生长阶段(加锁阶段)和衰退阶段(解锁阶段). 加锁阶段:在该阶段可以进行加锁操作.在对任何数据进行读操作之前 ...

  2. 安装zookeeper

    从zookeeper官方网站下载安装包:zookeeper-3.4.9.tar.gz 解压安装 tar xvf zookeeper-3.4.9.tar.gz -C /usr/java cd /usr/ ...

  3. 学习设计模式第二十七 - GoF之外简单工厂模式

    示例代码来自<深入浅出设计模式>和<大话设计模式> 概述 简单工厂模式又被称为静态工厂模式,属于类的创建型模式.其实质是由一个工厂类根据传入的参量,动态决定应该创建出哪一个产品 ...

  4. 黑科技:gif二维码

    本篇文章是缘于在微博上看到了一的有意思的东西.由于腾讯与阿里的竞争关系,如果你是一个大V,在微博上发布微信的二维码会被屏蔽掉.于是有人发现了这样一个现象:人眼有视觉暂留效应,手机的摄像头由于捕捉影像的 ...

  5. 谈谈对Spring IOC的理解

    学习过Spring框架的人一定都会听过Spring的IoC(控制反转) .DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC .DI这两个概念是模糊不清的,是很难理解的,今天和大家 ...

  6. RxAndroid+Retrofit+MVVM(1)OKHttp

    1)Gradlecompile 'com.squareup.okhttp:okhttp:2.4.0'compile 'com.squareup.okio:okio:1.5.0' 2)Get //创建o ...

  7. 体验了微信小程序,发现安卓用户终于把果粉“碾压”了一次

    今天早上,张小龙在微信公开课上分享了小程序的理念,并且公布了小程序将于1月9日上线. 为了体现张小龙对未来程序形态的理解,小程序有四个特定:无需安装.触手可及.用完即走.无需卸载.今天,36氪刚好有机 ...

  8. ESP8266刷AT固件与nodemcu固件

    这回是使用的这一款 因为这款默认的是支持AT指令的固件,,所以我们就刷nodemcu的 先看接线 GPIO0 默认是工作模式(不接线).如果接了低电平就是下载模式(给模块刷固件!!)所以接低电平.CH ...

  9. 考勤系统——代码分析datagrid

    datagrid是easyui的控件,DataGrid以表格形式展示数据,并提供了丰富的选择.排序.分组和编辑数据的功能支持.DataGrid的设计用于缩短开发时间,并且使开发人员不需要具备特定的知识 ...

  10. 搭建LNAMP环境(六)- PHP7源码安装MongoDB和MongoDB拓展

    上一篇:搭建LNAMP环境(五)- PHP7源码安装Redis和Redis拓展 一.安装MongoDB 1.创建mongodb用户组和用户 groupadd mongodb useradd -r -g ...