C#和C++在互操作时,会涉及到数据类型对应的问题,如果数据类型用得不对,就会得不到想要的结果,严重的情况下,可能还会导致程序崩溃。这里做一下相关知识点的总结。
说明:
-
表格第一列是Visual C++中的数据类型,第二列是标准C中的数据类型
-
表格第三列括号中显示的是别名(关键字)。
-
关于使用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 位 |
- 如果启用了"允许不安全代码",可以使用指针类型来代替**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}");