一、類型轉化
下面重點羅列下常用的類型轉化。
| 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特性
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的使用
常用的UnmanagedType枚舉值:(詳細內容查MSDN)
BStr 長度前綴為雙字節的 Unicode 字符串;
LPStr 單字節、空終止的 ANSI 字符串。;
LPWStr 一個 2 字節、空終止的 Unicode 字符串;
ByValArray 用於在結構中出現的內聯定長字符數組,應始終使用MarshalAsAttribute的SizeConst字段來指示數組的大小。常用的是數組對應的為ByAvlArray,string對應的是ByValStr。
