P/Invoke各種總結(四、平台調用時的數據類型對應)


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}");

 示例代碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM