C#调用C/C++从零深入讲解

news2024/10/6 12:22:14

C#调用非托管DLL从零深入讲解

一、结构对齐

结构对齐是C#调用非托管DLL的必备知识。

在没有#pragma pack声明下结构体内存对齐的规则为:

  • 第一个成员的偏移量为0,
  • 每个成员的首地址为自身大小的整数倍
  • 子结构体的第一个成员偏移量应当是子结构体最大成员的整数倍
  • 结构体总大小必须是内部最大成员的整数倍

案例1

struct  TestFrame
{
   
	unsigned char id; //0-1
	int width; //4-8 必须是本身的整数倍
	long long height; //8-16
	unsigned char* data; //16-24 64位系统下,地址占8字节,32位占4字节
};

案例2

struct TestInfo
{
   
	char username[10]; //0-10
	double userdata;//16-24
};
struct  TestFrame
{
   
	unsigned char id; //0-1
	int width; //4-8 必须是本身的整数倍
	long long height; //8-16
	unsigned char* data; //16-24 64位系统下,地址占8字节,32位占4字节
	char mata;//24-25
	TestInfo 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-10
	double userdata;//10-18
};

如果我设置#pragma pack(10)则结果

struct TestInfo
{
   
	char username[10]; //0-10
	double 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 *

image-20231023160146261

四、创建并调用dll

本章节使用C++创建一个dll库,并使用C#和C++来调用

  1. 新建一个dll链接库项目,删除所有文件,新增Native.h和Native.cpp
  2. 在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();
  1. 在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常见参数有:

  1. dllName:动态链接库的名称,必填

    引用路径: (1)exe运行程序所在的目录

    ​ (2)System32目录

    ​ (3)环境变量目录

    ​ (4)自定义路径,如:DllImport(@“C:\OJ\Bin\Judge.dll”)

  2. CallingConvention:调用约定,常用的为CdeclStdCall,默认约定为StdCall

  3. CharSet:设置字符串编码格式

  4. EntryPoint:函数入口名称,默认使用方法本身的名字

  5. ExactSpelling:是否必须与入口点的拼写完全匹配,默认为true,如果为false,则根据CharSet来找函数的A版本还是W版本。

    这是一个CreateWindow的定义,后面有两个版本W和A。

    image-20231019154616465

    ExactSpelling,如果为true则只会使用CreateWindow,如果为False,则会选择W或者A版本,会自动根据当前系统采用的是那种UNICODE选择

  6. 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

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

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

相关文章

内衣洗衣机和手洗哪个干净?内衣洗衣机热销第一名

这两年内衣洗衣机可以称得上较火的小电器&#xff0c;小小的身躯却有大大的能力&#xff0c;一键可以同时启动洗、漂、脱三种全自动为一体化功能&#xff0c;在多功能和性能的提升上&#xff0c;还可以解放我们双手的同时将衣物给清洗干净&#xff0c;让越来越多小伙伴选择一款…

【深度学习 | 核心概念】那些深度学习路上必经的 常见问题解决方案及最佳实践,确定不来看看? (一)

&#x1f935;‍♂️ 个人主页: AI_magician &#x1f4e1;主页地址&#xff1a; 作者简介&#xff1a;CSDN内容合伙人&#xff0c;全栈领域优质创作者。 &#x1f468;‍&#x1f4bb;景愿&#xff1a;旨在于能和更多的热爱计算机的伙伴一起成长&#xff01;&#xff01;&…

UserWarning: CUDA initialization: CUDA unknown error

CUDA在suspend之后不可用问题 问题描述 一觉醒来&#xff0c;电脑cuda不可用 /home/你的电脑/pytorch/lib/python3.8/site-packages/torch/cuda/__init__.py:107: UserWarning: CUDA initialization: CUDA unknown error - this may be due to an incorrectly set up enviro…

Java11安装

Java11安装 打开官网 登录使用的账号密码可以用下面这个&#xff1a; 账户&#xff1a;3028064308qq.com 密码&#xff1a;OraclePassword123! 然后打开环境变量 系统变量下新建一个变量 在path路径中也新添两条 参考博文 Windows下安装JDK11&#xff08;详细版&#xf…

SAP MIRO发票过账报错 发出数量为0

SAP MIRO发票过账报错 发出数量为0 原因是预制发票的项目544行采购订单收货数量已退货&#xff0c;当前净收货数量为0。应该是bapi创建的预制发票

【uniapp】小程序开发7:自定义组件、自动注册组件

一、自定义轮播图组件、自动注册 以首页轮播图组件为例。 1、创建组件文件src/components/my-swipper.vue 代码如下&#xff1a; <template><view><view class"uni-margin-wrap"><swiper class"swiper" circular :indicator-dots…

如何解决数据倾斜

星光下的赶路人star的个人主页 臣书刷字墨淋漓&#xff0c;舒卷烟云势最奇 文章目录 1、数据倾斜的现象2、解决办法2.1 单表聚合&#xff08;group bysum()&#xff09;2.2 多表关联&#xff08;join&#xff09; 3、倾斜原因 1、数据倾斜的现象 部分Reduce一直运行&#xff0…

五个步骤轻松搞定软件开发流程

互联网在当今社会非常普遍&#xff0c;日常生活中很多东西都离不开互联网&#xff0c;应用软件是互联网必不可少的载体和终端。因此&#xff0c;软件是互联网中不可缺少的关键因素。软件开发已经成为许多企业和企业家非常重要的布局。在软件开发之前&#xff0c;我们应该了解软…

如何利用GPT4 和 ChatGPT 搞科研?

灵魂发问 GPT科研中没有那么神&#xff1f; GPT账号不能轻松使用&#xff1f; GPT怎样才融合到我的科研中&#xff1f; 别人用的非常酷&#xff0c;为什么我用的不行&#xff1f; 2023年我们进入了AI2.0时代。微软创始人比尔盖茨称ChatGPT的出现有着重大历史意义&#xff…

短视频矩阵系统源码/技术应用搭建

短视频矩阵系统开发围绕的开发核心维度&#xff1a; 1. 多账号原理开发维度 适用于多平台多账号管理&#xff0c;支持不同类型账号矩阵通过工具实现统一便捷式管理。&#xff08;企业号&#xff0c;员工号&#xff0c;个人号&#xff09; 2. 账号矩阵内容开发维护 利用账号矩…

用Python快速生成报表之一

一、前言 昨天两年多没有见过面的大Boss在澳洲给我老板和我开视频会议&#xff0c;他要求我们做到当他提出需要什么数据的时候&#xff0c;技术这边能够在5分钟之内快速给到他 &#xff0c;我心里有一万匹马奔腾而过&#xff0c;Java在处理这些事情上效率比较低&#xff0c;ph…

个人的微信公众号如何变更主体?

公众号迁移有什么作用&#xff1f;只能变更主体吗&#xff1f;长期以来&#xff0c;由于部分公众号在注册时&#xff0c;主体不准确的历史原因&#xff0c;或者公众号主体发生合并、分立或业务调整等现实状况&#xff0c;在公众号登记主体不能对应实际运营人的情况下&#xff0…

【2024秋招】小米中间件后端开发一面2023-9-13-base武汉

1 自我介绍 2 快手实习 2.1 讲讲你写的curd启动器&#xff0c;做了哪些工作呢 答&#xff1a; 2.2 网上也有一些开源的curd代码生成器&#xff0c;你为什么需要自研呢&#xff08;重要&#xff09; 答&#xff1a; &#xff08;1&#xff09;这个必须得自研&#xff0c;因…

Leetcode—323.无向图中连通分量的数目【中等】Plus

2023每日刷题&#xff08;七&#xff09; Leetcode—323.无向图中连通分量的数目 并查集思路实现代码 static int father[2010] {0};int Find(int x) {if(x ! father[x]) {father[x] Find(father[x]);}return father[x]; }void Union(int x, int y) {int a Find(x);int b …

使用R和curl库编写一段爬虫代码

以下是一个使用R和curl库的下载器程序&#xff0c;用于下载企鹅网站的内容。此程序使用了duoip.cn/get_proxy的代码。 # 引入必要的库 library(curl) library(jsonlite)# 获取爬虫ip proxy_url <- "https://www.duoip.cn/get_proxy" proxy_response <- curl_fe…

Fortify-设置中文语言

文章目录 运行scapostinstall.cmd设置Options 运行scapostinstall.cmd 设置Options 击Update Secutiry Content-zh_CN等待更新成中文即可 重启fortify

【Docker】Dockerfile常用指令

参考官方文档&#xff1a;https://docs.docker.com/engine/reference/builder/ Dockerfile常用指令 指令说明from基础镜像&#xff0c;当前镜像基于&#xff08;依赖&#xff09;哪个镜像maintainer镜像的维护者和邮箱run镜像构建时需要执行的命令workdir镜像的工作目录expos…

leetcode:292. Nim 游戏(数学推理)

一、题目 函数原型&#xff1a;bool canWinNim(int n) 二、思路 通过数学推理&#xff0c;列举找规律&#xff0c;发现当石头数为4的倍数时&#xff0c;我会输掉游戏&#xff1b;而其他情况&#xff0c;我都会赢得游戏。 三、代码 bool canWinNim(int n){if(n%40)return false;…

DC电源模块的模拟电源有什么优势?

BOSHIDA DC电源模块的模拟电源有什么优势&#xff1f; DC电源模块是电子系统中必不可少的部件之一。它们提供了可靠的直流电源&#xff0c;以驱动多种类型的电子设备。随着技术的进步&#xff0c;市场上出现了各种不同类型的DC电源模块&#xff0c;包括模拟电源和数字电源等。…

SM4国密4在jdk1.7版本和jdk1.8版本中的工具类使用

&#xff08;一&#xff09;首先&#xff0c;直接可用的工具类如下&#xff1a; 1、JDK1.8版本&#xff0c;使用hutool工具类实现SM4对称加密&#xff0c;pom依赖如下&#xff1a; <!-- Hutool 工具包 --><dependency><groupId>cn.hutool</groupId><…