C#调用带结构体指针的C Dll的方法

C#调用带结构体指针的C Dll的方法
原文:C#调用带结构体指针的C Dll的方法

在C#中调用C(C++)类的DLL的时候,有时候C的接口函数包含很多参数,而且有的时候这些参数有可能是个结构体,而且有可能是结构体指针,那么在C#到底该如何安全的调用这样的DLL接口函数呢?本文将详细介绍如何调用各种参数的方法。

一、调用接口仅含普通变量

eg:int fnAdd(int num1,int num2);

那么在C#调用这种函数最简单了,直接用函数原型即可,如下:

[DllImport("你的dll名称", EntryPoint = "fnAdd", CallingConvention = CallingConvention.Cdecl)]
public static extern int fnAdd(int num1, int num2);

这样在C#的方法内可以放心的使用这个dll函数了。

二、调用接口含普通变量的指针

大家都知道C#为了安全起见,隐形的避开了指针(其实在C#完全可以使用指针的,只是为了安全),采用了引用的方式来取代指针,引用的好处就是可以和指针一样操作参数原地址内的数据,并且这些数据在调用函数返回时还存活,但是引用不可以想指针那样++或者--到此PC指针乱跑,引出的一系列问题,下面举例来操作普通变量的指针,如下:

eg:int fnAdd(int *p_n1,int *p_n2);

上文已经说了C#采用引用来代替指针,那么好了调用接口可以这么写了:

[DllImport("你的dll名称", EntryPoint = "fnAdd", CallingConvention = CallingConvention.Cdecl)]
public static extern int fnAdd(ref int num1,ref int num2);

对,就这样的简单,这样C#便可以调用带指针的普通变量了。

三、来电稍微难度点的,调用接口含结构体

eg:在C的头文件内包含这样一个简单的结构体

struct mybuf
{int num1;int num2;
}

接口函数如下:

int fnAdd(struct mybuf mydata);

那么这样在C#该如何调用这样的接口函数呢?

首先在C#我们要声明一个结构体,在C#结构体并没有被抛弃,只不过在使用结构体时需要注意一些细节,比如要调用C的DLL那么最好在C#内定义的结构体前加上一些修饰符,如下:

[StructLayout(LayoutKind.Sequential)]
public struct MyBuf
{public int num1;public int num2;public MyBuf(int n1,int n2){num1 = n1;num2 = n2;}
}
[DllImport("你的dll名称", EntryPoint = "fnAdd", CallingConvention = CallingConvention.Cdecl)]
public static extern int fnAdd(MyBuf mydata);

大家可能会发现怎么这个结构体这么像个类啊,是的啊在C#中结构体确实是个特殊的类,也有构造函数,如上例子中的public MyBuf(int n1,int n2)这样的构造函数;

大家也可能看到定义结构体前我们使用StructLayout这样的结构体布局修饰符,这个其实是很有用的,我们使用了LayoutKind.Sequential这个属性,这在dll的参数是指针的时候特别有用,因为你的C中的结构体内存是顺序布局的,因此我们在C#内也要采用顺序布局,这样传递指针的时候在C dll内就不会出错了(也不一定)。

另外大家看到结构体的成员变量我们都用来public修饰符,当没有public只有int num1这样的语句的时候,C#默认成员变量是保护的,那么你在C#中其他方法内定义这个结构体就不能随便的访问修改其成员变量了(只能通过构造函数new的时候进行初始化),因此需要使用public来修饰一下成员变量。

四、继续来点难度,其实也没什么难度,就是dll接口参数包含结构体指针

eg:int fnAdd(struct mybuf *p_mydata),或者写成int fnAdd(void *p_mydata)

上面两个函数其实是一样的,因为C规定void类型的指针可以指向任何数据类型,只不过在c函数实体内强制为你的数据类型即可,比如:

struct mybuf*p = (structmybuf*)p_mydata;

那么在C#内该如何调用该函数接口呢?很简单举一反三ref嘛……

好了,代码如下:

[StructLayout(LayoutKind.Sequential)]
public struct MyBuf
{public int num1;public int num2;public MyBuf(int n1,int n2){num1 = n1;num2 = n2;}
}
[DllImport("你的dll名称", EntryPoint = "fnAdd", CallingConvention = CallingConvention.Cdecl)]
public static extern int fnAdd(ref MyBuf mydata);

对这样就OK了。

五、其实这样调用还有更复杂的,比如结构体内嵌套结构体,嵌套结构体指针,结构体内包含数组,这些都需要在C#

内声明结构体的时候需要特别处理,暂时就不增加这样的难度了。

<over>

<补>

为了继续增加点难度,下面继续补充几种情况,来涨点姿势……

六、dll接口参数内的结构体包含一个整形,一个字符数组

eg:如下的结构体

struct mybuf
{int a;int b;bool bl;int arr[200];char ch[100];
};
dll内接口原型为int fnAdd(struct mybuf mydata),那么这种情况在C#下该如何调用呢?

在C#中数据的布局和C(C++)中的数据布局有很大的不同,因此当用户需要在C#和C代码间进行数据传递时,必须手动

的告诉C#的老大.NET,这批数据该怎么传递给C的DLL来使用;因此这就涉及了C#的历史遗留问题(数据封送)。好

不多说先上代码,在C#该怎么声明这样一个结构体呢,如下:

[StructLayout(LayoutKind.Sequential)]
public struct MyBuf
{public int num1;public int num2;public bool flg;// 整形数组[MarshalAs(UnmanagedType.ByValArray, SizeConst = 200)]public int[] buf;// 字符数组[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]public char[] ch;public MyBuf(int n1, int n2, bool bl){num1 = n1;num2 = n2;flg = bl;buf = new int[200];ch = new char[100];}
};


是的,你可能奇怪的发现每个数值的声明前,增加了一个[MarshalAsxxxx]字段,这是干嘛用的呢?这就是前面红色字体标注的数据封送格式,简单介绍一下,MarshalAs的属性告诉了.NET如何将下面的数据进行封送到dll接口中,当UnmanagedType的值为ByValArray时,就是告诉下面的数据是一个数组,并且使用这个ByValArray值后面必须跟上SizeConst来告诉.NET这个数组的大小,如上;其实VS2010内写代码的时候当输入UnmanagedType之后按【.】之后VS会自动弹出框里面会列举很多数据封送格式,每个格式都有中文的tooltip来说明,自己看看就会明白的;前段时间看到字符数组和整形数据数据封送格式不一样,整形用ByValArray,而字符使用ByValTStr,但是实际我测下来当字符使用ByValTStr时调试的时候回报错,说非法的封送格式,把字符封送也改为ByValArray后就OK了,不晓得啥问题?还要继续研究。那么继续,在C#把结构体封装好了,就可以直接调用了,无论是结构体还是结构体指针按照前面的方法就可以使用了。

posted on 2018-07-30 08:47 NET未来之路 阅读(...) 评论(...) 编辑 收藏

转载于:https://www.cnblogs.com/lonelyxmas/p/9388797.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/570600.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【MFC】遍历文件夹及其子文件夹

void ScanFile(CString Dir) {CFileFind finder;CString AddL"\\*";CString DirSpecDirAdd; //补全要遍历的文件夹的目录BOOL bWorking finder.FindFile(DirSpec);while (bWorking){bWorking finder.FindNextFile();if(!finder.IsDots(…

HihoCoder - 1483 区间最值

给定n个数A1...An&#xff0c;小Ho想了解AL..AR中有多少对元素值相同。小Ho把这个数目定义为区间[L,R]的价值&#xff0c;用v[L,R]表示。 例如1 1 1 2 2这五个数所组成的区间的价值为4。 现在小Ho想知道在所有的的v[L,R](1 < L < R < n)中&#xff0c;第k小的值是多少…

用EnumProcesses()枚举进程

参照msdn的例子&#xff0c;用EnumProcesses&#xff08;&#xff09;枚举进程并输入进程名和句柄。以下代码在vs2008中测试通过&#xff1a; #include "stdafx.h" #include <windows.h> #include "psapi.h" #pragma comment (lib, "psapi.…

利用Tomcat运行一个JSP页面 详细步骤

1.启动Tomcat: 在Tomcat安装目录下的bin文件夹中&#xff0c;运行startup.bat批处理文件。截图如下&#xff1a; 此时会自动弹出窗口&#xff1a; 不要关闭这个窗口。 2.测试Tomcat是否启动成功。 打开浏览器&#xff0c;在地址栏中输入http://localhost:8080&#xff08;Tom…

springboot 找不到mapper问题

<!-- 如果不添加此节点mybatis的mapper.xml文件都会被漏掉。 --><resources> <!--新版本IDEA 开发工具maven编译不支持xml文件的编译&#xff0c;eclipse不影响--> <resource> <directory>src/main/java</directory> <in…

JSP的三个编译指令-page,include详解

JSP的编译指令是通知JSP引擎的消息。 常见的编译指令有三个&#xff1a; page&#xff1a;该指令是针对当前页面的指令。 include&#xff1a;用于指定如何包含另一个页面。 tablib&#xff1a;用于定义和访问自定义标签。 编译指令格式如下&#xff1a; <% 编译指令…

计算机基础:计算机网络-socket编程

来源&#xff1a;mooc大学华南理工大学计算机网络课程 chapter6 代码&#xff1a;https://github.com/NeilKeats/SocketDemo/commit/5f3a795250a95339106ed741b4355a9c6c3d39e9 1.设计原理&#xff1a;CS系统 2.如何使用&#xff1a; 3.TCP通信的socket例子&#xff1a;文件传输…

centos 静态拨号

本人系统centos6.5&#xff1b;虚拟机太丑&#xff0c;固ssh。 centos的与联网相关的配置文件在 $ /etc/sysconfig/network-scripts DHCP方式-联网 打开文件 $ vim /etc/sysconfig/network-scripts/ifcfg-eth0 常见参数说明 DEVICEeth0 # 网卡名称&#xff0c;一般不会改动 HWA…

JSP的9个内置对象-application

JSP页面中包含9个内置对象&#xff0c;这9个内置对象都是Servlet API的类或者接口的实例&#xff0c;只是JSP规范将它们完成了默认初始化&#xff0c;即它们已经是对象&#xff0c;可以直接使用。 application&#xff1a;javax.servlet.ServletContext的实例&#xff0c;该实…

jsp中获取list长度

<% taglib uri"http://java.sun.com/jsp/jstl/core" prefix"c"%> <% taglib prefix"fn" uri"http://java.sun.com/jsp/jstl/functions"%> list的长度是&#xff1a;${fn:length(list)}转载于:https://www.cnblogs.com/ca…

JSP的7个动作指令-forward指令

forward指令用于将页面响应控制转发给另外的页面。既可以转发给静态的HTML页面&#xff0c;也可以转发到动态的JSP页面&#xff0c;或者转发到容器的Servlet。 JSP的forward指令格式如下&#xff1a; 对于JSP1.0&#xff0c;使用如下语法&#xff1a; <jsp:forward page&q…

JSP的9个内置对象-request

request&#xff1a;javax.servlet.http:HttpServletRequest的实例&#xff0c;该对象封装了一次请求&#xff0c;客户端的请求参数都被封装在该对象里。获取客户端请求参数必须使用该对象。常用的方法有getParameter(String paramName), getParameterValues(String paramName)…

算法图解学习笔记02:递归和栈

计算机内存原理 要说递归和栈的问题&#xff0c;首先就要说下计算机内存的基本原理。简单理解计算机内存原理可以将一台电脑看作超市的存包柜&#xff0c;每个柜子都有柜号&#xff08;即计算机中的地址&#xff0c;如0x000000f&#xff09;。当需要将数据存储到计算机中时&…

JSP的9个内置对象-response

response代表服务器对客户端的响应。大部分时候&#xff0c;程序无须使用response来响应客户端请求&#xff0c;因为有个更简单的响应对象-out。它是页面输出流&#xff0c;是JstWriter的实例。JspWriter是Writer的子类&#xff0c;Writer是字符流&#xff0c;无法输出非字符内…

一个权重的物体拷贝权重给多个(oneWeightToMany)

你是否在做项目的时候经常会遇见一个物体带权重需要拷贝给其他物体&#xff0c;这时候其他物体多的数不胜数 你怎么办呢? 举例&#xff1a;假如一头狮子 身体你已经做好了&#xff0c;但是模型师把半个身体都做满了垂落的实体模型毛发&#xff0c;你是否感到头疼&#xff1f;&…

JSP的9个内置对象-session

session代表一次用户会话&#xff0c;其含义是&#xff1a;从客户端浏览器连接服务器开始&#xff0c;到客户端浏览器与服务器断开为止&#xff0c;这个过程就是一次会话。 session通常用于跟踪用户的会话信息&#xff0c;如判断用户是否登录系统&#xff0c;或者在购物车应用中…

JavaScript-Tool:jquery.qrcode.js

ylbtech-JavaScript-Tool&#xff1a;jquery.qrcode.js1.返回顶部 1、插件描述&#xff1a;jquery.qrcode.js 是一个能够在客户端生成矩阵二维码QRCode 的jquery插件 &#xff0c;使用它可以很方便的在页面上生成二维条码。如何使用它 将jquery.qrcode.min.js和jquery添加到您的…

lunix下的redis数据库操作——list列表

首先&#xff0c;需要先了解栈和队列的概念&#xff1a; 栈 先进后出&#xff1a;类比弹夹上的子弹&#xff0c;最后上进弹夹的子弹第一个使用&#xff0c;砌墙的板砖&#xff0c;后来居上 队列 先进先出&#xff1a;排队打饭&#xff0c;先到先得 创建列表&#xff1a; 左添…

通通玩blend美工(8)——动态绘制路径动画,画出个萌妹子~

通通玩blend美工&#xff08;8&#xff09;——动态绘制路径动画&#xff0c;画出个萌妹子~ 原文:通通玩blend美工&#xff08;8&#xff09;——动态绘制路径动画&#xff0c;画出个萌妹子~2年前我在玩Flex的时候就一直有一个疑问&#xff0c;就是如何来实现一个蚊香慢慢烧完的…

HTML 超链接

1.如何创建html超链接 <html> <body> <p> <a href"http://www.baidu.com" > website link</a> </p></body> </html>点击后跳转到baidu主页 2.超链接的title属性 <html> <body> <p> <a href&q…