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

        创建内存映射文件对象。

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

2.OpenFileMapping

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

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

3.MapViewOfFile

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

IntPtr MapViewOfFile(
    IntPtr hFileMapping, 
    uint dwDesiredAccess, 
    uint dwFileOffsetHigh, 
    uint dwFileOffsetLow, 
    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.读数据用到的函数

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

2.函数调用

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();
}

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

3.Unity读写数据实现

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文件夹下即可。

你可能感兴趣的:(Unity,C++,unity,c++,游戏引擎)