C#調(diào)用C++的實(shí)現(xiàn)步驟
1、內(nèi)存對齊的規(guī)則
(適用于C++、C#)
首先需要了解C++、C#的內(nèi)存對齊的規(guī)則:
結(jié)構(gòu)體的數(shù)據(jù)成員,第一個成員的偏移量為0,后面的每個數(shù)據(jù)成員存儲的起始位置要從自己大小的整數(shù)倍開始。
子結(jié)構(gòu)體中的第一個成員偏移量應(yīng)當(dāng)是子結(jié)構(gòu)體中最大成員的整數(shù)倍。
結(jié)構(gòu)體總大小必須是其內(nèi)部最大成員的整數(shù)倍。
以下為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));//輸出結(jié)果:48 } }
2、調(diào)用約定
_cdecl稱之為c調(diào)用約定,參數(shù)從右至左的方式入棧,函數(shù)本身不清理?xiàng)#斯ぷ饔烧{(diào)用者負(fù)責(zé),所以允許可變參數(shù)函數(shù)存在。我們自己編寫的程序一般為_cdecl
_stdcall稱之為標(biāo)準(zhǔn)調(diào)用約定,參數(shù)從右至左的方式入棧,函數(shù)本身清理?xiàng)?,所以不允許可變參數(shù)函數(shù)存在。windowsAPI一般為_stdcall
3、C#傳遞基本數(shù)據(jù)類型到C++
C++與C#的基本類型對應(yīng)關(guān)系如下表所示,數(shù)據(jù)通過值傳遞
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#中托管堆上的數(shù)據(jù),按照傳引用的方式傳遞到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;//在此處添加斷點(diǎn),觀察在C#中的數(shù)據(jù)傳遞到了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調(diào)試過程中,需要在C#工程中勾選啟動本地代碼調(diào)試如下入所示,這樣調(diào)試的時候才會進(jìn)入C++代碼。
建議在VS的解決方案屬性中,將C#控制臺項(xiàng)目依賴C++的dll項(xiàng)目,如下入所示。這樣編譯C#項(xiàng)目會自動編譯C++項(xiàng)目。
4、C#傳遞基本數(shù)據(jù)類型的數(shù)組到C++
**不要將C#中托管堆上的數(shù)據(jù),按照傳引用的方式傳遞到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地址內(nèi)的數(shù)據(jù),在C#代碼中也可以看到dpoint地址內(nèi)的數(shù)據(jù)被改為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);//在非托管內(nèi)存中創(chuàng)建4個int大小內(nèi)存的指針 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);//輸出結(jié)果為16 Marshal.FreeHGlobal(dpoint);//非托管內(nèi)存需要在C#代碼中釋放 Console.ReadLine(); }
5、C#傳遞基本類型組成的結(jié)構(gòu)體給C++
5.1 按值傳遞基本類型組成的結(jié)構(gòu)體
**不要將C#中托管堆上的數(shù)據(jù),按照傳引用的方式傳遞到C++中 **
C++代碼如下
//native.h文件 extern "C" { struct Shape//注意:一定要與C#中的結(jié)構(gòu)體對齊方式一致 { 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++中的結(jié)構(gòu)體對齊方式一致 { 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);//傳值得方式將結(jié)構(gòu)體傳給C++ Console.WriteLine(len); }
5.2 按引用傳遞基本類型組成的結(jié)構(gòu)體給C++結(jié)構(gòu)體指針
**不要將C#中托管堆上的數(shù)據(jù),按照傳引用的方式傳遞到C++中 **
C++代碼如下
//native.h文件 extern "C" { struct Shape//注意:一定要與C#中的結(jié)構(gòu)體對齊方式一致 { 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++中的結(jié)構(gòu)體對齊方式一致 { 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方式將結(jié)構(gòu)體傳給C++,通過斷點(diǎn)調(diào)試,查看C#中的shape也更改了 Console.WriteLine(len); }
6、C#傳遞元素有數(shù)組的結(jié)構(gòu)體
**不要將C#中托管堆上的數(shù)據(jù),按照傳引用的方式傳遞到C++中 **
結(jié)構(gòu)體是按值進(jìn)行傳遞的。
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#中的結(jié)構(gòu)體映射到C++內(nèi)存中,要求結(jié)構(gòu)體只能包含非托管類型
C#代碼如下
unsafe struct Student//注意:一定要與C++中的結(jié)構(gòu)體對齊方式一致 { 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,結(jié)尾添加\0 Marshal.Copy(name, 0,new IntPtr(s.name),name.Length); var len = TestStudentStructor(s); Console.WriteLine(len); }
7、C#傳遞元素有數(shù)組的結(jié)構(gòu)體指針
** 不要將C#中托管堆上的數(shù)據(jù),按照傳引用的方式傳遞到C++中 **
C++代碼如下
//native.h文件 #pragma once extern "C" { struct Student { char name[50];//結(jié)構(gòu)體中含有char數(shù)組 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#中的結(jié)構(gòu)體映射到C++內(nèi)存中,要求結(jié)構(gòu)體只能包含非托管類型
C#代碼如下
unsafe struct Student//注意:一定要與C++中的結(jié)構(gòu)體對齊方式一致 { 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");//結(jié)尾添加\0 unsafe { Marshal.Copy(name, 0, new IntPtr(s.name), name.Length); } var len = TestStudentStructorPointer(ref s);//ref方式將結(jié)構(gòu)體傳給C++,通過斷點(diǎn)查看C#中的s也更改了 Console.WriteLine(len); }
到此這篇關(guān)于C#調(diào)用C++的實(shí)現(xiàn)步驟的文章就介紹到這了,更多相關(guān)C#調(diào)用C++內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
在C# WPF下自定義滾動條ScrollViewer樣式的操作
這篇文章主要介紹了在C# WPF下自定義滾動條ScrollViewer樣式的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-01-01操作XML文檔遇到的XMLNS問題及解決方法 (C# 和 PHP)
不管是用 PHP 還是 C#, 在操作 XML 的時候我們除了一個節(jié)點(diǎn)一個節(jié)點(diǎn)去取值之外, 還有一個非常方便的表達(dá)式, 就是 XPATH2011-05-05Unity實(shí)現(xiàn)模型點(diǎn)擊事件的方法
這篇文章主要介紹了Unity實(shí)現(xiàn)模型點(diǎn)擊事件的方法,本文通過多種方法給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-05-05C#獲取微信小程序的云數(shù)據(jù)庫中數(shù)據(jù)的示例代碼
本文主要介紹了C#獲取微信小程序的云數(shù)據(jù)庫中數(shù)據(jù)的示例代碼,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-08-08Unity實(shí)現(xiàn)單機(jī)游戲每日簽到系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了Unity實(shí)現(xiàn)單機(jī)游戲每日簽到系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-04-04C#使用CallContext緩存線程數(shù)據(jù)
這篇文章介紹了C#使用CallContext緩存線程數(shù)據(jù)的方法,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-05-05C#類型轉(zhuǎn)換之自定義隱式轉(zhuǎn)換和顯式轉(zhuǎn)換
本文主要為大家介紹了一個新的類型轉(zhuǎn)換方法:通過自定義隱式轉(zhuǎn)換,把不一樣的數(shù)據(jù)類型反序列化為一樣的數(shù)據(jù)類型,需要的同學(xué)可以參考一下2022-03-03