C#调用C/C++从零深入讲解
C#调用非托管DLL从零深入讲解
一、结构对齐
结构对齐是C#调用非托管DLL的必备知识。
在没有#pragma pack
声明下结构体内存对齐的规则为:
- 第一个成员的偏移量为0,
- 每个成员的首地址为自身大小的整数倍
- 子结构体的第一个成员偏移量应当是子结构体最大成员的整数倍
- 结构体总大小必须是内部最大成员的整数倍
案例1
struct TestFrame
{unsigned char id; //0-1int width; //4-8 必须是本身的整数倍long long height; //8-16unsigned char* data; //16-24 64位系统下,地址占8字节,32位占4字节
};
案例2
struct TestInfo
{char username[10]; //0-10double userdata;//16-24
};
struct TestFrame
{unsigned char id; //0-1int width; //4-8 必须是本身的整数倍long long height; //8-16unsigned char* data; //16-24 64位系统下,地址占8字节,32位占4字节char mata;//24-25TestInfo info;//32-56 要从子结构体中最大成员的整数倍开始
};
查看具体的地址
//使用神器0,0可以转换为任意类型的NULL指针
#define FIELDOFFSET(TYPE,MEMBER) (int)(&(((TYPE*)0)->MEMBER))//使用
int infoLen = sizeof(TestInfo);
int offsetusername = FIELDOFFSET(TestInfo, username);
int offsetuserdata = FIELDOFFSET(TestInfo, userdata);
使用#pragma pack
这个宏声明按照几个字节对齐
#pragma pack(1)
struct TestInfo
{char username[10]; //0-10double userdata;//10-18
};
如果我设置#pragma pack(10)
则结果
struct TestInfo
{char username[10]; //0-10double userdata;//16-24
};
这与不使用#pragma pack(10)
结果相同,也就是说是按照宏声明的和实际数据类型中最大值中的较小的那个来决定
在C#中自定义结构对齐
//设置c#的结构对齐,按照1字节对齐
[StructLayout(LayoutKind.Sequential,Pack =1)]
public struct TestFrame
{public char id; int width; long height; char mata;
};
//手动指定偏移量
[StructLayout(LayoutKind.Explicit)]
public struct TestFrame
{[FieldOffset(0)]public char id;[FieldOffset(10)]int width;[FieldOffset(15)]long height;[FieldOffset(40)]char mata;
};
//也可以在字段定义使用什么类型
[MarshalAs(UnmanagedType.BStr)]
public string name;
二、调用约定
经常用到的调用约定有两种,C语言调用约定(_cdecl
)和标准调用约定(_stdcall
)。两种方式都是按照从右至左的方式入栈,但是C语言调用约定函数本身不清理栈,此工作由调用者负责,所以允许可变参数。而标准调用约定则是函数本身调用栈。
c语言调用约定和标准调用约定的最大区别在于,谁来清理参数所在的栈,C则调用者来清理,标准则是函数本身来清理。当所调用的程序中有可变参数的函数时,建议采用C语言约定
三、常用数据对应关系
常用的数据结构类型对比
C# | C/C++ |
---|---|
sbyte/char | char |
short | short |
int | int |
long | long long/int64_t |
float | float |
double | double |
intPtr/[] | void * |
四、创建并调用dll
本章节使用C++创建一个dll库,并使用C#和C++来调用
- 新建一个dll链接库项目,删除所有文件,新增Native.h和Native.cpp
- 在Native.h中
#pragma once
//判断是否为C++
#ifdef __cplusplus
#define EXTERNC extern "C" //如果是C++,则使用extern "C"
#else
#define EXTERNC //如果不是C++,则什么都不用加,后面是空的
#endif #ifdef DLL_IMPORT
//如果不使用dllimport则函数只能自己使用,别人不能使用
#define HEAD EXTERNC __declspec(dllimport) //HEAD宏定义--extern "C" __declspec(dllexport)
#else
#define HEAD EXTERNC __declspec(dllexport)
#endif#define CallingConvention __cdecl //定义调用约定
//使用宏定义替代了下面的语句
//extern "C" __declspec(dllexport) void __cdecl Test1();
- 在Native.cpp中实现函数,并编译为DLL路径
#include "Native.h"
#include<iostream>
#include<Windows.h>
HEAD void CallingConvention Test1()
{printf("调用成功\n");
}
//其实是使用上面的宏定义替代下面的语句
//extern "C" __declspec(dllexport) void __cdecl Test1()
//{
// printf("调用成功\n");
//}
在C#中调用
一定要设置好对应的NativeDll.dll路径
[DllImport("../../NativeDll.dll")]
public static extern void Test1(); //定义外部函数
static void Main(string[] args)
{Test1();
}
在C++中调用
#include <iostream>
#include <stdarg.h>
#define DLL_IMPORT //其实可以不用,为了遵循dll库的定义
#include "../NativeDll/Native.h" //引入Native.h
#pragma comment(lib,"../bin/NativeDll.lib") //导入库,除了dll,一定要有Lib,lib可以当做是DLL中方法的索引
int main(int argc,char* argv[])
{Test1();
}
五、DllImpoert常用参数
DllImport
常见参数有:
-
dllName:动态链接库的名称,必填
引用路径: (1)exe运行程序所在的目录
(2)System32目录
(3)环境变量目录
(4)自定义路径,如:DllImport(@“C:\OJ\Bin\Judge.dll”)
-
CallingConvention:调用约定,常用的为
Cdecl
和StdCall
,默认约定为StdCall
-
CharSet:设置字符串编码格式
-
EntryPoint:函数入口名称,默认使用方法本身的名字
-
ExactSpelling:是否必须与入口点的拼写完全匹配,默认为true,如果为false,则根据CharSet来找函数的A版本还是W版本。
这是一个CreateWindow的定义,后面有两个版本W和A。
ExactSpelling,如果为true则只会使用CreateWindow,如果为False,则会选择W或者A版本,会自动根据当前系统采用的是那种UNICODE选择
-
SetLastError:指示方法是否保留Win32的上一个错误,默认false。如果为true,则使用
Marshal.GetLastWin32Error()
来获取错误码。
六、基本数据传递和函数返回值
基本数据类型
//c++
HEAD void CallingConvention TestBasicData(char d1, short d2, int d3, long long d4, float d5, double d6)
{printf("d1:%d,d2:%d,d3:%d,d4:%lld,d5:%f,d6:%lf\n", d1, d2, d3, d4, d5, d6);
}
[DllImport("../../NativeDll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void TestBasicData(char d1,short d2,int d3,long d4,float d5,double d6
);
按引用传递基本数据类型
HEAD void CallingConvention TestBasicDataRef(char& d1, short& d2, int& d3, long long& d4, float& d5, double& d6)
{d1 = 1;d2 = 2;d3 = 3;d4 = 4;d5 = 5.6f;d6 = 6.7;printf("d1:%d,d2:%d,d3:%d,d4:%lld,d5:%f,d6:%lf\n", d1, d2
相关文章:

C#调用C/C++从零深入讲解
C#调用非托管DLL从零深入讲解 一、结构对齐 结构对齐是C#调用非托管DLL的必备知识。 在没有#pragma pack声明下结构体内存对齐的规则为: 第一个成员的偏移量为0,每个成员的首地址为自身大小的整数倍子结构体的第一个成员偏移量应当是子结构体最大成员的整数倍结构体总大小…...

MyBatis篇---第五篇
系列文章目录 文章目录 系列文章目录一、MyBatis 中见过什么设计模式?二、MyBatis 中比如 UserMapper.java 是接口,为什么没有实现类还能调用? 一、MyBatis 中见过什么设计模式? 二、MyBatis 中比如 UserMapper.java 是接口&#…...

十三水中各种牌型判断LUA版
近期回归程序行业,由于业务需求需要做十三水游戏,什么是十三水就不在多讲,下面是判断十三水牌型的方法(带大小王) GetSSSPaiType {}; local this GetSSSPaiType; local huaseTable {}; local numTable {}; functi…...

2023.10.19 关于设计模式 —— 单例模式
目录 引言 单例模式 饿汉模式 懒汉模式 懒汉模式线程安全问题 分析原因 引言 设计模式为编写代码的 约定 和 规范 阅读下面文章前建议点击下方链接明白 对象 和 类对象 对象和类对象 单例模式 单个实例(对象)在某些场景中有特定的类,…...
4个鲜为人知的Python迭代过滤函数
在Python中,迭代器可以帮助你编写更多Pythonic的代码,并在处理长序列时提高效率,内置的itertools模块提供了几个有用的函数来创建迭代器。 当你只需要遍历迭代器、检索序列中的元素并对其进行处理,而无需将它们存储在内存中时&am…...

使用logger.error(“自定义错误信息描述“,e)将错误信息输出到日志文件上
之前一直用e.getMessage()来获取错误信息 import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;RestController public class ClassF…...

音乐的数字未来:虚拟演唱会与TikTok的巅峰融合
在数字时代,音乐产业正在经历着革命性的变革。虚拟演唱会与TikTok的融合正引领着音乐的数字未来,为艺术家、粉丝和创作者带来了前所未有的互动性和娱乐体验。本文将深入探讨这一巅峰融合,以揭示音乐产业的新前景。 虚拟演唱会的崛起 虚拟演唱…...

基于图像识别的跌倒检测算法 计算机竞赛
前言 🔥 优质竞赛项目系列,今天要分享的是 基于图像识别的跌倒检测算法 该项目较为新颖,适合作为竞赛课题方向,学长非常推荐! 🧿 更多资料, 项目分享: https://gitee.com/dancheng-senior/…...

NSS [SWPUCTF 2021 新生赛]PseudoProtocols
NSS [SWPUCTF 2021 新生赛]PseudoProtocols 先看题目,题目要求我们先找到hint.php。 看这个get请求头,我们先用php://filter协议读一波 得到提示,让我们前往/test2222222222222.php 源码如下 <?php ini_set("max_execution_time&qu…...

字节码进阶之JVM Attach API详解
字节码进阶之JVM Attach API详解 文章目录 字节码进阶之JVM Attach API详解附加到虚拟机加载代理和获取信息分离虚拟机 使用Attach API的基本步骤1. **获取虚拟机实例**:2. **附加到虚拟机**:3. **加载代理或获取信息**4. **从虚拟机分离**:…...

Kubernetes 部署 kubeflow1.6.1
前言 安装前请注意捋清楚版本关系,如kubeflow版本对应的K8S版本及其相关工具版本等等 我们此处使用的是是kubeflow-1.6.1和K8s-v1.22.8 单机部署 部署K8S 初始化Linux 1.关闭selinux setenforce 0 && sed -i "s/SELINUXenforcing/SELINUXdisable…...

设计模式:建造者模式(C#、JAVA、JavaScript、C++、Python、Go、PHP)
上一篇《策略模式》 下一篇《适配器模式》 简介: 建造者模式,它是一种对象构建模式,它提供了一种构建对象的最佳方式。这种模式适用于当对象的构建过程需要涉及到多个部分ÿ…...
Maxon Cinema 4D 2024:打造独一无二的视觉效果 模拟模块大更新
在视觉效果和3D建模领域,Maxon的Cinema 4D一直以其卓越的性能和创新的功能引领着时代潮流。今天,我们很高兴地宣布推出最新版本——Maxon Cinema 4D 2024(C4D 2024),它将再次提升行业标准,为设计师提供更强…...

16.2 ARP 主机探测技术
ARP (Address Resolution Protocol,地址解析协议),是一种用于将 IP 地址转换为物理地址(MAC地址)的协议。它在 TCP/IP 协议栈中处于链路层,为了在局域网中能够正确传输数据包而设计,…...

三级等保-linux服务器三权分立设置
安全问题 安全控制点 风险分析 风险等级 标准要求 加固建议 服务器未严格按照系统管理员权限、审计管理员权限、安全管理员权限进行分配管理员账户,未实现管理员用户的最小权限划分。 访问控制 可能存在管理员越权操作的风险 中 d)应授予管理用户所需的最…...

抓取网页的含义和URL基本构成
抓取网页是指通过爬虫程序从互联网上获取网页的内容和数据。抓取网页是爬虫的核心功能之一,通过抓取网页,可以获取到网页中的文本、图片、链接等信息,用于后续的数据分析、挖掘和应用。 URL(Uniform Resource Locator)…...
计算机毕业设计 机器学习深度学习人工智能
视频参考: 计算机毕业设计项目分享_哔哩哔哩_bilibili 基于深度学习的农业病虫害识别基于SpringBootVue的博客系统基于SpringBootVue的仓库管理系统基于卷积网络的花卉图像识别 毕业设计选题: VX:whbwqq123 基于机器学习的大气数据的污染物pm2.5预测基…...

施密特正交化
相信大家在平时的期末考试中一定少不了对某某向量组执行标准正交化类型的题目。今天我们从这个题目入手,说明这个如何执行施密特正交化,以及为什么要进行正交化。 一、例子 例子:设 a 1 [ 1 2 − 1 ] a_1\begin{bmatrix}1\\2\\-1\end{bmat…...
低代码开发:加速应用开发的利器
目录 一、引言 二、低代码开发的定义和原理 三、低代码开发的关键特性和优势 四、低代码开发的应用场景 五、低代码开发平台的市场现状和发展趋势 六、成功案例分析 七、结论 一、引言 随着信息技术的快速发展,企业对于应用开发的需求也日益增长。传统的应用…...

数据安全发展趋势与密码保护技术研究
随着数据跃升为新型生产要素,数据安全的内涵也从数据本身安全、数据资源安全,发展到数据资产安全三个层面提出了不同的要求,本文就是详细探讨数据安全的这三个层面的安全内容进行分析。 通过对数据安全不同发展阶段的安全需求和保障对象进行研…...

idea大量爆红问题解决
问题描述 在学习和工作中,idea是程序员不可缺少的一个工具,但是突然在有些时候就会出现大量爆红的问题,发现无法跳转,无论是关机重启或者是替换root都无法解决 就是如上所展示的问题,但是程序依然可以启动。 问题解决…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...
python如何将word的doc另存为docx
将 DOCX 文件另存为 DOCX 格式(Python 实现) 在 Python 中,你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是,.doc 是旧的 Word 格式,而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...
什么是EULA和DPA
文章目录 EULA(End User License Agreement)DPA(Data Protection Agreement)一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA(End User License Agreement) 定义: EULA即…...

【Oracle】分区表
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...
在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案
这个问题我看其他博主也写了,要么要会员、要么写的乱七八糟。这里我整理一下,把问题说清楚并且给出代码,拿去用就行,照着葫芦画瓢。 问题 在继承QWebEngineView后,重写mousePressEvent或event函数无法捕获鼠标按下事…...

【VLNs篇】07:NavRL—在动态环境中学习安全飞行
项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战,克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...

springboot整合VUE之在线教育管理系统简介
可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...
Go 语言并发编程基础:无缓冲与有缓冲通道
在上一章节中,我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道,它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好࿰…...