C#和C++在互操作时,会涉及到数据类型对应的问题,如果数据类型用得不对,就会得不到想要的结果,严重的情况下,可能还会导致程序崩溃。这里做一下相关知识点的总结。
说明:
1. 表格第一列是Visual C++中的数据类型,第二列是标准C中的数据类型
2. 表格第三列括号中显示的是别名(关键字)。
3. 关于使用string还是StringBuilder,可以参考P/Invoke各种总结(三、与字符串交互char*,wchar_t*) - zhaotianff - 博客园
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}");
示例代码