C#调用C++的实现步骤
作者:qq_27663383
1、内存对齐的规则
(适用于C++、C#)
首先需要了解C++、C#的内存对齐的规则:
结构体的数据成员,第一个成员的偏移量为0,后面的每个数据成员存储的起始位置要从自己大小的整数倍开始。
子结构体中的第一个成员偏移量应当是子结构体中最大成员的整数倍。
结构体总大小必须是其内部最大成员的整数倍。
以下为C#示例代码:
internal class Program { struct Info { double dd;//32-40 bool bo;//40-48 } struct MyStruct { char c;//0-1 decimal d;//8-16 int a;//16-20 double b;//24-32 Info info;//32- } static unsafe void Main(string[] args) { Console.WriteLine(sizeof(MyStruct));//输出结果:48 } }
2、调用约定
_cdecl称之为c调用约定,参数从右至左的方式入栈,函数本身不清理栈,此工作由调用者负责,所以允许可变参数函数存在。我们自己编写的程序一般为_cdecl
_stdcall称之为标准调用约定,参数从右至左的方式入栈,函数本身清理栈,所以不允许可变参数函数存在。windowsAPI一般为_stdcall
3、C#传递基本数据类型到C++
C++与C#的基本类型对应关系如下表所示,数据通过值传递
C++基本类型 | C#基本类型 |
---|---|
int | int |
char | sbyte |
short | short |
float | float |
double | double |
long long | long |
bool | bool |
char* | string(传值,需CharSet = CharSet.Ansi) |
int*,double* | ref int,ref double |
int&,double& | ref int,ref double |
**不要将C#中托管堆上的数据,按照传引用的方式传递到C++中 **
C++ 代码
//native.h文件 extern "C" { __declspec(dllexport) void __cdecl TestBasicData(bool d1,char d2,short d3, int d4,long long d5,float d6,double d8); } //native.cpp文件 #include "native.h" void __cdecl TestBasicData(bool d1, char d2, short d3, int d4, long long d5, float d6, double d8) { return;//在此处添加断点,观察在C#中的数据传递到了C++代码中 }
C#代码
[DllImport("NativeDll", CallingConvention = CallingConvention.Cdecl)] public static extern int TestBasicData(bool d1, sbyte d2, short d3, int d4, long d5, float d6, double d8); static void Main(string[] args) { TestBasicData(true, 64, 128, 123456, 123456789, 12.45f, 3.142592); }
注意:使用VS调试过程中,需要在C#工程中勾选启动本地代码调试如下入所示,这样调试的时候才会进入C++代码。
建议在VS的解决方案属性中,将C#控制台项目依赖C++的dll项目,如下入所示。这样编译C#项目会自动编译C++项目。
4、C#传递基本数据类型的数组到C++
**不要将C#中托管堆上的数据,按照传引用的方式传递到C++中 **
以下以传递int*为例
C++代码如下,生成NativeDll.dll文件
//native.h文件 #pragma once extern "C" { __declspec(dllexport) int __cdecl TestAddDoubles(int* d, int length); } //native.cpp文件 #include "native.h" int __cdecl TestAddDoubles(int* d, int length) { int re = 0; for (size_t i = 0; i < length; i++) { re += d[i]; d[i] = 99;//改变d地址内的数据,在C#代码中也可以看到dpoint地址内的数据被改为99 } return re; }
C#代码如下
[DllImport("NativeDll", CallingConvention = CallingConvention.Cdecl)] public static extern int TestAddDoubles(IntPtr d, int length); static void Main(string[] args) { IntPtr dpoint = Marshal.AllocHGlobal(sizeof(int)*4);//在非托管内存中创建4个int大小内存的指针 unsafe { int* ptr = (int*)dpoint.ToPointer(); ptr[0] = 1; ptr[1] = 3; ptr[2] = 5; ptr[3] = 7; } var re = TestAddDoubles(dpoint, 4); Console.WriteLine(re);//输出结果为16 Marshal.FreeHGlobal(dpoint);//非托管内存需要在C#代码中释放 Console.ReadLine(); }
5、C#传递基本类型组成的结构体给C++
5.1 按值传递基本类型组成的结构体
**不要将C#中托管堆上的数据,按照传引用的方式传递到C++中 **
C++代码如下
//native.h文件 extern "C" { struct Shape//注意:一定要与C#中的结构体对齐方式一致 { int x; int y; int z; double area; double volume; }; __declspec(dllexport) int __cdecl TestStructor(Shape p); } //native.cpp文件 #include"native.h" int __cdecl TestStructor(Shape p) { return sizeof(p); }
C#代码如下
struct Shape//注意:一定要与C++中的结构体对齐方式一致 { public int x; public int y; public int z; public double area; public double volume; } [DllImport("NativeDll", CallingConvention = CallingConvention.Cdecl)] public static extern int TestStructor(Shape shape); static void Main(string[] args) { Shape shape = new Shape() { x = 10, y = 20, z = 30, area = 123.45, volume = 3456.98 }; var len = TestStructor(shape);//传值得方式将结构体传给C++ Console.WriteLine(len); }
5.2 按引用传递基本类型组成的结构体给C++结构体指针
**不要将C#中托管堆上的数据,按照传引用的方式传递到C++中 **
C++代码如下
//native.h文件 extern "C" { struct Shape//注意:一定要与C#中的结构体对齐方式一致 { int x; int y; int z; double area; double volume; }; __declspec(dllexport) int __cdecl TestStructorPointer(Shape* p); } //native.cpp文件 #include"native.h" int __cdecl TestStructorPointer(Shape* p) { int r = sizeof(*p); p->x = 100; p->y = 100; p->z = 100; p->area = 10.1; p->volume = 10.1; return r; }
C#代码如下
struct Shape//注意:一定要与C++中的结构体对齐方式一致 { public int x; public int y; public int z; public double area; public double volume; } [DllImport("NativeDll", CallingConvention = CallingConvention.Cdecl)] public static extern int TestStructorPointer(ref Shape shape); static void Main(string[] args) { Shape shape = new Shape() { x = 10, y = 20, z = 30, area = 123.45, volume = 3456.98 }; var len = TestStructorPointer(ref shape);//ref方式将结构体传给C++,通过断点调试,查看C#中的shape也更改了 Console.WriteLine(len); }
6、C#传递元素有数组的结构体
**不要将C#中托管堆上的数据,按照传引用的方式传递到C++中 **
结构体是按值进行传递的。
C++代码如下
//native.h文件 #pragma once extern "C" { struct Student { char name[50]; int age; double score; }; __declspec(dllexport) int __cdecl TestStudentStructor(Student s); } //native.cpp文件 #include "native.h" int __cdecl TestStudentStructor(Student s) { int len = sizeof(s); return len; }
C#中的结构体映射到C++内存中,要求结构体只能包含非托管类型
C#代码如下
unsafe struct Student//注意:一定要与C++中的结构体对齐方式一致 { public fixed byte name[50]; public int age; public double score; } [DllImport("NativeDll", CallingConvention = CallingConvention.Cdecl)] public static extern int TestStudentStructor(Student s); static unsafe void Main(string[] args) { Student s = new Student(); s.age = 12; s.score = 123.4; var name = Encoding.GetEncoding("GB2312").GetBytes("abcd\0");//C++的字符串编码为GB2312,结尾添加\0 Marshal.Copy(name, 0,new IntPtr(s.name),name.Length); var len = TestStudentStructor(s); Console.WriteLine(len); }
7、C#传递元素有数组的结构体指针
** 不要将C#中托管堆上的数据,按照传引用的方式传递到C++中 **
C++代码如下
//native.h文件 #pragma once extern "C" { struct Student { char name[50];//结构体中含有char数组 int age; double score; }; __declspec(dllexport) int __cdecl TestStudentStructorPointer(Student* s); } //native.cpp文件 #include "native.h" int __cdecl TestStudentStructorPointer(Student* s)//C++中改变s,C#中也会变 { int len = sizeof(*s); s->age = 1000; s->score = 1000.0; for (size_t i = 0; i < sizeof(s->name); i++) { s->name[i] = 123; } return len; }
C#中的结构体映射到C++内存中,要求结构体只能包含非托管类型
C#代码如下
unsafe struct Student//注意:一定要与C++中的结构体对齐方式一致 { public fixed byte name[50]; public int age; public double score; } [DllImport("NativeDll", CallingConvention = CallingConvention.Cdecl)] public static extern int TestStudentStructorPointer(ref Student s);//使用ref static void Main(string[] args) { Student s = new Student(); s.age = 12; s.score = 123.4; var name = Encoding.GetEncoding("GB2312").GetBytes("abcd\0");//结尾添加\0 unsafe { Marshal.Copy(name, 0, new IntPtr(s.name), name.Length); } var len = TestStudentStructorPointer(ref s);//ref方式将结构体传给C++,通过断点查看C#中的s也更改了 Console.WriteLine(len); }
到此这篇关于C#调用C++的实现步骤的文章就介绍到这了,更多相关C#调用C++内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!