目录
之前的博文已经实现了C++两个进程间的通信,今天实现的是C++进程写数据,Unity进程读数据。所以C++测的实现方式就略过了~
创建内存映射文件对象。
- IntPtr CreateFileMapping(
- int hFile,
- IntPtr lpAttributes,
- uint flProtect,
- uint dwMaxSizeHi,
- uint dwMaxSizeLow,
- string lpName);
打开已创建好的内存映射文件对象。
- IntPtr OpenFileMapping(
- int dwDesiredAccess,
- [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle,
- string lpName);
将一个文件映射对象映射到当前应用程序的地址空间。如果成功,则返回映射视图文件的开始地址值。如果失败,则返回 NULL,可调用GetLastError() 查看错误。
- IntPtr MapViewOfFile(
- IntPtr hFileMapping,
- uint dwDesiredAccess,
- uint dwFileOffsetHigh,
- uint dwFileOffsetLow,
- uint dwNumberOfBytesToMap);
- FILE_MAP_ALL_ACCESS:等价于CreateFileMapping的 FILE_MAP_WRITE|FILE_MAP_READ。文件映射对象被创建时必须指定PAGE_READWRITE 选项.
- FILE_MAP_COPY:可以读取和写入文件。写入操作会导致系统为该页面创建一份副本.在调用CreateFileMapping时必须传入PAGE_WRITECOPY保护属性。
- FILE_MAP_EXECUTE:可以将文件中的数据作为代码来执行。在调用CreateFileMapping时可以传入PAGE_EXECUTE_READWRITE或PAGE_EXECUTE_READ保护属性。
- FILE_MAP_READ:可以读取文件。在调用CreateFileMapping时可以传入PAGE_READONLY或PAGE_READWRITE保护属性。
- FILE_MAP_WRITE:可以读取和写入文件。在调用CreateFileMapping时必须传入PAGE_READWRITE保护属性。
停止当前程序的一个内存映射。
bool UnmapViewOfFile(IntPtr pvBaseAddress);
关闭内存映射文件的句柄。
bool CloseHandle(IntPtr handle);
int GetLastError();
首先需要注意的是,经过测试发现:内存共享句柄由哪个进程创建,哪个进程才有写数据的权力。如果双方都需要读写数据,那么要各自创建一个内存共享句柄。所以Unity测读数据的话,内存共享句柄需要在C++进程创建。为了方便阅读,我先列出unity测(读数据)的函数声明。
- public class ShareMemoryHelper{
- public bool GetShareMemoryMap(string name, uint length);
- public int Read(ref byte[] bytData, int lngAddr, int lngSize);
- public void Close();
- }
- ShareMemoryHelper shareMemoryHelper = new ShareMemoryHelper();
- if (shareMemoryHelper != null)
- {
- bool isGet= shareMemoryHelper.GetShareMemoryMap("TALVirtualHuman", 1024);
- byte[] bytData = new byte[512];
- int ret = shareMemoryHelper.Read(ref bytData, 0, 512);
- //...数据处理
- //...
- shareMemoryHelper.Close();
- }
注意:数据处理部分可能会有中文乱码问题,后续会说明解决方式。
- public class ShareMemoryHelper
- {
- [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
- private static extern IntPtr OpenFileMapping(int dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, string lpName);
- [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
- private static extern IntPtr MapViewOfFile(IntPtr hFileMapping, uint dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, uint dwNumberOfBytesToMap);
- [DllImport("kernel32", EntryPoint = "GetLastError")]
- public static extern int GetLastError();
- [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
- public static extern IntPtr CreateFileMapping(int hFile, IntPtr lpAttributes, uint flProtect, uint dwMaxSizeHi, uint dwMaxSizeLow, string lpName);
- [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
- public static extern bool UnmapViewOfFile(IntPtr pvBaseAddress);
- [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
- public static extern bool CloseHandle(IntPtr handle);
-
-
- public IntPtr fileMapping = IntPtr.Zero;
- public IntPtr mapView = IntPtr.Zero;
- const int FILE_MAP_COPY = 0x0001;
- const int FILE_MAP_WRITE = 0x0002;
- const int FILE_MAP_READ = 0x0004;
- const int FILE_MAP_ALL_ACCESS = 0x0002 | 0x0004;
- const int PAGE_READWRITE = 0x04;
- const int INVALID_HANDLE_VALUE = -1;
- const int ERROR_ALREADY_EXISTS = 183;
- private bool bValid;
- bool m_bAlreadyExist = false;
- bool m_bInit = false;
- long m_MemSize = 0;
-
- public ShareMemoryHelper()
- {
- }
-
- //~ShareMemoryHelper()
- //{
- // Close();
- //}
- public bool GetShareMemoryMap(string name, uint length)
- {
- m_MemSize = length;
- fileMapping = OpenFileMapping(PAGE_READWRITE, false, name);
- if (fileMapping == IntPtr.Zero)
- {
- return false;
- }
- mapView = MapViewOfFile(fileMapping, (uint)FILE_MAP_READ, 0, 0, length);
- if (mapView == IntPtr.Zero)
- {
- int a = GetLastError();
- Debug.Log(a);
- return false;
- }
- m_bInit = true;
- return true;
- }
-
- public int CreateShareMemoryMap(string strName, long lngSize)
- {
- if (lngSize <= 0 || lngSize > 0x00800000) lngSize = 0x00800000;
- m_MemSize = lngSize;
- if (strName.Length > 0)
- {
- fileMapping = CreateFileMapping(INVALID_HANDLE_VALUE, IntPtr.Zero, (uint)PAGE_READWRITE, 0, (uint)lngSize, strName);
- if (fileMapping == IntPtr.Zero)
- {
- return 2;
- }
-
- if (GetLastError() == ERROR_ALREADY_EXISTS)
- {
- m_bAlreadyExist = true;
- }
- else
- {
- m_bAlreadyExist = false;
- }
- mapView = MapViewOfFile(fileMapping, FILE_MAP_WRITE, 0, 0, (uint)lngSize);
- if (mapView == IntPtr.Zero)
- {
- m_bInit = false;
- CloseHandle(fileMapping);
- return 3;
- }
- else
- {
- m_bInit = true;
- if (m_bAlreadyExist == false)
- {
- }
- }
- }
- else
- {
- return 1;
- }
- return 0;
- }
-
- public int Write(byte[] bytData, int lngAddr, int lngSize)
- {
- if (lngAddr + lngSize > m_MemSize) return 2;
- if (m_bInit)
- {
- Marshal.Copy(bytData, lngAddr, mapView, lngSize);
- }
- else
- {
- return 1;
- }
- return 0;
- }
-
- public int Read(ref byte[] bytData, int lngAddr, int lngSize)
- {
- if (lngAddr + lngSize > m_MemSize) return 2;
- if (m_bInit)
- {
- Marshal.Copy(mapView, bytData, lngAddr, lngSize);
- }
- else
- {
- return 1;
- }
- return 0;
- }
-
- public void Close()
- {
- if (m_bInit)
- {
- UnmapViewOfFile(mapView);
- CloseHandle(fileMapping);
- }
- }
- }
中文乱码可尝试用以下方式解决:
- System.Text.Encoding gb2312 = System.Text.Encoding.GetEncoding("gb2312");
- 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文件夹下即可。