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…...
低代码开发:加速应用开发的利器
目录 一、引言 二、低代码开发的定义和原理 三、低代码开发的关键特性和优势 四、低代码开发的应用场景 五、低代码开发平台的市场现状和发展趋势 六、成功案例分析 七、结论 一、引言 随着信息技术的快速发展,企业对于应用开发的需求也日益增长。传统的应用…...

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

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...

Linux应用开发之网络套接字编程(实例篇)
服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...

Docker 离线安装指南
参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性,不同版本的Docker对内核版本有不同要求。例如,Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本,Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...
Spring Boot 实现流式响应(兼容 2.7.x)
在实际开发中,我们可能会遇到一些流式数据处理的场景,比如接收来自上游接口的 Server-Sent Events(SSE) 或 流式 JSON 内容,并将其原样中转给前端页面或客户端。这种情况下,传统的 RestTemplate 缓存机制会…...
c++ 面试题(1)-----深度优先搜索(DFS)实现
操作系统:ubuntu22.04 IDE:Visual Studio Code 编程语言:C11 题目描述 地上有一个 m 行 n 列的方格,从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子,但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...
python如何将word的doc另存为docx
将 DOCX 文件另存为 DOCX 格式(Python 实现) 在 Python 中,你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是,.doc 是旧的 Word 格式,而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...
Robots.txt 文件
什么是robots.txt? robots.txt 是一个位于网站根目录下的文本文件(如:https://example.com/robots.txt),它用于指导网络爬虫(如搜索引擎的蜘蛛程序)如何抓取该网站的内容。这个文件遵循 Robots…...

k8s业务程序联调工具-KtConnect
概述 原理 工具作用是建立了一个从本地到集群的单向VPN,根据VPN原理,打通两个内网必然需要借助一个公共中继节点,ktconnect工具巧妙的利用k8s原生的portforward能力,简化了建立连接的过程,apiserver间接起到了中继节…...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...