Windows 进程间通信 共享内存(FileMapping)

    技术2022-07-05  202

    Windows 进程间通信 共享内存(FileMapping)

    net高级编程 2010-09-26 15:00:54 阅读44 评论0   字号: 订阅

      FileMapping用于将存在于磁盘的文件放进一个进程的虚拟地址空间,并在该进程的虚拟地址空间中产生一个区域用于存放该文件,这个空间就叫做File View,系统并同时产生一个File Mapping Object(存放于物理内存中)用于维持这种映射关系,这样当多个进程需要读写那个文件的数据时,它们的File View其实对应的都是同一个File  Mapping  Object,这样做可节省内存和保持数据的同步性,并达到数据共享的目的。

    当然在一个应用向文件中写入数据时,其它进程不应该去读取这个正在写入的数据。这就需要进行一些同步的操作。 下边来看一下具体的 API      CreateFileMaping 的用法:     HANDLE   CreateFileMapping(                           // 返回 File   Mapping   Object 的句柄         HANDLE   hFile,                                         //   想要产生映射的文件的句柄         LPSECURITY_ATTRIBUTES   lpAttributes,    //   安全属性(只对 NT 2000 生效)         DWORD   flProtect,                                    //   保护标致         DWORD   dwMaximumSizeHigh,                  //   DWORD 的高位中存放               File Mapping  Object                                  //    的大小         DWORD   dwMaximumSizeLow,                   //   DWORD 的低位中存放       File Mapping Object                                     //    的大小(通常这两个参数有一个为 0           LPCTSTR   lpName                                     //   File   Mapping   Object 的名称。     );     1) 物理文件句柄     任何可以获得的物理文件句柄,如果你需要创建一个物理文件无关的内存映射也无妨,将它设置成为 0xFFFFFFFF(INVALID_HANDLE_VALUE) 就可以了 . 如果需要和物理文件关联,要确保你的物理文件创建的时候的访问模式和 " 保护设置 " 匹配,比如 : 物理文件只读,内存映射需要读写就会发生错误。推荐你的物理文件使用独占方式创建。 如果使用 INVALID_HANDLE_VALUE ,也需要设置需要申请的内存空间的大小,无论物理文件句柄参数是否有效 , 这样 CreateFileMapping 就可以创建一个和物理文件大小无关的内存空间给你 , 甚至超过实际文件大小,如果你的物理文件有效,而大小参数为 0 ,则返回给你的是一个和物理文件大小一样的内存空间地址范围。返回给你的文件映射地址空间是可以通过复制,集成或者命名得到,初始内容为 0 2) 保护设置    就是安全设置 , 不过一般设置 NULL 就可以了 , 使用默认的安全配置 . win2k 下如果需要进行限制 , 这是针对那些将内存文件映射共享给整个网络上面的应用进程使用是 , 可以考虑进行限制 . 3) 高位文件大小 32 位地址空间 , 设置为 0 4) 共享内存名称 命名可以包含 "Global" 或者 "Local" 前缀在全局或者会话名空间初级文件映射 . 其他部分可以包含任何除了 () 以外的字符 , 可以参考 Kernel Object Name Spaces. 5) 调用 CreateFileMapping 的时候 GetLastError 的对应错误    ERROR_FILE_INVALID     如果企图创建一个零长度的文件映射 , 应有此报    ERROR_INVALID_HANDLE   如果发现你的命名内存空间和现有的内存映射 , 互斥量 , 信号量 , 临界区同名就麻烦了    ERROR_ALREADY_EXISTS   表示内存空间命名已经存在 使用函数 CreateFileMapping 创建一个想共享的文件数据句柄,然后使用 MapViewOfFile 来获取共享的内存地址,然后使用 OpenFileMapping 函数在另一个进程里打开共享文件的名称,这样就可以实现不同的进程共享数据。 下边是 C# 是对使用的接口函数声明:   [DllImport("kernel32", SetLastError = true, CharSet = CharSet.Auto)] public static extern IntPtr CreateFile(string lpFileName,     int dwDesiredAccess, int dwShareMode, IntPtr lpSecurityAttributes,     int dwCreationDisposition, int dwFlagsAndAttributes,     IntPtr hTemplateFile);   [DllImport("kernel32", SetLastError = true, CharSet = CharSet.Auto)] public static extern IntPtr CreateFileMapping(IntPtr hFile,     IntPtr lpAttributes, int flProtect,     int dwMaximumSizeLow, int dwMaximumSizeHigh, string lpName);   [DllImport("kernel32", SetLastError = true)] public static extern bool FlushViewOfFile(IntPtr lpBaseAddress,     IntPtr dwNumBytesToFlush);   [DllImport("kernel32", SetLastError = true)] public static extern IntPtr MapViewOfFile(     IntPtr hFileMappingObject, int dwDesiredAccess, int dwFileOffsetHigh,     int dwFileOffsetLow, IntPtr dwNumBytesToMap);   [DllImport("kernel32", SetLastError = true, CharSet = CharSet.Auto)] public static extern IntPtr OpenFileMapping(     int dwDesiredAccess, bool bInheritHandle, string lpName);   [DllImport("kernel32", SetLastError = true)] public static extern bool UnmapViewOfFile(IntPtr lpBaseAddress);   [DllImport("kernel32", SetLastError = true)] public static extern bool CloseHandle(IntPtr handle);       我们在示例里 Server 端建立的一个 FileMapping, 命名为: @"Global/MyFileMappingObject" ; 这样我们在 Client 端就可以打开同名的 FileMapping ,这样在 Server Client 之前进行通信。每次 Server 将数据写入后,我们通过 Message 的方式通知 Client 端数据已经准备好,可以读取了。(关于 Message 我们之前说过,不在细说。) 具体看一下代码: 在看具体的代码这之前,先看一下其中用到的一些常量定义:       [Flags]     public enum MapProtection     {         PageNone = 0x00000000,         // protection - mutually exclusive, do not or         PageReadOnly = 0x00000002,         PageReadWrite = 0x00000004,         PageWriteCopy = 0x00000008,         // attributes - or-able with protection         SecImage = 0x01000000,         SecReserve = 0x04000000,         SecCommit = 0x08000000,         SecNoCache = 0x10000000,     }       public enum MapAccess     {         FileMapCopy = 0x0001,         FileMapWrite = 0x0002,         FileMapRead = 0x0004,         FileMapAllAccess = 0x001f, }   public const short FILE_ATTRIBUTE_NORMAL = 0x80; public const short INVALID_HANDLE_VALUE = -1; public const uint GENERIC_READ = 0x80000000; public const uint GENERIC_WRITE = 0x40000000; public const uint FILE_SHARE_READ = 0x00000001; public const uint CREATE_NEW = 1; public const uint CREATE_ALWAYS = 2; public const uint OPEN_EXISTING = 3;   Windows Message:   public const int WM_USER = 0x0400; public const int WM_USER_DATAREADY = WM_USER + 101;     下边是 Server 端的:   创建 MapView:   fileHandle = Win32Wrapper.CreateFileMapping(     IntPtr.Zero, IntPtr.Zero,     (int)(MapProtection.PageReadWrite | MapProtection.SecCommit),     0, 1000000, Win32Wrapper.MappingFileName); mappingHandle = Win32Wrapper.MapViewOfFile(     fileHandle, (int)MapAccess.FileMapWrite, 0, 0, new IntPtr(1024)); if (mappingHandle == IntPtr.Zero) {     MessageBox.Show("Open mapping file failed!"); }   写入信息并通知 Client 端:    private void btnSend_Click(object sender, EventArgs e)         {             //Write message             string message = string.IsNullOrEmpty(this.sendTxt.Text) ?                 "no message" : this.sendTxt.Text;             byte[] source = Encoding.ASCII.GetBytes(message);             byte[] msg = new byte[1024];             Array.Copy(source, msg, source.Length);             Marshal.Copy(msg, 0, mappingHandle, msg.Length);             Win32Wrapper.FlushViewOfFile(mappingHandle, new IntPtr(1024));               //Send message to client             IntPtr handle = GetClientMainFormHandle();             if (handle != IntPtr.Zero)                 Win32Wrapper.SendMessage(                     handle, Win32Wrapper.WM_USER_DATAREADY,                     IntPtr.Zero, IntPtr.Zero);         }   得到 Client 端主窗体句柄的代码:    private IntPtr GetClientMainFormHandle()         {             string name =                 @"CSharp.MultiProcess.Communication.FileMappingClient";             Process process = GetProcessByName(name);             return process.MainWindowHandle;         }           private Process GetProcessByName(string processName)         {             Process[] processes = Process.GetProcesses();             foreach (Process p in processes)             {                 //just for debug                 //this method has some question because                 //the visual studio started process name                 //is not same with the release. so yan can                 //close visual studio to test the project.                 //normal, you should use                 //if(p.ProcessName == processName)                   //debug                 if (p.ProcessName.StartsWith(processName))                     return p;                   //release                 if (p.ProcessName == processName)                     return p;             }             return null;         }   关闭 FileMapping:    private void Server_FormClosing(             object sender, FormClosingEventArgs e)         {             if (mappingHandle != IntPtr.Zero)             {                 Win32Wrapper.UnmapViewOfFile(mappingHandle);                 Win32Wrapper.CloseHandle(mappingHandle);             }             if (fileHandle != IntPtr.Zero)                 Win32Wrapper.CloseHandle(fileHandle);         }   Client 端代码: 当有 Message 收到时接收发送的数据:    protected override void WndProc(ref Message m)         {             switch (m.Msg)             {                 case Win32Wrapper.WM_USER_DATAREADY:                    RecevieMessage();                     break;                 default:                     base.WndProc(ref m);                     break;             }         }           private void RecevieMessage()         {             IntPtr handle = Win32Wrapper.OpenFileMapping(                 (int)MapProtection.PageReadWrite, false,                 Win32Wrapper.MappingFileName);             IntPtr mappingFile = Win32Wrapper.MapViewOfFile(                 handle, (int)MapAccess.FileMapRead,                 0, 0, new IntPtr(1024));             //Marshal.GetLastWin32Error();             byte[] msg = new byte[1024];             Marshal.Copy(mappingFile, msg, 0, 1024);             string message = Encoding.ASCII.GetString(msg);             this.listBox1.Items.Add(message);             Win32Wrapper.UnmapViewOfFile(mappingFile);             Win32Wrapper.CloseHandle(mappingFile);             Win32Wrapper.CloseHandle(handle);         }

    最新回复(0)