• P/Invoke各种总结(四、平台调用时的数据类型对应)


    C#和C++在互操作时,会涉及到数据类型对应的问题,如果数据类型用得不对,就会得不到想要的结果,严重的情况下,可能还会导致程序崩溃。这里做一下相关知识点的总结。

    说明:

    1. 表格第一列是Visual C++中的数据类型,第二列是标准C中的数据类型

    2. 表格第三列括号中显示的是别名(关键字)。

    3. 关于使用string还是StringBuilder,可以参考https://www.cnblogs.com/zhaotianff/p/12524947.html

    VC++Ansi CC#(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}");

     示例代码

  • 相关阅读:
    java.lang.NoClassDefFoundError的原因及解决
    关于安卓应用(APK文件)的二次打包
    Android应用Icon大小在不同分辨率下定义
    Android常见错误
    RelativeLayout相对布局 安卓布局技巧
    onTextChanged参数解释及实现EditText字数监听
    Django 基础
    Django 入门
    paramiko模块实现堡垒机
    Python操作MySQL
  • 原文地址:https://www.cnblogs.com/zhaotianff/p/12896297.html
Copyright © 2020-2023  润新知