目录
一、内存共享用到的函数
1.CreateFileMapping
2.OpenFileMapping
3.MapViewOfFile
4.UnmapViewOfFile
5.CloseHandle
6.GetLastError
二、Unity测内存共享(读数据)
1.读数据用到的函数
2.函数调用
3.Unity读写数据实现
三、中文乱码解决方式
之前的博文已经实现了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文件夹下即可。