VC++中用内存映射文件实现程序间互斥运行

Author: 粟利民 孙强 Date: 2000年 第2期

  在软件的开发过程中,有时需要控制一些程序使它们不能同时运行,也就是多个程序间互斥运行(还包括禁止同一程序运行多个实例)。针对这一问题,我们在Visual C++ 6.0中利用内存映射文件实现了多个程序间的互斥运行。
  内存映射文件可以创建一个没有和磁盘文件相联系的内存对象,将文件的信息映射到一个进程的地址空间上,我们可以访问该文件中的数据,就如同它位于内存中一样。同时,在程序设计中可以给内存映射文件对象起一个名字,这个名字在整个系统中是唯一的,这个名字可以在多个进程之间共享,通过名字共享能实现进行信息交换,进而实现多个程序间的互斥运行。
  在讲述具体的编程方法之前,让我们先介绍和内存映射文件操作有关的几个重要的函数:
  1)CreateFileMapping函数为指定的文件创建一个文件映射对象,该函数的原型如下:
  HANDLE CreateFileMapping(
  HANDLE hFile,   // 用于映射的文件句柄
  LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
  // 内存映射文件的安全描述符
  DWORD flProtect,  // 文件的保护方式
  DWORD dwMaximumSizeHigh, // 文件映射对象的最大长度的高32位
  DWORD dwMaximumSizeLow, // 最大长度的低32位
  LPCTSTR lpName // 指定这个内存映射文件的名字
  ); 
  值得注意的是,参数如果是0xFFFFFFFF,将在操作系统虚拟内存页面替换文件中创建文件映射对象,而不是使用磁盘文件,同时必须给出这个映射对象的大小。
  2)MapViewOfFile函数将文件的视图映射到一个进程的地址空间上,返回LPVOID类型的内存指针,通过它,就可以直接访问文件视图中的信息。
  LPVOID MapViewOfFile(
  HANDLE hFileMappingObjct, // 映射文件对象句柄
  DWORD dwDesiredAccess, // 访问模式
  DWORD dwFileOffsetHigh, // 文件偏移地址的高32位
  DWORD dwFileOffsetLow, // 文件偏移地址的低32位
  DWORD dwNumberOfBytesToMap // 映射视图的大小
  );
  在Visual C++ 6.0中我们用默认方式生成基于对话框的应用程序,在程序的初始化阶段,在CwinApp派生类的InitInstance函数的开始处,添加以下代码:
  {  // 创建内存映射文件对象,MutexRunning是其名字,所有需要互斥运行
  // 的程序都使用这个名字 (这些代码对于需要互斥运行的程序是通用的)
  HANDLE hMap=CreateFileMapping((HANDLE)0xFFFFFFFF, NULL,
  PAGE_READWRITE, 0, 128, ″MutexRunning″);
  if(hMap==NULL)// 如果创建失败
  {  AfxMessageBox(″创建用于互斥运行的内存映射文件对象失败!″,MB_OK|MB_ICONSTOP);
  return FALSE;  // 退出此程序 }
  // 如果已经存在这个同名对象, 说明已有需要互斥的其他程序运行了
  else if(GetLastError()==ERROR_ALREADY_EXISTS)
  { LPVOID lpMem=MapViewOfFile(hMap, FILE_MAP_WRITE, 0,0,0);
  CString str=(char*)lpMem;   // 获得已在运行的程序的描述信息
  UnmapViewOfFile(lpMem);   // 解除映射图
  CloseHandle(hMap); // 关闭此对象
  AfxMessageBox(str, MB_OK|MB_ICONSTOP); // 显示有关的描述信息
  return FALSE;// 退出此程序 }
  else // 经过上面的检查, 说明这是第一个运行的互斥程序
  {  LPVOID lpMem=MapViewOfFile(hMap, FILE_MAP_WRITE, 0,0,0);
  // 这里可写入该程序运行的描述信息, 上面的错误提示就是这个信息
  strcpy((char*)lpMem, ″xxx程序正在运行!″);
  UnmapViewOfFile(lpMem);  // 解除映射图 }
  // 下面可以继续执行函数InitInstance原有的代码了
  AfxEnableControlContainer();
  ......
  // 当程序运行结束了,要记住调用ClsoeHandle(hMap)关闭这个对象句柄,
  // 这里可以在InitInstance函数最后 return FALSE 之前调用
  CloseHandle(hMap); //关闭内存映射文件对象句柄
  return FALSE;  }
  以上的程序在Visual C++ 6.0中已调试通过。其他非对话框类型的程序可以在各自的初始化和终止阶段添加类似的代码,只是如果内存映射文件对象的句柄hMap可能在不同函数中使用,那就要将其定义成CWinApp派生类的成员变量或是全局变量了。