C# 與C++的資料轉換
一、型別轉化
下面重點羅列下常用的型別轉化。
C++型別 |
C#型別 |
備註說明 |
Int |
Int16、Int32 |
沒有懸念,直接轉化 |
Uint |
UInt16、Uint32、int |
在程式中,不太清楚是,就可以直接對應為int |
Long |
Int32 |
Long相對int就定型了,對應的就是Int32 |
DWORD(unsigned long) |
Uint32 |
|
WORD(unsigned short) |
Uint16 |
這是對WORD的認知。 |
Byte(Unsigned char) |
Byte |
在 |
DECIMAL |
Decimal |
位數轉化 |
BOOL |
bool |
|
char |
char |
這種沒有加指標,比較容易,直接對應入座 |
Handle(void *) |
Intptr |
函式中為視窗控制代碼,c#就是預設的為Intptr |
HMODULE |
Intptr |
同上 |
HISTANCE |
Intptr |
同上 |
Int *、long * |
Ref int、ref long |
這種整形指標,在程式中是為了引用,所以在c#中對應的是ref,所以在關鍵詞之前加入ref. |
Int &.long & |
Ref int 、ref long |
解釋同上,當然在關鍵詞加入 out,也是可以的 |
Char *(LPSTR、Pchar)、const char *(LPCSTR)、 |
String |
也是為了獲取資料,在c#中string在應用中就是引用,所以直接改為string即可。Intptr也可以,不提倡。 |
Byte * |
Ref byte、byte[] |
程式byte * 是為了獲取型別為byte資料,在C#則用byte陣列獲取儲存資料。String也可以,但是不提倡,關鍵看看獲取的資料,做什麼用 |
GUID |
Guid |
|
Char[],byte[],in[] |
可對應找指標型別對一個的轉化 |
Char[],byte[],int,這種在c++中應用時,先初始化陣列,然後定義一個指標指向資料地址,然後訪問,所以在轉化是,在對應為char*》string , int[]>int * > intptr |
Char **,byte ** |
Intptr |
這種雙指標的呼叫,一般是訪問二位陣列,所以我們直接處理為Intptr,當然intptr和之前不一樣,需要處理,可以看見例子說明。 |
結構體 * 變數名、結構體 &變數名。 |
Ref 結構體 變數名、intptr |
在結構體引用,或者傳入值c++一般是用指標,在c#中,用ref代替。當然intptr也可以,但是不太方便。 |
通常在只要你選擇在win32執行環境中找到相匹配的CLR(公共語言執行庫,負責資源管理:記憶體分配和回收,並保證應用和底層作業系統之間有必要分離)型別,就可應正常工作。當然也有例外:BOOL在c++中發現其實為int型,所以轉化為int,而不是bool。
指標引數,在winAPI許多函式中將指標作為一個或者多個引數。指標的作用是儲存資料的地址,而不是資料。指標的加入,增加了資料的複雜性,同時增加資料靈活性,實現資料的傳入傳出,如果只是值型別應用只能是傳入資料。在應用中如果沒有指標,您可以直接通過值線上程堆疊中傳遞資料。有了指標,可以通過引用傳遞資料,將資料的記憶體地址推入到執行緒堆疊中,然後函式通過記憶體地址間接訪問資料。在c#用ref、out定義為類似指標作用關鍵詞。out是ref一個引數規範,實際上他們在執行中產生相同的機器碼,out作用為了讓呼叫者明白,資料只是傳出,ref表明資料傳入也是獲取。託管程式碼中ref、out引數另一個很好用的是,可以作為結構體、類、陣列提供一個地址供呼叫。只有在發現ref或out引數不符合需要情況下,才會封裝成更復雜的CLR型別。
在windows API中會有視窗控制代碼的獲取或者賦值,其方法的傳遞是不透明的,如handle、void *、histance等。
少數情況下,API 函式也將不透明指標定義為 PVOID 或 LPVOID 型別。在 Windows API 的定義中,這些型別意思就是說該指標沒有型別。當 一個不透明指標返回給您的應用程式(或者您的應用程式期望得到一個不透明指標)時,您應該將引數或返回值封送為 CLR 中的一種特殊型別 — System.IntPtr。當您使用 IntPtr 型別時,通常不使用 out 或 ref 引數,因為 IntPtr 意為直接持有指標。
在CLR型別系統中intptr是一種特殊的屬性,沒有固定的大小,在執行時再繫結,依據作業系統的正常指標而定。這意味著在 32 位的 Windows 中,IntPtr 變數的寬度是 32 位的,而在 64 位的 Windows 中,實時編譯器編譯的程式碼會將 IntPtr 值看作 64 位的值。當在託管程式碼和非託管程式碼之間封送不透明指標時,這種自動調節大小的特點十分有用。然而,當使用 Windows API 函式時,因為指標應是不透明的,所以除了儲存和傳遞給外部方法外,不能將它們另做它用。這種“只限儲存和傳遞”規則的兩個特例是當您需要向外部方法傳遞 null 指標值和需要比較 IntPtr 值與 null 值的情況。為了做到這一點,您不能將零強制轉換為 System.IntPtr,而應該在 IntPtr 型別上使用 Int32.Zero 靜態公共欄位,以便獲得用於比較或賦值的 null 值。
封送文字,主要是指在獲取資料時,資料可能是儲存在char陣列中,如果我們用string接收時,有可能為亂碼。所以在函式呼叫過程中,當char *,char[]是作為輸入資料時,可以改為string。當作為資料傳出時,則要好好考慮了,有時需要改為char []。在c+程式中,就是在c中字串實際上是隻是一個字元值陣列,通常為null,大多數windows API函式是按照對於ansi,將其作為字元值陣列(比較常用),對於unicode,將其作為寬字元值陣列。有時獲取的資料為亂碼時,可能就是需要轉為unicode,就解決了。大多數windows API函式都帶有LPTSTR或者LPCTSTR值。他們分別是可修改和不可修改的緩衝區,包含以null結束的字元陣列。“C”代表const,意味資料不會傳遞到函式外部。“T”代表該引數可以是Unicode和ANSI,在CLR執行中取決你選擇的字符集和底層作業系統的字符集.。所以函式宣告時,加上DllImportAttribute 為CharSet.Auto就可以了。如果字串引數只用作輸入,則使用 System.String 型別。在託管程式碼中,字串是不變的,適合用於不會被本機 API 函式更改的緩衝區。如 果字串引數可以用作輸入和/或輸出,則使用 System.StringBuilder 型別。StringBuilder 型別是一個很有用的類庫型別,它可以幫助您有效地構建字串,也正好可以將緩衝區傳遞給本機函式,由本機函式為您填充字串資料。一旦函式呼叫返回,您只 需要呼叫 StringBuilder 物件的 ToString 就可以得到一個 String 物件。
CharSet的各變數對char以及char[]的影響如下:
ANSI:char以及char[]佔一個位元組
AUTO:char以及char[]佔兩個位元組
UNICODE:char以及char[]佔兩個位元組
總體原則可以總結為:
1、在c++常用的基本型別(數值型別、位元組型別)直接轉化到c#中的數值型別。(原則是位元組數,確定好)
2、在c++常用的指標型別(數值型別*、位元組型別*)則轉化為c#中的ref 數值型別、ref 位元組型別。但是在常用時,針對char * ,轉化為string。
3、在c++常用的構造型別(結構體、陣列、列舉型別、共用體)行對比較複雜。列舉和共用體直接複製就可以用,結構體的宣告隨後重點講,在函式呼叫時,如果為結構體 * 變數名,則為 ref 結構體 變數名。陣列在 函式呼叫,可以直接寫為陣列名,也可以寫為Intptr。
二、結構體
1、結構體的重定義。
在c++中會有很多結構體,結構體內有各種各樣的資料型別,所以就牽涉到資料型別的轉化,同時在通過結構體獲取到資料後,也牽涉到編碼轉化問題。
結構體型別和類型別在語法上有很多相似之處,他們都是一種資料結構,都可以包括資料成員和方法成員。
結構體和類區別:
1、結構體是值型別,它在棧中分配空間;類是引用型別,他在堆中分配空間,棧儲存的是引用。
2、結構體型別直接儲存成員資料,類中資料型別存在堆中,然後通過在棧中引用,訪問資料。因為結構體是值型別,直接儲存,因此當物件的主要成員為資料切不大時,使用結構體效率更高。
3、結構體直接包含自己的資料,每個結構體都儲存一份資料,在程式宣告兩個結構體物件,改變其中一個,另一個數據不變,但是類是引用,則另一個數據會改變。
4、結構體是值型別,不能初始化為null,複製時則資料全部複製。;類中的複製是引用的複製,資料較大時,結構體複製則效果不是很好。
結構和類的適用場合分析:
1、當堆疊的空間很有限,且有大量的邏輯物件時,建立類要比建立結構好一些;
2、對於點、矩形和顏色這樣的輕量物件,假如要宣告一個含有許多個顏色物件的陣列,則CLR需要為每個物件分配記憶體,在這種情況下,使用結構的成本較低;
3、在表現抽象和多級別的物件層次時,類是最好的選擇,因為結構不支援繼承。
4、大多數情況下,目標型別只是含有一些資料,或者以資料為主。
結構體的宣告、初始化、引用在c#還是很重要的,下面根據程式碼進行分析。
//C++的結構體
//錄影索引列表檔案
typedef struct tagINDEX_INFO
{
DWORD dwStartTime; //錄影開始時間
DWORD dwEndTime; //錄影停止時間
BYTE btFileType; //檔案型別
BYTE btFileStatus; //檔案狀態
BYTE Reserved[2]; //預留,LMC向NVR請求回放時Reserved[0]標識NVR傳送速度,Reserved[1]存放錄影倒放標誌
BYTE btMAC[6]; //裝置MAC地址
WORD wChan; //裝置通道
DWORD dwIP1; //裝置IP1
DWORD dwIP2; //裝置IP2,公網模式下,儲存此檔案所在的NVR的IP
DWORD dwIP3; //裝置IP3,V3061用來儲存錄影片段在錄影檔案的偏移量,勿動
DWORD dwIP4; //裝置IP4,V3061用來錄影檔名,勿動
DWORD dwFileOffset; //檔案偏移,用於檔案下載時斷點續傳和定位
DWORD dwReserved; //預留,3070有用到,不要動它
}INDEX_INFO, *LPINDEX_INFO;
C#結構體
//錄影索引列表檔案[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi,Pack = 1)]
public struct NET_INDEX_INFO
{
public UInt32 dwStartTime;//錄影開始時間
public UInt32 dwEndTime;//錄影停止時間
public byte btFileType;//檔案型別
public byte btFileStatus;//檔案狀態
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public byte [] Reserved;//預留,LMC向NVR請求回放時Reserved[0]標識NVR傳送速度,Reserved[1]存放錄影倒放標誌
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public byte [] btMAC;//裝置MAC地址
public UInt16 wChan;//裝置通道
public UInt32 dwIP1;//裝置IP1
public UInt32 dwIP2;//裝置IP2,公網模式下,儲存此檔案所在的NVR的IP
public UInt32 dwIP3;//裝置IP3,V3061用來儲存錄影片段在錄影檔案的偏移量,勿動
public UInt32 dwIP4;//裝置IP4,V3061用來錄影檔名,勿動
public UInt32 dwFileOffset;//檔案偏移,用於檔案下載時斷點續傳和定位
public UInt32 dwReserved;//預留,3070有用到,不要動它
}
上面成員前面必須新增public,因為預設是private。
2、StructLayout特性
公共語言執行庫利用StructLayoutAttribute控制類或結構的資料欄位在託管記憶體中的物理佈局,即類或結構需要按某種方式排列。如果要將類傳遞給需要指定佈局的非託管程式碼,則顯式控制類佈局是重要的。它的建構函式中用 LayoutKind值初始化 StructLayoutAttribute 類的新例項。 LayoutKind.Sequential 用於強制將成員按其出現的順序進行順序佈局。 System.Runtime.InteropServices.StructLayout 允許的值有StructLayout.Auto StructLayout.Sequential StructLayout.Explicit. 在應用中為了資料順利傳入,一般是用StructLayout.Sequential,意味著結構體體內資料傳入的格局按照宣告一樣。 StructLayout.Explicit需要用FieldOffset()設定每個成員的位置這樣就可以實現類似c的公用體的功能。 [StructLayout(LayoutKind.Explicit)]struct S1
{
[FieldOffset(0)]
int a;
[FieldOffset(0)]
int b;
}
這樣a和b在記憶體中地址相同
StructLayout特性支援三種附加欄位:CharSet、Pack、Size。
· CharSet定義在結構中的字串成員在結構被傳給DLL時的排列方式。可以是Unicode、Ansi或Auto。
預設為Auto,在WIN NT/2000/XP中表示字串按照Unicode字串進行排列,在WIN 95/98/Me中則表示按照ANSI字串進行排列。
· Pack定義了結構的封裝大小。可以是1、2、4、8、16、32、64、128或特殊值0。特殊值0表示當前操作平臺預設的壓縮大小。
3.MarshalAs的使用
MarshalAs屬性指示如何在託管程式碼和非託管程式碼之間封送資料。 [MarshalAs(UnmanagedType unmanagedType, 命名引數)]常用的UnmanagedType列舉值:(詳細內容查MSDN)
BStr 長度字首為雙位元組的 Unicode 字串;
LPStr 單位元組、空終止的 ANSI 字串。;
LPWStr 一個 2 位元組、空終止的 Unicode 字串;
ByValArray 用於在結構中出現的內聯定長字元陣列,應始終使用MarshalAsAttribute的SizeConst欄位來指示陣列的大小。常用的是陣列對應的為ByAvlArray,string對應的是ByValStr。