文章目录
- 前言
- 一、基础知识
-
- 1. 结构体对齐
-
- 1. 内存对齐
- 2. 调用约定
- 3. C#与C/C++类型对应关系
- 2. 创建并调用动态链接库
- 3. Dllimport常用参数
- 二、实例调用
-
- 1. C#与Dll链接库的数据交换
-
- 1. 基本数据类型
- 2. 数组(引用传递)
- 3. 结构体
- 2. C++回调C#
前言
- 视频资源:B站:C#与C/C++动态链接库
- 本篇对C#与C/C++动态链接库做一个小结。
一、基础知识
1. 结构体对齐
1. 内存对齐
- 定义: 计算机系统对基本数据类型合法地址做出一些限制,要求某些类型地址必须是某个值K的倍数;
- 目的: 简化处理器喝存储器系统之间的接口硬件设计,方便存储器操作读取或写入;
- 规则:
- 结构体的数据成员,第一个成员的偏移量为0,后面每个数据成员存储的起始位置要从自己大小的整数倍开始;
- 子结构体中的第一个成员偏移量应当是子结构体中最大成员的整数倍;
- 结构体总大小必须是其内部最大成员的整数倍。
#include
#define FIELDOFFSET(TYPE,MEMBER)(int)(&(((TYPE*)0)->MEMBER))
#pragma pack(push)
#pragma pack(1)
struct Info
{
char username[10];
double userdata;
};
#pragma pack(pop)
struct Frame
{
unsigned char id;
int width;
long long height;
unsigned char* data;
Info info;
};
int main()
{
int len = sizeof(Frame);
int len2 = sizeof(Info);
int offset_width = FIELDOFFSET(Frame, width);
int offset_longlong = FIELDOFFSET(Frame, height);
return 0;
}
- 查看结构体偏移量方法:#define FIELDOFFSET(TYPE,MEMBER)(int)(&(((TYPE*)0)->MEMBER))
2. 调用约定
- _cdecl: C调用约定,参数从 右至左的方式入栈,函数本身不清理栈,由调用者负责,故,允许可变参数函数存在;
- _stdcall: 标准调用约定,参数按照从 右至左的方式入栈,函数本身清理栈。
![《C# 语言入门 - 学习笔记 - C#调C/C++(Dll)方法总结》_第1张图片](http://img.e-com-net.com/image/info8/16ae10af68a54a548fe1b8a57c21b3d7.jpg)
3. C#与C/C++类型对应关系
- 说明:ubyte<—>char;byte<—>unsigned char
![《C# 语言入门 - 学习笔记 - C#调C/C++(Dll)方法总结》_第2张图片](http://img.e-com-net.com/image/info8/1392a5f8e5ef4ad89ac39c7bbc67eb78.jpg)
2. 创建并调用动态链接库
-
新建文件夹,存放项目(src 源码文件夹)
![《C# 语言入门 - 学习笔记 - C#调C/C++(Dll)方法总结》_第3张图片](http://img.e-com-net.com/image/info8/5cf28214a0674408a4c6d1e6c4a9a63a.jpg)
![《C# 语言入门 - 学习笔记 - C#调C/C++(Dll)方法总结》_第4张图片](http://img.e-com-net.com/image/info8/706bc3a3163b4e08b3dee32b5c001508.jpg)
-
创建C# 项目(.Framework)
- 设置输出目录、目标平台(x64)
![《C# 语言入门 - 学习笔记 - C#调C/C++(Dll)方法总结》_第5张图片](http://img.e-com-net.com/image/info8/22a6267b084743e399fee9b3353dd5cd.jpg)
- 启用本地代码调试(方便调试跳转dll文件):
![《C# 语言入门 - 学习笔记 - C#调C/C++(Dll)方法总结》_第6张图片](http://img.e-com-net.com/image/info8/931c777dfcfb4ee78dd72e019c0c2f0f.jpg)
- 链接Dll示例:
![《C# 语言入门 - 学习笔记 - C#调C/C++(Dll)方法总结》_第7张图片](http://img.e-com-net.com/image/info8/fbd71676c021465ea9e8fd11f99824b0.jpg)
- 创建C++项目
- 设置输出/调试目录
![《C# 语言入门 - 学习笔记 - C#调C/C++(Dll)方法总结》_第8张图片](http://img.e-com-net.com/image/info8/f7540626a3fa4da6b83167009fe81d56.jpg)
- 链接Dll示例:
![《C# 语言入门 - 学习笔记 - C#调C/C++(Dll)方法总结》_第9张图片](http://img.e-com-net.com/image/info8/38ce833fd22e49f3b08c0d914ba42ba0.jpg)
- 创建Dll动态链接库
- 配置项目依赖项
![《C# 语言入门 - 学习笔记 - C#调C/C++(Dll)方法总结》_第14张图片](http://img.e-com-net.com/image/info8/9edc43af313b4669a5d9d841216e4992.jpg)
- 重新生成解决方案:
![《C# 语言入门 - 学习笔记 - C#调C/C++(Dll)方法总结》_第15张图片](http://img.e-com-net.com/image/info8/313bb5ff68f14ba191cea99ba4094749.jpg)
3. Dllimport常用参数
![《C# 语言入门 - 学习笔记 - C#调C/C++(Dll)方法总结》_第16张图片](http://img.e-com-net.com/image/info8/e13b7e26c5a94b4aa49ea356642bfdf9.jpg)
二、实例调用
1. C#与Dll链接库的数据交换
- C/C++基本数据字节:
![《C# 语言入门 - 学习笔记 - C#调C/C++(Dll)方法总结》_第17张图片](http://img.e-com-net.com/image/info8/a884549af00644c193a04c46d29740c1.jpg)
1. 基本数据类型
- Dll代码(.h / .cpp)
#pragma once
#ifdef __cplusplus
#define EXTERNC extern "C"
#else
#define EXTERNC
#endif
#ifdef DLL_IMPORT
#define HEAD EXTERNC __declspec(dllimport)
#else
#define HEAD EXTERNC __declspec(dllexport)
#endif
#define CallingConvention _cdecl
HEAD void CallingConvention Test_BasicData(char d1,short d2,int d3,long long d4,float d5,double d6);
#include "Native.h"
#include
HEAD void CallingConvention Test_BasicData(char d1, short d2, int d3, long long d4, float d5, double d6)
{
d1, d2, d3, d4, d5, d6;
}
- C#代码
using System;
using System.Runtime.InteropServices;
namespace CallNativeDllCSharp
{
class Program
{
[DllImport("NativeDll.dll",CallingConvention = CallingConvention.Cdecl)]
public static extern void Test_BasicData(sbyte d1, short d2, int d3, long d4, float d5, double d6);
static void Main(string[] args)
{
Test_BasicData(-56,128,446,11223344,12.5f,3.1415926d);
Console.Read();
}
}
}
- 引用传递(C# < ---- > C/C++ ;若C/C++有值,则数据是由C/C++ ----> C#)
- Dll代码(.h / .cpp)–两种方式(地址或指针)
#pragma once
#ifdef __cplusplus
#define EXTERNC extern "C"
#else
#define EXTERNC
#endif
#ifdef DLL_IMPORT
#define HEAD EXTERNC __declspec(dllimport)
#else
#define HEAD EXTERNC __declspec(dllexport)
#endif
#define CallingConvention _cdecl
HEAD void CallingConvention Test_BasicDataRef(char &d1, short &d2, int &d3, long long &d4, float &d5, double &d6);
HEAD void CallingConvention Test_BasicDataPointer(char* d1, short* d2, int* d3, long long* d4, float* d5, double* d6);
#include "Native.h"
#include
HEAD void CallingConvention Test_BasicDataRef(char& d1, short& d2, int& d3, long long& d4, float& d5, double& d6)
{
d1 = 1, d2 = 2, d3 = 3, d4 = 4, d5 = 5.5f, d6 = 6.6;
}
HEAD void CallingConvention Test_BasicDataPointer(char* d1, short* d2, int* d3, long long* d4, float* d5, double* d6)
{
*d1 = 100, *d2 = 200, *d3 = 300, *d4 = 400, *d5 = 500.5f, *d6 = 600.6;
}
- C#代码
using System;
using System.Runtime.InteropServices;
namespace CallNativeDllCSharp
{
class Program
{
[DllImport("NativeDll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void Test_BasicDataRef(ref sbyte d1, ref short d2, ref int d3, ref long d4, ref float d5, ref double d6);
[DllImport("NativeDll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void Test_BasicDataPointer(ref sbyte d1, ref short d2, ref int d3, ref long d4, ref float d5, ref double d6);
static void Main(string[] args)
{
sbyte num1 = 10;
short num2 = 20;
int num3 = 30;
long num4 = 40;
float num5 = 50.5f;
double num6 = 60.6;
Test_BasicDataPointer(ref num1, ref num2, ref num3, ref num4, ref num5, ref num6);
Console.Read();
}
}
}
2. 数组(引用传递)
- Dll代码(.h / .cpp)
#pragma once
#ifdef __cplusplus
#define EXTERNC extern "C"
#else
#define EXTERNC
#endif
#ifdef DLL_IMPORT
#define HEAD EXTERNC __declspec(dllimport)
#else
#define HEAD EXTERNC __declspec(dllexport)
#endif
#define CallingConvention _cdecl
HEAD void CallingConvention Test_BasicDataArr(int* d1, char* d2);
HEAD void* CallingConvention Test_BasicDataArrRet();
#include "Native.h"
#include
HEAD void CallingConvention Test_BasicDataArr(int* d1, char* d2)
{
int num1[3];
char num2[6];
memcpy(num1, d1, sizeof(num1));
memcpy(num2, d2, sizeof(num2));
}
char str[6] = "test1";
HEAD void* CallingConvention Test_BasicDataArrRet()
{
return &str;
}
- C# 代码
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace CallNativeDllCSharp
{
class Program
{
[DllImport("NativeDll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void Test_BasicDataArr(int[] arry1,string arry2);
[DllImport("NativeDll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr Test_BasicDataArrRet();
static void Main(string[] args)
{
int[] d1 = new int[3]{10,20,30};
string d2 = "test2";
Test_BasicDataArr(d1, d2);
byte[] d3 = new byte[10];
IntPtr strPtr = Test_BasicDataArrRet();
Marshal.Copy(strPtr,d3,0,10);
string d32str = Encoding.Default.GetString(d3);
Console.Read();
}
}
}
3. 结构体
- Dll代码(.h / .cpp)
#pragma once
#ifdef __cplusplus
#define EXTERNC extern "C"
#else
#define EXTERNC
#endif
#ifdef DLL_IMPORT
#define HEAD EXTERNC __declspec(dllimport)
#else
#define HEAD EXTERNC __declspec(dllexport)
#endif
#define CallingConvention _cdecl
struct ChildStruct
{
int num;
char describ[10];
double pi;
};
struct StructA
{
short id;
char name[10];
int number;
ChildStruct cs;
};
HEAD void CallingConvention Test_Struct(StructA parameter);
HEAD void* CallingConvention Test_StructRet();
#include "Native.h"
#include
HEAD void CallingConvention Test_Struct(StructA parameter)
{
parameter.id = 123;
}
StructA structa;
HEAD void* CallingConvention Test_StructRet()
{
structa.id = 2;
char name[10] = "peter";
memcpy(structa.name, name, sizeof(structa.name));
structa.number = 2;
structa.cs.num = 2;
return &structa;
}
- C# 代码
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace CallNativeDllCSharp
{
class Program
{
[StructLayout(LayoutKind.Sequential)]
public struct ChildStruct
{
public int num;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]
public string describ;
public double pi;
};
[StructLayout(LayoutKind.Sequential)]
public struct StructA
{
public short id;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]
public string name;
public ChildStruct cs;
};
[DllImport("NativeDll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void Test_Struct(ref StructA parameter);
[DllImport("NativeDll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr Test_StructRet();
static void Main(string[] args)
{
StructA structA = new StructA();
structA.id = 1;
structA.name = "Tom";
structA.cs.num = 10;
structA.cs.describ = "cool";
structA.cs.pi = 1.2;
Test_Struct(ref structA);
byte[] d3 = new byte[10];
IntPtr structPtr = Test_StructRet();
StructA restructA = Marshal.PtrToStructure<StructA>(structPtr);
int reid = restructA.id;
Console.Read();
}
}
}
- Dll代码(.h / .cpp)
#pragma once
#ifdef __cplusplus
#define EXTERNC extern "C"
#else
#define EXTERNC
#endif
#ifdef DLL_IMPORT
#define HEAD EXTERNC __declspec(dllimport)
#else
#define HEAD EXTERNC __declspec(dllexport)
#endif
#define CallingConvention _cdecl
struct FrameInfo
{
char username[10];
double pts;
};
struct Frame
{
int width;
int height;
int format;
int linesize[4];
unsigned char* data[4];
FrameInfo* info;
};
HEAD void* CallingConvention Test_StructRet();
#include "Native.h"
#include
Frame frame;
FrameInfo info;
HEAD void* CallingConvention Test_StructRet()
{
frame.width = 1920;
frame.height = 1080;
frame.format = 0;
for (int i = 0; i < 4; i++)
{
frame.linesize[i] = i * 100;
frame.data[i] = new unsigned char[10];
for (int j = 0; j < 10; j++)
{
frame.data[i][j] = i;
}
}
info.pts = 12.5;
memset(info.username, 0, 10);
memcpy(info.username, "Test", sizeof(info.username));
frame.info = &info;
return &frame;
}
- C# 代码
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace CallNativeDllCSharp
{
class Program
{
[DllImport("NativeDll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr Test_StructRet();
static void Main(string[] args)
{
IntPtr ptr = Test_StructRet();
int width = Marshal.ReadInt32(ptr, 0);
int height = Marshal.ReadInt32(ptr, 4);
int format = Marshal.ReadInt32(ptr, 8);
int[] linesize = new int[4];
Marshal.Copy(new IntPtr(ptr.ToInt64()+12), linesize,0,4);
IntPtr[] datas = new IntPtr[4];
Marshal.Copy(new IntPtr(ptr.ToInt64() + 32),datas,0,4);
for (int i = 0; i < 4; i++)
{
byte[] temp = new byte[10];
Marshal.Copy(datas[i],temp,0,10);
}
IntPtr infoptr = Marshal.ReadIntPtr(ptr, 64);
byte[] username = new byte[20];
Marshal.Copy(infoptr,username,0,10);
string str = Encoding.ASCII.GetString(username);
double pts = BitConverter.ToDouble(BitConverter.GetBytes(Marshal.ReadInt64(infoptr, 16)),0);
Console.Read();
}
}
}
2. C++回调C#