C#引用C++dll

参考:C#调用C++的dll方法_c#调用c++dll-CSDN博客

_stdcall与_cdecl区别 (QT 加载MFC的dll时,要注意的"_stdcall"或者CALLBACK的问题)_qt stdcall-CSDN博客

c#调用c++的DLL的实现方法_C#教程_脚本之家  

首先C++dll的制作

不论是使用C++builder还是vs来制作C++的dll,在导出c接口的时候都是要使用__stdcall的方式,即接口导出为:

extern "C" __declspec(dllexport) int __stdcall ExecuteSchTest(int nSchNo, char *pChannelInfo);

同样的对于如果是使用回调函数 的时候,回调函数的定义也要使用__stdcall

//定义带回调函数的接口
extern "C" __declspec(dllexport) int __stdcall ExecuteSchTest(int nSchNo, char *pChannelInfo);
extern "C" __declspec(dllexport) int __stdcall SetProductSpecification(int nSetLength,double* pSetData);
//定义回调函数
typedef bool ( __stdcall *FnPowerOn)(int, double*);
extern "C" __declspec(dllexport) void __stdcall RegisterInterface_PowerOn(FnPowerOn fn);
//定义回调函数
typedef bool ( __stdcall *CallBackReadStdData)(int , double*);
extern "C" __declspec(dllexport) void __stdcall RegisterInterface_ReadStdData(CallBackReadStdData fn);

以上是对于C++接口导出C接口的要求,要求使用__stdcall,否则在C#中调用接口会出现问题( 或者全部都是用_cdecl方式导出c接口方式,同时对应在C#调用时要强调使用_cdecl的方式加载接口,原因参考:_stdcall与_cdecl区别 (QT 加载MFC的dll时,要注意的"_stdcall"或者CALLBACK的问题)_qt stdcall-CSDN博客)

C#写法:

使用windows的LoadLibrary加载 dll文件:

[DllImport("Kernel32", SetLastError = true)]
private static extern int GetProcAddress(int handle, string funcName);[DllImport("Kernel32", SetLastError = true)]
private static extern int LoadLibrary(string dllPath);[DllImport("Kernel32", SetLastError = true)]
private static extern int FreeLibrary(int handle);//普通的接口注册  string---->对应C++dll中的char*
public delegate int ExecuteSchTest(int n, string p);/*带参数为double数组的接口,double[]---->对应C++dll中的double* ,
*需要注意如果使用数组,需要在使用[In, MarshalAs(UnmanagedType.LPArray, SizeConst = 50)]方式
*说明以下数组的类型和长度,对于如果该形参数组是接口的输入参数,用In来表示参数是输入类型
*In类型是可以省略的,即:[MarshalAs(UnmanagedType.LPArray, SizeConst = 50)],即默认就为In类型,但是如果参数为输出参数,用Out来表示为输出参数,Out是不可省略的
*/
public delegate int SetProductSpecification(int nSetLength, [In, MarshalAs(UnmanagedType.LPArray, SizeConst = 50)]double[] pSetData);//带参数为回调函数的接口,使用代理的方式  注意如果如果在参数中带有了
public delegate bool CallBackPowerOn(int nSetLength, [In, MarshalAs(UnmanagedType.LPArray, SizeConst = 50)]double[] fSetData);
public static CallBackPowerOn PowerOn = SetPowerOn;
public delegate void RegisterInterface_PowerOn(CallBackPowerOn callback);//切记此处的SizeConst必须固定与dll中数组长度一致,否则将会导致dll出现异常
/*
*下面回调函数中有一个形参为输出类型,即从C#中注册一个回调接口到C++dll中,C++主动发送一个消息到C#中,而该回调接口中存在一个从c++dll中开辟一个数组空间,C++dll把该数组地址传出给C#,然后由C#修改数据,在接口返回后,c++dll使用该数组数据,即该参数是Out类型
需要注意的一点是:如果是Out数组形参,SizeConst长度必须与C++dll中开辟的数组空间长度一致,如果不一致会导致C++dll栈数据出现问题。
*/
public delegate bool CallBackGetStdData(int nSetLength, [Out, MarshalAs(UnmanagedType.LPArray, SizeConst = 25)]double[] pSetData);
public static CallBackGetStdData GetStdData = ReadStdMeter;
public delegate void RegisterInterface_ReadStdData(CallBackGetStdData fn);//定义局部变量
private int dllHandle = 0;
public ExecuteSchTest ProExecuteSchTest;
public RegisterInterface_PowerOn ProRegisterInterface_PowerOn;
public SetProductSpecification ProSetProductSpecification;
public RegisterInterface_PowerOn ProRegisterInterface_PowerOn;
public RegisterInterface_ReadStdData ProRegisterInterface_ReadStdData;private static Delegate GetAddress(int dllModule, string functionname, Type t)
{int addr = GetProcAddress(dllModule, functionname);if (addr == 0)return null;elsereturn Marshal.GetDelegateForFunctionPointer(new IntPtr(addr), t);
}public void FreeLinbary()
{if (this.dllHandle >= 32){FreeLibrary(this.dllHandle);}
}public int LoadDll(string dllPath)
{this.dllHandle = LoadLibrary(dllPath);if (this.dllHandle >= 32){this.ProExecuteSchTest = (ExecuteSchTest)GetAddress(dllHandle, "ExecuteSchTest", typeof(ExecuteSchTest));this.ProSetProductSpecification = (SetProductSpecification)GetAddress(dllHandle, "SetProductSpecification", typeof(SetProductSpecification));this.ProRegisterInterface_PowerOn = (RegisterInterface_PowerOn)GetAddress(dllHandle, "RegisterInterface_PowerOn", typeof(RegisterInterface_PowerOn));this.ProRegisterInterface_ReadStdData = (RegisterInterface_ReadStdData)GetAddress(dllHandle, "RegisterInterface_ReadStdData", typeof(RegisterInterface_ReadStdData));double[] fData = { 0, 220, 5, 60, 2000, 4, 4, 50, 0, 0, 32, 1, 0, 0, 0 };int a = this.ProSetProductSpecification(15, fData);this.ProRegisterInterface_PowerOn(PowerOn);this.ProRegisterInterface_ReadStdData(GetStdData);string strComm = "111111111";
return 0;}else{return 1;}}int nRlt = this.ProExecuteSchTest(10000010, strComm);public Form1(){InitializeComponent();}private void Form1_Load(object sender, EventArgs e){string str1 = System.AppDomain.CurrentDomain.BaseDirectory;string path = str1 + "\\Test.dll";LoadDll(path);}  public static bool SetPowerOn(int nLength, double[] fData){if (nLength != 15)return false;for (int i = 0; i < nLength; i++){double d = fData[i];}return true;        }public static bool SetLoop(int nLength){return true;}public static bool ReadStdMeter(int nLength,  double[] pSetData){if (nLength != 25){return false;}int nPos = 0;for(int i = 0; i < 25; i++){pSetData[nPos] = i;}return true;}
}
}

注意:

带参数为double数组的接口,double[]---->对应C++dll中的double* ,
需要注意如果使用数组,需要在使用[In, MarshalAs(UnmanagedType.LPArray, SizeConst = 50)]方式
说明以下数组的类型和长度,对于如果该形参数组是接口的输入参数,用In来表示参数是输入类型
In类型是可以省略的,即:[MarshalAs(UnmanagedType.LPArray, SizeConst = 50)],即默认就为In类型,但是如果参数为输出参数,用Out来表示为输出参数,Out是不可省略的,而且对于Out类型的参数SizeConst必须是要dll中的长度一致,否则会出问题。

遇到的问题:

在使用过程中由于使用C++builder定义回调函数的时候使用了_cdecl, 而导出接口使用了__stdcall,导致在C#中使用调用接口的时候,C++dll中出现问题,排查问题过程中,调试发现接口附近的临时变量,在调用了之后,附近的变量值无缘无故的发生了原因,其根本原因就是_cdecl和__stdcall对于形参的入栈顺序不一致,c++dll中修改为__stdcall之后,解决问题。

参考:_stdcall与_cdecl区别 (QT 加载MFC的dll时,要注意的"_stdcall"或者CALLBACK的问题)_qt stdcall-CSDN博客

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

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

相关文章

动态规划7,等差数列划分,湍流子数组,唯一的子字符串,最长递增子序列

等差数列划分 思路&#xff1a; 经验题目要求 dp[i]表示&#xff1a;以 i 位置为结尾的所有子数组中有多少个等差数列 状态转移方程 对 dp[i] 位置&#xff0c;数列至少有三个元素&#xff0c;如果相邻三个为等差数列&#xff0c;dp[i] dp[i-1] 1; 如果相邻三个不为等差数…

c1月考测试

文章目录 月考技能2月1.某场100公里加速时间比赛中有摩托车&#xff0c;汽车&#xff0c;飞机等&#xff0c;现定义一个Runner类为参赛者,Runner类中增加成员变量加速时间acc_time,现要求使用Runner类实例化motor&#xff0c;car,airplane三个对象&#xff0c;输出比赛的第一名…

windows批处理脚本(cmd指令)

一、简介 最早期的电脑系统是DOS系统&#xff0c;DOS系统只有一个黑漆漆的窗口&#xff0c;需要自己输入命令&#xff0c;所以学习命令是很有必要的&#xff0c;那么CMD命令大全是什么?直到今天的Windows系统&#xff0c;还是离不开DOS命令的操作。如今懂得使用windows批处理脚…

【AI绘画教程】AI绘画图生图怎么用?

AI绘画技术已经越来越成熟&#xff0c;越来越多的人开始尝试利用AI进行创作。而AI绘画图生图作为一款优秀的AI绘画工具&#xff0c;正是帮助许多人创作的好帮手。 AI绘画图生图功能可以通过多种软件实现&#xff0c;具体的操作步骤可能因软件而异&#xff0c;但大体流程相似。以…

基于springboot+vue的会议室预约系统(源码+论文)

目录 前言 一、功能设计 二、功能实现 三、库表设计 四、论文 前言 随着互联网技术的发展&#xff0c;各行各业乃至人们的衣食住行都离不开网络。就拿最普普通通的衣食住行来说吧&#xff0c;穿衣服、买衣服我们现在基本都是在网络上进行购买&#xff0c;线下商场基本不去。…

身份证联网核查接口 Android身份证实名认证接口调用 二要素核验接口

身份证核验也就是核查身份证的真实性&#xff0c;虽然现有的技术越来越强&#xff0c;但是利益驱使下&#xff0c;不法分子也在想尽各种办法来利用非真实的身份证来实施违法行为。翔云身份证核验接口就可随时为用户提供核查身份证真伪的接口&#xff0c;让实名认证政策落地。 以…

实验二-写一个主程序来上机设计并验证线性表顺序表示的所有操作(至少包含算法2.3、2.4、2.5),并设计一个算法删除所有值大于min而且小于max的元素。

废话不多说&#xff0c;数据结构自己写代码见识了太多的bug&#xff0c;看来还是自己写代码的功夫不到家啊&#xff0c;进入正题。直接上代码。 #include <stdio.h> #include <stdlib.h> #define MAXSIZE 100 #define ERROR 0 #define OK 1 typedef int ElemType;…

维修住友注塑机 Sumitomo SE50D 工业液晶屏 SE50S工业电脑显示屏

Sumitomo (SHI) Demag 的 NC5 plus 控制器是一款易于使用的控制器&#xff0c;可帮助成型商实现卓越的注塑成型精度。 该控制器作为用户和注塑机之间的通信接口发挥着关键作用。只有通过控制才能访问机器的全部性能属性&#xff0c;从而以各种方式帮助最大限度地提高生产效率。…

1688商品详情数据采集(商品属性,规格,价格,详情图等)

京东商品详情数据采集是一个复杂但重要的过程&#xff0c;它涉及获取商品的详细信息&#xff0c;包括商品属性、规格、价格以及详情图等。以下是关于如何进行京东商品详情数据采集的基本步骤&#xff1a; 确定采集目标&#xff1a;首先&#xff0c;你需要明确需要采集的商品信…

tcp 连接数上限突破

连接数上限条件 文件句柄的限制 一个tcp连接就需要占用一个文件描述符&#xff0c;一旦文件描述符用完&#xff0c;新的连接就会返回给我们错误是&#xff1a;Can’topen so many files。linux系统出于安全角度的考虑&#xff0c;在多个维度对于可打开的文件描述符进行了限制…

unityAB包管理(远程下载)

1.AB包生成脚本 using UnityEngine; using UnityEditor; using System.IO; using System; using System.Collections.Generic;/// <summary> /// AB包创建 /// </summary> public class CreateAssetBundles : MonoBehaviour {public static string BuildAssetBund…

ARM 汇编指令:(五)CMP指令

目录 1.CMP比较指令 2.指令条件码 cond 1.CMP比较指令 CMP指令是计算机指令集中的一种比较指令&#xff0c;用于比较两个操作数的大小关系或相等性&#xff0c;并根据比较结果设置或更新条件码寄存器&#xff08;或程序状态字&#xff09;的标志位。 指令格式&#xff1a;C…

VUE内盘期货配资软件源码国际外盘二合一

开发一个Vue内盘期货配资软件源码&#xff0c;同时兼容国际外盘二合一的功能&#xff0c;是一个复杂且专业的任务&#xff0c;涉及前端Vue.js框架的使用、后端服务器处理、数据库管理、实时交易接口对接等多个方面。下面是一些关于开发此类软件的基本指导和考虑因素&#xff1a…

富格林:洞悉暗箱手法可信策略远离

富格林悉知&#xff0c;在现货黄金中&#xff0c;揭秘很多投资者都是奔着盈利来的&#xff0c;因此学习正规盈利出金技能是必不可少的。但一些投资者在进入市场后并不注意技能的提升&#xff0c;只是一味的看中盈利&#xff0c;这样的做单导致被诱导暗箱亏损的不少&#xff0c;…

什么是同城上门预约按摩系统,上门预约平台有哪些功能?

随着互联网技术的发展&#xff0c;人们的生活方式发生了很大的变化。在日常生活中&#xff0c;大家都习惯使用手机来订餐、购物、家政服务等&#xff0c;这也为我们的生活带来了很大的便利。而同城按摩小程序作为一种新兴的按摩预约方式&#xff0c;受到了越来越多人的欢迎。下…

轻松上手MacOS:HomeBrew安装全指南

轻松上手MacOS&#xff1a;HomeBrew安装全指南 你是否曾为在MacOS上安装软件而烦恼&#xff1f;是否想要一个既强大又简单的包管理器来简化你的安装过程&#xff1f;HomeBrew正是你所需要的工具。作为MacOS上的包管理器&#xff0c;HomeBrew让安装软件变得像数123一样容易。在…

机器视觉检测设备的组成要素

机器视觉检测设备是一种先进的自动化检测技术工具&#xff0c;它利用光学、图像处理和计算机硬件及软件技术模拟并扩展人类的视觉功能&#xff0c;以实现对产品或目标物体进行自动化的尺寸测量、缺陷检测、表面质量评估、颜色识别、形状匹配以及位置判断等功能。这种设备通常包…

GettingStartedwithLLVMCoreLibraries书籍

Getting Started with LLVM Core Libraries 参考1 LLVM是构架编译器(compiler)的框架系统&#xff0c;以C编写而成&#xff0c;用于优化以任意程序语言编写的程序的编译时间(compile-time)、链接时间(link-time)、运行时间(run-time)以及空闲时间(idle-time)&#xff0c;对开…

c/c++| 常规 |sizeof 、strlen

总结来说 &#xff0c;sizeof 查看内存给对象分配的空间大小&#xff0c;不仅仅是普通的内置变量&#xff0c;还包括用户自定义变量、结构体、类对象 然后strlen 是查看字符串的实际长度大小&#xff0c;注意它不会计算那个结束符’\0’

重生奇迹MU攻击防御技能石哪里掉

在《重生奇迹MU》中&#xff0c;攻击和防御技能石可以从以下途径获得&#xff1a; 1.怪物掉落&#xff1a;你可以通过击败怪物获得攻击和防御技能石&#xff0c;不同的怪物掉落不同的石头。你可以在各个地图的怪物掉落表中查看特定怪物掉落的技能石。 2.商店购买&#xff1a;…