对C#调用C++的dll的一点思考
最近在对接C++程序的时候碰到了一些问题,然后花了一段时间才解决,今天就这些小问题来做一个总结,很多时候由于对另外一种开发语言的不熟悉,会在使用的过程中遇到很多的问题,这些问题看似简单但是背后却有很多的东西需要去总结的,下面就最近遇到的两个调用C++ API的示例来做一个总结。
1 首先我们看看如果有下面的一些C++的接口,我们怎样来进行对接。
typedef unsigned long KEIO_LONG;
typedef char KEIO_BYTE;
typedef void * KEIO_HANDLE; int KEIOAPI OpenKeio(KEIO_HANDLE * phKeio);
int KEIOAPI CloseKeio(KEIO_HANDLE hKeio); int KEIOAPI GetKeioTime1(KEIO_HANDLE hKeio, KEIO_LONG * pTime);
int KEIOAPI GetKeioCode1(KEIO_HANDLE hKeio, KEIO_BYTE * pKeioCodeBuf, KEIO_LONG nKeioCodeBuf);
int KEIOAPI GetKeioTime2(KEIO_HANDLE hKeio, KEIO_LONG * pTime);
int KEIOAPI GetKeioCode2(KEIO_HANDLE hKeio, int flag, KEIO_BYTE * pKeioCodeBuf, KEIO_LONG nKeioCodeBuf);
int KEIOAPI GetKeioCode3(KEIO_HANDLE hKeio, KEIO_BYTE * pKeioCodeBuf, KEIO_LONG nKeioCodeBuf);
我们知道C#中调用C++的dll的时候是需要经过平台转换的,那么上面的API要经过转换以后得到的具体的类型是什么样的呢?
#region 接口函数
[DllImport("keIO.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern int OpenKeio(ref IntPtr phKeio); [DllImport("keIO.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern int CloseKeio(IntPtr phKeio); [DllImport("keIO.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern int GetKeioTime1(IntPtr hKeio, ref int pTime); [DllImport("keIO.dll", CharSet = CharSet.Ansi,CallingConvention = CallingConvention.StdCall)]
public static extern int GetKeioCode1(IntPtr hKeio, StringBuilder pKeioCodeBuf,int nKeioCodeBuf); [DllImport("keIO.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern int GetKeioTime2(IntPtr hKeio,ref int pTime); [DllImport("keIO.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern int GetKeioCode2(IntPtr hKeio, int flag, StringBuilder pKeioCodeBuf, int nKeioCodeBuf); #endregion
这里我们需要特别注意的是GetKeioCode1和GetKeioCode2这两个函数,刚开始在调用的时候一直在报错“”托管的PInvoke签名和非托管的目标签名不匹配“”那么到底是哪里类型不匹配呢?我们来看看这个函数的三个变量,第一个很明显是一个指针类型,这个在C#语言中通过IntPtr类型来匹配,第二个是无符号的char类型的指针,刚开始一直把精力放在这个上面,通过查阅相关资料,通过使用StringBulider类型来匹配,注意第二个参数是传出的类型,也就四相当于C#中的out类型,传入StringBulider类型的变量能够获取函数内部的赋值,第三个“很自然”的使用long类型去匹配,一切看似都是非常自然,但是在使用的时候总是报“类型不匹配”,到底是哪个类型不匹配呢?
报错的截图如下:

找了很长时间,最后发现是C++中的long类型的变量和C#中long类型的变量字节数不匹配,C++(编译器编译成32位时)中long类型占4个字节而C#中long的类型占8个字节,字节数不匹配,最终将GetKeioCode1和GetKeioCode2中第三个变量用C#中的int变量去替换就好了,这给了后面的开发对接工作一个警示,关于C++中变量的字节数目的定义可以参考下下面的这篇博客:http://blog.csdn.net/acelit/article/details/69218776,C#中通过下面的测试结果可以有一个直观的了解:
Debug.WriteLine("在32位模式下:");
Debug.WriteLine(string.Format("Int16型变量占用的字节数:{0}", sizeof(Int16)));
Debug.WriteLine(string.Format("int型变量占用的字节数:{0}",sizeof(int)));
Debug.WriteLine(string.Format("Int32型变量占用的字节数:{0}", sizeof(Int32)));
Debug.WriteLine(string.Format("Int64型变量占用的字节数:{0}", sizeof(Int64)));
Debug.WriteLine(string.Format("char型变量占用的字节数:{0}", sizeof(char)));
Debug.WriteLine(string.Format("long型变量占用的字节数:{0}", sizeof(long)));
Debug.WriteLine(string.Format("double型变量占用的字节数:{0}", sizeof(double)));
Debug.WriteLine(string.Format("decimal型变量占用的字节数:{0}", sizeof(decimal)));
输出的结果是:
在32位模式下:
Int16型变量占用的字节数:2
int型变量占用的字节数:4
Int32型变量占用的字节数:4
Int64型变量占用的字节数:8
char型变量占用的字节数:2
long型变量占用的字节数:8
double型变量占用的字节数:8
decimal型变量占用的字节数:16
那么同样编译成x64程序呢?
在64位模式下:
Int16型变量占用的字节数:2
int型变量占用的字节数:4
Int32型变量占用的字节数:4
Int64型变量占用的字节数:8
char型变量占用的字节数:2
long型变量占用的字节数:8
double型变量占用的字节数:8
decimal型变量占用的字节数:16
看了,好像没有区别,那么到底编译成x86和编译成x64的区别是什么呢?这里也可以参考下面这篇博客:http://blog.csdn.net/zuguangboy/article/details/51509670
我们可以看到在这里编译成32位和64位程序,对变量的占用字节数是没有区别的,另外关于C#引用C++的时候需要做以下几点的总结:
A:普通的指针类型,使用IntPtr来进行转换就可以了,如果是指针的指针就在IntPtr的前面加上一个ref表示对指针的引用。
B:int类型的指针、long类型的指针在作为函数参数使用的时候在C#中调用统一在变量前面加ref,例如:ref int、ref long
C:关于char类型的指针,如果仅仅是作为参数传入的话用string,如果当前参数需要返回到调用者,那么使用StringBulider变量
D:这里还需要特别注意的是C++编译成的dll的版本号,如果C++编译成的版本号为x86即32位程序,那么.net程序必须也编译成32位的,这一点特别需要注意,dll的类型必须完全统一。
2 我们再来看一个关于返回值的例子
C++函数的原型
#ifndef VMAXAPI
#define VMAXAPI __stdcall Const char * VMAXAPI VmaxSDKGetSystemVersion(void *pCtrl);
C#对接的类型
#region 获取当前软件的版本号 2017-11-27
[DllImport("VmaxSDK.dll", CharSet = System.Runtime.InteropServices.CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr VmaxSDKGetSystemVersion(IntPtr pLPtr);
#endregion
在对接之后该怎样来使用呢?我们知道C++中返回的是一个char类型的指针,实际上最终返回的是一个字符串,但这里C#是不能够直接使用string来对接的。
对接的时候我们需要进行一个转换:
IntPtr currentVersionPtr = Processor.DICS.VmaxSDKGetSystemVersion(m_DICSPtr);
string currentVersion = Marshal.PtrToStringAnsi(currentVersionPtr);
这里m_DICSPtr是一个IntPtr类型,返回的currentVersionPtr实际上是一个内存中的32位地址值,这个可以通过编译器来查看。
对C#调用C++的dll的一点思考的更多相关文章
- java调用C#的dll
链接地址:http://www.cnblogs.com/yinhaiming/articles/1712463.html .net产生的比java晚,其类库的封装在某些方面也比java更优秀,更全面. ...
- 64位进程调用32位dll的解决方法 / 程序64位化带来的问题和思考
最近做在Windows XP X64,VS2005环境下做32位程序编译为64位程序的工作,遇到了一些64位编程中可能遇到的问题:如内联汇编(解决方法改为C/C++代码),long类型的变化,最关键的 ...
- .Net 环境下C# 通过托管C++调用本地C++ Dll文件
综述 : 本文章介绍.Net 环境下C# 通过托管C++调用本地C++ Dll文件, 示例环境为:VS2010, .Net4.0, Win7. 具体事例为测试C++, C#, 及C#调用本地C++D ...
- C#调用Delphi的dll之详解
C#调用Delphi接口方法,有两种解决办法: 一.将Delphi程序编译成一个COM组件,然后在C#里引用COM组件. 二.非托管调用Dephi的DLL文件. 这里我们主要讲解一下第二种方法,讲第二 ...
- c#Winform程序调用app.config文件配置数据库连接字符串 SQL Server文章目录 浅谈SQL Server中统计对于查询的影响 有关索引的DMV SQL Server中的执行引擎入门 【译】表变量和临时表的比较 对于表列数据类型选择的一点思考 SQL Server复制入门(一)----复制简介 操作系统中的进程与线程
c#Winform程序调用app.config文件配置数据库连接字符串 你新建winform项目的时候,会有一个app.config的配置文件,写在里面的<connectionStrings n ...
- 调用Interop.zkemkeeper.dll无法使用解决方案
调用Interop.zkemkeeper.dll无法使用 已经注册dll成功但是还是报错 检索 COM 类工厂中 CLSID 为 {00853A19-BD51-419B--2DABE57EB61F} ...
- c# 传递Null的string值导致的调用C++的dll报错 Attempted to read or write protected memory.
c# 调用C++的dll报错 Attempted to read or write protected memory: 原因是:c# 传递Null的string值导致的,将Null改为string ...
- Node调用C++(dll)
最近开始搞毕设了,打算用自己拿手的js来搞,但是仿佛入坑了,Node还不是很熟.总之,兵来将挡,水来土掩嘛,带着问题学习才是最高效的. 折腾1:Node 调用 C++ 刚开始,虽然我老师把dll文件给 ...
- golang调用c++的dll库文件
最近使用golang调用c++的dll库文件,简单了解了一下,特作此笔记:一.DLL 的编制与具体的编程语言及编译器无关 dll分com的dll和动态dll,Com组件dll:不管是何种语言写的都可以 ...
随机推荐
- iptables 端口转发规则
玩 vps 的经常要用到端口转发用以实现更快的速度.比如 ovh 机房的网络我这里访问非常慢,用远程桌面会吐血的类型.所以就会用其他的线路作为跳板,比如洛杉矶,香港之类的.再比如如果需要一个日本 ip ...
- mac下进行连接pptp协议
环境:mac系统 软件:shimo 协议:pptp协议 说明: mac 自带vpn已经不支持 pptp协议的vpn,可以下载shimo连接. mac下进行vpn连接pptp协议操作方法: 下载: 链接 ...
- ElasticSearch(站内搜索) 转发 https://www.cnblogs.com/xibei666/p/5929970.html
简介 Elasticsearch是一个实时的分布式搜索和分析引擎.它可以帮助你用前所未有的速度去处理大规模数据.它可以用于全文搜索,结构化搜索以及分析,当然你也可以将这三者进行组合.Elasticse ...
- /dev/mem可没那么简单
这几天研究了下/dev/mem.发现功能非常奇妙,通过mmap能够将物理地址映射到用户空间的虚拟地址上.在用户空间完毕对设备寄存器的操作,于是上网搜了一些/dev/mem的资料. 网上的说法也非常统一 ...
- 浅谈文件断点续传和WebUploader的基本结合
0.写在前面的话 上篇博客已经是在8月了,期间到底发生了什么,只有我自己知道,反正就是心情特别糟糕,生活状态工作状态学习状态都十分不好,还有心思进取吗,No!现在状态好起来了,生活又充满了希望 :D ...
- linux驱动编写之poll机制
一.概念 1.poll情景描述 以按键驱动为例进行说明,用阻塞的方式打开按键驱动文件/dev/buttons,应用程序使用read()函数来读取按键的键值.这样做的效果是:如果有按键按下了,调用该re ...
- Burp Suite学习之Intruder的4种攻击模式
burp suit的intruder攻击共有四种模式,如图所示,下面分别讲讲这四种模式的使用方法和场景. 一 .Sniper模式 Sniper模式使用一组payload集合,它一次只使用一个paylo ...
- JS /javascript 解除网页屏蔽右键(无法复制)的代码
javascript:(function() { function R(a){ona = "on"+a; if(window.addEventListener) window.ad ...
- Spring基于AspectJ的AOP的开发——注解
源码:https://gitee.com/kszsa/dchart 一, AspectJ的概述: AspectJ是一个面向切面的框架,它扩展了Java语言.AspectJ定义了AOP语法所以它有一个专 ...
- python第四章:函数--小白博客
Python函数 函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段. 函数能提高应用的模块性,和代码的重复利用率.你已经知道Python提供了许多内建函数,比如print().但你也可 ...