C#和C++在互操作時,會涉及到數據類型對應的問題,如果數據類型用得不對,就會得不到想要的結果,嚴重的情況下,可能還會導致程序崩潰。這里做一下相關知識點的總結。
說明:
1. 表格第一列是Visual C++中的數據類型,第二列是標准C中的數據類型
2. 表格第三列括號中顯示的是別名(關鍵字)。
3. 關於使用string還是StringBuilder,可以參考https://www.cnblogs.com/zhaotianff/p/12524947.html
VC++ | Ansi C | C#(CTS) | 說明 |
---|---|---|---|
VOID |
void |
System.Void(void) | void類型,代表函數無返回值。在C++里函數無參數也可以傳入void,在C#里不這么做。 |
HANDLE |
void * |
System.IntPtr or System.UIntPtr | 長度:32位(32位系統), 64位(64位系統) |
BYTE |
unsigned char |
System.Byte(byte) | 長度:8位 |
SHORT |
short |
System.Int16(short) | 長度:16位 |
WORD |
unsigned short |
System.UInt16(ushort) | 長度:16 位 |
INT |
int |
System.Int32(int) | 長度:32 位 |
UINT |
unsigned int |
System.UInt32(uint) | 長度:32 位 |
LONG |
long |
System.Int32(int) | 長度:32 位 |
BOOL |
long |
System.Boolean(bool) or System.Int32(int) | 長度:32 位 |
DWORD |
unsigned long |
System.UInt32(uint) | 長度:32 位 |
ULONG |
unsigned long |
System.UInt32(uint) | 長度:32 位 |
CHAR |
char |
System.Char(char) | 字符集:ANSI(多字節) |
WCHAR |
wchar_t |
System.Char(char) | 字符集:Unicode(寬字符) |
LPSTR |
char * |
System.String(string) or System.Text.StringBuilder | 字符集:ANSI(多字節) |
LPCSTR |
const char * |
System.String(string) or System.Text.StringBuilder | 字符集:ANSI(多字節) |
LPWSTR |
wchar_t * |
System.String(string) or System.Text.StringBuilder | 字符集:Unicode(寬字符) |
LPCWSTR |
const wchar_t * |
System.String(string) or System.Text.StringBuilder | 字符集:Unicode(寬字符) |
FLOAT |
float |
System.Single(float) | 長度:32 位 |
DOUBLE |
double |
System.Double(double) | 長度:64 位 |
4. 如果啟用了“允許不安全代碼”,可以使用指針類型來代替System.IntPtr or System.UIntPtr。
一般情況下,推薦使用System.IntPtr or System.UIntPtr類,但是如果需要進行指針偏移,就一定要用指針類型,而不是System.IntPtr or System.UIntPtr。
下面的示例代碼將會說明第4點。
一、使用IntPtr的情況
使用C++創建共享內存並寫入數據(示例代碼,僅供參考)
1 RECT rect{ 100,100,100,100 }; 2 auto len = sizeof(rect); 3 4 HANDLE m_handle = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_EXECUTE_READWRITE, 0, MemorySize, "HelloWorld"); 5 if (m_handle != NULL) 6 { 7 PVOID m_pView = MapViewOfFile(m_handle, FILE_MAP_ALL_ACCESS, 0, 0, MemorySize); 8 if (m_pView != NULL) 9 { 10 memcpy_s(m_pView, MemorySize, (void *)&rect, len); 11 12 //程序退出時的資源釋放操作 13 //....... 14 } 15 }
使用C#讀出
1 IntPtr withoutOffsetPtr = OpenFileMapping(FILE_MAP_READ, false, "HelloWorld"); 2 IntPtr mapView = MapViewOfFile(withoutOffsetPtr, FILE_MAP_READ, 0, 0, MemorySize); 3 Rect rect = new Rect(); 4 var size = Marshal.SizeOf(rect); 5 IntPtr ptr = Marshal.AllocHGlobal(size); //往IntPtr里拷貝數據需要提前分配空間,否則會報錯 6 byte[] buffer = new byte[size]; 7 Marshal.Copy(mapView, buffer, 0, size); //先拷貝到字節數組 8 Marshal.Copy(buffer, 0, ptr, size); //再拷貝到IntPtr 9 var obj = Marshal.PtrToStructure(ptr, typeof(Rect)); //將IntPtr轉換成結構體 10 rect = (Rect)obj; 11 12 Console.WriteLine($"Rect:left={rect.left},top={rect.top},right={rect.right},bottom={rect.bottom}");
二、使用指針的情況
使用C++創建共享內存並寫入數據
1 RECT rect{ 10,10,10,10 }; 2 POINT point{ 3,3 }; 3 auto len = sizeof(rect); 4 5 HANDLE m_handle = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_EXECUTE_READWRITE, 0, MemorySize, "HelloWorld2"); 6 if (m_handle != NULL) 7 { 8 PVOID m_pView = MapViewOfFile(m_handle, FILE_MAP_ALL_ACCESS, 0, 0, MemorySize); 9 if (m_pView != NULL) 10 { 11 //先寫入rect 12 memcpy_s(m_pView, MemorySize,(void *)&rect, len); 13 14 char* p = (char *)m_pView; 15 p = p + len; 16 m_pView = (void *)p; 17 18 //再寫入Point 19 memcpy_s(m_pView, MemorySize,(void *)&point, sizeof(point)); 20 21 //程序退出時的資源釋放操作 22 //....... 23 } 24 }
使用C#讀出(由於在寫入Point數據時,指針的位置已經不是當初映射出來的起點了,要偏移,就需要使用指針)
1 //這一部分操作跟上面函數是一樣的 2 IntPtr withoutOffsetPtr = OpenFileMapping(FILE_MAP_READ, false, "HelloWorld2"); 3 IntPtr mapView = MapViewOfFile(withoutOffsetPtr, FILE_MAP_READ, 0, 0, MemorySize); 4 Rect rect = new Rect(); 5 var size = Marshal.SizeOf(rect); 6 IntPtr ptr = Marshal.AllocHGlobal(size); 7 byte[] buffer = new byte[size]; 8 Marshal.Copy(mapView, buffer, 0, size); 9 Marshal.Copy(buffer, 0, ptr, size); 10 var obj = Marshal.PtrToStructure(ptr, typeof(Rect)); 11 rect = (Rect)obj; 12 13 Console.WriteLine($"Rect:left={rect.left},top={rect.top},right={rect.right},bottom={rect.bottom}"); 14 15 //使用指針 16 unsafe 17 { 18 byte* b = (byte*)mapView; 19 b += size; 20 mapView = (IntPtr)b; 21 } 22 23 //讀取Point結構的值 24 Point point = new Point(); 25 size = Marshal.SizeOf(point); 26 ptr = Marshal.AllocHGlobal(size); 27 buffer = new byte[size]; 28 Marshal.Copy(mapView, buffer, 0, size); 29 Marshal.Copy(buffer, 0, ptr, size); 30 obj = Marshal.PtrToStructure(ptr, typeof(Point)); 31 point = (Point)obj; 32 33 Console.WriteLine($"Point:x={point.x},y={point.y}");