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…...
低代码开发:加速应用开发的利器
目录 一、引言 二、低代码开发的定义和原理 三、低代码开发的关键特性和优势 四、低代码开发的应用场景 五、低代码开发平台的市场现状和发展趋势 六、成功案例分析 七、结论 一、引言 随着信息技术的快速发展,企业对于应用开发的需求也日益增长。传统的应用…...
数据安全发展趋势与密码保护技术研究
随着数据跃升为新型生产要素,数据安全的内涵也从数据本身安全、数据资源安全,发展到数据资产安全三个层面提出了不同的要求,本文就是详细探讨数据安全的这三个层面的安全内容进行分析。 通过对数据安全不同发展阶段的安全需求和保障对象进行研…...
wordpress后台更新后 前端没变化的解决方法
使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…...
springboot 百货中心供应链管理系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,百货中心供应链管理系统被用户普遍使用,为方…...
大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...
反射获取方法和属性
Java反射获取方法 在Java中,反射(Reflection)是一种强大的机制,允许程序在运行时访问和操作类的内部属性和方法。通过反射,可以动态地创建对象、调用方法、改变属性值,这在很多Java框架中如Spring和Hiberna…...
成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战
在现代战争中,电磁频谱已成为继陆、海、空、天之后的 “第五维战场”,雷达作为电磁频谱领域的关键装备,其干扰与抗干扰能力的较量,直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器,凭借数字射…...
select、poll、epoll 与 Reactor 模式
在高并发网络编程领域,高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表,以及基于它们实现的 Reactor 模式,为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。 一、I…...
vue3+vite项目中使用.env文件环境变量方法
vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量,这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...
【JavaSE】多线程基础学习笔记
多线程基础 -线程相关概念 程序(Program) 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存…...
提升移动端网页调试效率:WebDebugX 与常见工具组合实践
在日常移动端开发中,网页调试始终是一个高频但又极具挑战的环节。尤其在面对 iOS 与 Android 的混合技术栈、各种设备差异化行为时,开发者迫切需要一套高效、可靠且跨平台的调试方案。过去,我们或多或少使用过 Chrome DevTools、Remote Debug…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现指南针功能
指南针功能是许多位置服务应用的基础功能之一。下面我将详细介绍如何在HarmonyOS 5中使用DevEco Studio实现指南针功能。 1. 开发环境准备 确保已安装DevEco Studio 3.1或更高版本确保项目使用的是HarmonyOS 5.0 SDK在项目的module.json5中配置必要的权限 2. 权限配置 在mo…...
