• Unity | unity&C++内存共享及中文乱码解决方式


    目录

    一、内存共享用到的函数

    1.CreateFileMapping

    2.OpenFileMapping

    3.MapViewOfFile

    4.UnmapViewOfFile

    5.CloseHandle

    6.GetLastError

    二、Unity测内存共享(读数据)

    1.读数据用到的函数

    2.函数调用

    3.Unity读写数据实现

    三、中文乱码解决方式


            之前的博文已经实现了C++两个进程间的通信,今天实现的是C++进程写数据,Unity进程读数据。所以C++测的实现方式就略过了~

    一、内存共享用到的函数

    1.CreateFileMapping

            创建内存映射文件对象。

    1. IntPtr CreateFileMapping(
    2. int hFile,
    3. IntPtr lpAttributes,
    4. uint flProtect,
    5. uint dwMaxSizeHi,
    6. uint dwMaxSizeLow,
    7. string lpName);
    •  hFile:指定要映射的文件的句柄,如果这是一个已经打开的文件的句柄(CreateFile函数的返回值),那么将建立这个文件的内存映射文件,如果这个参数为INVALID_HANDLE_VALUE(-1),则建立共享内存。
    • lpAttribute:安全属性,一般设为NULL。
    • flProtect:指定映射文件的保护类型,它的取值可以是PAGE_READONLY(内存页面只读) 或PAGE_READWRITE(内存页面可读写)。
    • dwMaxSizeHi 和 dwMaxSizeLow参数组合指定了一个64位的内存映射文件的长度。一种简单的方法是将这两个参数全部设置为0,那么内存映射文件的大小将与磁盘文件大小一致。
    • lpName:指定文件映射对象的名字。如存在这个名字的一个映射,函数就会打开它。

    2.OpenFileMapping

            打开已创建好的内存映射文件对象。

    1. IntPtr OpenFileMapping(
    2. int dwDesiredAccess,
    3. [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle,
    4. string lpName);
    • dwDesiredAcess:指定保护类型,有FILE_MAP_ALL_ACCESS、FILE_MAP_WRITE 或FILE_MAP_READ。
    • bInheritHandle:如这个函数返回的句柄能由当前进程启动的新进程继承,则这个参数为TRUE。一般为NULL。
    • lpName:指定要打开的文件映射对象名称。

    3.MapViewOfFile

            将一个文件映射对象映射到当前应用程序的地址空间。如果成功,则返回映射视图文件的开始地址值。如果失败,则返回 NULL,可调用GetLastError() 查看错误。

    1. IntPtr MapViewOfFile(
    2. IntPtr hFileMapping,
    3. uint dwDesiredAccess,
    4. uint dwFileOffsetHigh,
    5. uint dwFileOffsetLow,
    6. uint dwNumberOfBytesToMap);
    • hFileMapping:CreateFileMapping()或OpenFileMapping()返回的文件映像对象句柄。
    • dwDesiredAccess:映射对象的文件数据的访问方式,而且同样要与CreateFileMapping()函数所设置的保护属性相匹配,可取以下值:
    1. FILE_MAP_ALL_ACCESS:等价于CreateFileMapping的 FILE_MAP_WRITE|FILE_MAP_READ。文件映射对象被创建时必须指定PAGE_READWRITE 选项.
    2. FILE_MAP_COPY:可以读取和写入文件。写入操作会导致系统为该页面创建一份副本.在调用CreateFileMapping时必须传入PAGE_WRITECOPY保护属性。
    3. FILE_MAP_EXECUTE:可以将文件中的数据作为代码来执行。在调用CreateFileMapping时可以传入PAGE_EXECUTE_READWRITE或PAGE_EXECUTE_READ保护属性。
    4. FILE_MAP_READ:可以读取文件。在调用CreateFileMapping时可以传入PAGE_READONLY或PAGE_READWRITE保护属性。
    5. FILE_MAP_WRITE:可以读取和写入文件。在调用CreateFileMapping时必须传入PAGE_READWRITE保护属性。
    • dwFileOffsetHigh:表示文件映射起始偏移的高32位。
    • dwFileOffsetLow:表示文件映射起始偏移的低32位(64KB对齐不是必须的)。
    • dwNumberOfBytesToMap:指定映射文件的字节数。

    4.UnmapViewOfFile

            停止当前程序的一个内存映射。

    bool UnmapViewOfFile(IntPtr pvBaseAddress);
    • pvBaseAddress:指定要解除映射的一个文件映射的基准地址。这个地址是早先用MapViewOfFile函数获得的。 

    5.CloseHandle

            关闭内存映射文件的句柄。

    bool CloseHandle(IntPtr handle);
    • handle:CreateFileMapping()或OpenFileMapping()返回的文件映像对象句柄。

    6.GetLastError

    int GetLastError();

    二、Unity测内存共享(读数据)

            首先需要注意的是,经过测试发现:内存共享句柄由哪个进程创建,哪个进程才有写数据的权力。如果双方都需要读写数据,那么要各自创建一个内存共享句柄。所以Unity测读数据的话,内存共享句柄需要在C++进程创建。为了方便阅读,我先列出unity测(读数据)的函数声明。

    1.读数据用到的函数

    1. public class ShareMemoryHelper{
    2. public bool GetShareMemoryMap(string name, uint length);
    3. public int Read(ref byte[] bytData, int lngAddr, int lngSize);
    4. public void Close();
    5. }

    2.函数调用

    1. ShareMemoryHelper shareMemoryHelper = new ShareMemoryHelper();
    2. if (shareMemoryHelper != null)
    3. {
    4. bool isGet= shareMemoryHelper.GetShareMemoryMap("TALVirtualHuman", 1024);
    5. byte[] bytData = new byte[512];
    6. int ret = shareMemoryHelper.Read(ref bytData, 0, 512);
    7. //...数据处理
    8. //...
    9. shareMemoryHelper.Close();
    10. }

            注意:数据处理部分可能会有中文乱码问题,后续会说明解决方式。 

    3.Unity读写数据实现

    1. public class ShareMemoryHelper
    2. {
    3. [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
    4. private static extern IntPtr OpenFileMapping(int dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, string lpName);
    5. [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
    6. private static extern IntPtr MapViewOfFile(IntPtr hFileMapping, uint dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, uint dwNumberOfBytesToMap);
    7. [DllImport("kernel32", EntryPoint = "GetLastError")]
    8. public static extern int GetLastError();
    9. [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
    10. public static extern IntPtr CreateFileMapping(int hFile, IntPtr lpAttributes, uint flProtect, uint dwMaxSizeHi, uint dwMaxSizeLow, string lpName);
    11. [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
    12. public static extern bool UnmapViewOfFile(IntPtr pvBaseAddress);
    13. [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
    14. public static extern bool CloseHandle(IntPtr handle);
    15. public IntPtr fileMapping = IntPtr.Zero;
    16. public IntPtr mapView = IntPtr.Zero;
    17. const int FILE_MAP_COPY = 0x0001;
    18. const int FILE_MAP_WRITE = 0x0002;
    19. const int FILE_MAP_READ = 0x0004;
    20. const int FILE_MAP_ALL_ACCESS = 0x0002 | 0x0004;
    21. const int PAGE_READWRITE = 0x04;
    22. const int INVALID_HANDLE_VALUE = -1;
    23. const int ERROR_ALREADY_EXISTS = 183;
    24. private bool bValid;
    25. bool m_bAlreadyExist = false;
    26. bool m_bInit = false;
    27. long m_MemSize = 0;
    28. public ShareMemoryHelper()
    29. {
    30. }
    31. //~ShareMemoryHelper()
    32. //{
    33. // Close();
    34. //}
    35. public bool GetShareMemoryMap(string name, uint length)
    36. {
    37. m_MemSize = length;
    38. fileMapping = OpenFileMapping(PAGE_READWRITE, false, name);
    39. if (fileMapping == IntPtr.Zero)
    40. {
    41. return false;
    42. }
    43. mapView = MapViewOfFile(fileMapping, (uint)FILE_MAP_READ, 0, 0, length);
    44. if (mapView == IntPtr.Zero)
    45. {
    46. int a = GetLastError();
    47. Debug.Log(a);
    48. return false;
    49. }
    50. m_bInit = true;
    51. return true;
    52. }
    53. public int CreateShareMemoryMap(string strName, long lngSize)
    54. {
    55. if (lngSize <= 0 || lngSize > 0x00800000) lngSize = 0x00800000;
    56. m_MemSize = lngSize;
    57. if (strName.Length > 0)
    58. {
    59. fileMapping = CreateFileMapping(INVALID_HANDLE_VALUE, IntPtr.Zero, (uint)PAGE_READWRITE, 0, (uint)lngSize, strName);
    60. if (fileMapping == IntPtr.Zero)
    61. {
    62. return 2;
    63. }
    64. if (GetLastError() == ERROR_ALREADY_EXISTS)
    65. {
    66. m_bAlreadyExist = true;
    67. }
    68. else
    69. {
    70. m_bAlreadyExist = false;
    71. }
    72. mapView = MapViewOfFile(fileMapping, FILE_MAP_WRITE, 0, 0, (uint)lngSize);
    73. if (mapView == IntPtr.Zero)
    74. {
    75. m_bInit = false;
    76. CloseHandle(fileMapping);
    77. return 3;
    78. }
    79. else
    80. {
    81. m_bInit = true;
    82. if (m_bAlreadyExist == false)
    83. {
    84. }
    85. }
    86. }
    87. else
    88. {
    89. return 1;
    90. }
    91. return 0;
    92. }
    93. public int Write(byte[] bytData, int lngAddr, int lngSize)
    94. {
    95. if (lngAddr + lngSize > m_MemSize) return 2;
    96. if (m_bInit)
    97. {
    98. Marshal.Copy(bytData, lngAddr, mapView, lngSize);
    99. }
    100. else
    101. {
    102. return 1;
    103. }
    104. return 0;
    105. }
    106. public int Read(ref byte[] bytData, int lngAddr, int lngSize)
    107. {
    108. if (lngAddr + lngSize > m_MemSize) return 2;
    109. if (m_bInit)
    110. {
    111. Marshal.Copy(mapView, bytData, lngAddr, lngSize);
    112. }
    113. else
    114. {
    115. return 1;
    116. }
    117. return 0;
    118. }
    119. public void Close()
    120. {
    121. if (m_bInit)
    122. {
    123. UnmapViewOfFile(mapView);
    124. CloseHandle(fileMapping);
    125. }
    126. }
    127. }

    三、中文乱码解决方式

            中文乱码可尝试用以下方式解决:

    1. System.Text.Encoding gb2312 = System.Text.Encoding.GetEncoding("gb2312");
    2. string msg = gb2312.GetString(bytData);

            但是unity发布成exe后,程序会卡住,原因是exe依赖l18N开头的dll。Unity2022.2.1f的dll路径:xxx:\Unity 2022.1.2f1c1\Editor\Data\MonoBleedingEdge\lib\mono\unityaot-linux(I18N开的这些dll,unityaot-linux、unityaot-macos、unityaot-win32用的是同一个)。将这些dll拷贝到拷贝到发布好的xxx_Data\Managed文件夹下即可。

  • 相关阅读:
    Lombok首字母小写,第二个字母大写,jackson反序列化失败
    C++语言中预处理指令
    【多线程】线程安全的单例模式
    Git常用命令及IDEA集成Git
    【面试题精讲】什么是websocket?如何与前端通信?
    Linux 基础-文件属性与权限
    buildroot添加package包
    优先级反转那些事儿
    哪个蓝牙耳机好?盘点2022年600元左右的蓝牙耳机
    数据结构 —— BellmanFord算法
  • 原文地址:https://blog.csdn.net/weixin_39766005/article/details/127855013