65.9K
CodeProject 正在变化。 阅读更多。
Home

使用“文件映射”技术实现单实例应用程序

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (10投票s)

2009 年 4 月 29 日

CPOL

3分钟阅读

viewsIcon

28102

downloadIcon

630

另一种单实例实现

引言

创建“单实例应用程序”(一次只允许运行一个实例)的常用方法通常包含以下两个步骤:

  1. 应用程序启动时,创建一个命名的全局对象(例如 Mutex、Semaphore)。如果实例已启动,则会失败并显示错误代码 ERROR_ALREADY_EXISTS
  2. 如果应用程序实例正在运行,使用 FindWindowFindWindowEx 查找其主窗口,然后恢复并将窗口置于最前。

代码大致如下:

HANDLE hMutex = CreateMutex(NULL, FALSE, MUTEX_NAME);
if (GetLastError() == ERROR_ALREADY_EXISTS)
{
HWND hWnd = FindWindowEx(NULL, NULL, CLASSNAME, NULL);
if (IsWindow(hWnd))
{
ShowWindow(hWnd, SW_RESTORE);
BringWindowToTop(hWnd);
SetForegroundWindow(hWnd);
}
}

我正在寻找一种新的实现,因为我不太喜欢以前常用的方法。要找到正在运行实例的主窗口,必须使用已知的类名和(或)窗口名称创建它,但在 MFC 中这样做非常尴尬(我们必须重写主窗口的 PreCreateWindow 函数并设置 CREATESTRUCTlpszClasslpszName)。另一个问题是不建议使用 FindWindow

背景

我们可以将这两个步骤与“文件映射”技术结合使用。文件映射可用于在进程之间共享数据。当第一个应用程序实例启动时,它会创建一个基于分页文件(用作系统虚拟内存的磁盘文件)的命名文件映射对象,并将它的主窗口句柄存储在 4 字节的映射共享内存中。后面的实例找到并打开该命名文件映射对象,并检索映射共享内存中正在运行的主窗口句柄。

文件映射相关函数在此列出:

  1. HANDLE CreateFileMapping(
    HANDLE hFile, // handle to file to map
    LPSECURITY_ATTRIBUTES lpFileMappingAttributes, // optional security attributes
    DWORD flProtect, // protection for mapping object
    DWORD dwMaximumSizeHigh, // high-order 32 bits of object size
    DWORD dwMaximumSizeLow, // low-order 32 bits of object size
    LPCTSTR lpName // name of file-mapping object
    );

    创建一个具有系统全局名称的文件映射对象。 hFile 参数可以是已打开的磁盘文件句柄,也可以是 (HANDLE)-1 以引用分页文件。 lpName 参数是对象的全局名称,用于查找由另一个进程创建的文件映射对象。

  2. HANDLE OpenFileMapping(
    DWORD dwDesiredAccess, 	// access mode
    BOOL bInheritHandle, 	// inherit flag
    LPCTSTR lpName 		// pointer to name of file-mapping object
    );
    Open the File Mapping object with name referred by “lpName” parameter.
  3. LPVOID MapViewOfFile(
    HANDLE hFileMappingObject, 	// file-mapping object to map into address space
    DWORD dwDesiredAccess, 	// access mode
    DWORD dwFileOffsetHigh, 	// high-order 32 bits of file offset
    DWORD dwFileOffsetLow, 	// low-order 32 bits of file offset
    DWORD dwNumberOfBytesToMap 	// number of bytes to map
    );

    将文件视图映射到调用进程的地址空间。 返回值是一个 VOID 指针,它指向磁盘文件的偏移地址。 磁盘文件可以是真实文件或分页文件。

参考

我编写了一个类 CSingleInstance 来包装这些函数并简化其使用。 整个实现只包含一个源文件“SingleInstance.h”。 只需两行源代码即可使用它。

函数参考

1. CCSingleInstance::CCSingleInstance(LPCTSTR pszUniqueName=NULL,
    int nCmdShow=SW_RESTORE)

构造函数接受两个参数。

  • pszUniqueName 是文件映射对象的全局唯一名称,如果它是 NULLCSingleInstance 将使用默认名称 __akui__
  • nCmdShow 告诉激活时如何显示正在运行的实例的主窗口。
2. void CCSingleInstance::SetWindow(HWND hWnd)
// Store main window handle for later ones to active me.

Using the Code

  1. 包含“SingleInstance.h
  2. 声明一个全局 CSingleInstance 对象
  3. 在主窗口句柄有效后,调用 SetWindow 存储它

在 MFC 应用程序中,只需在 AppName.cpp 中声明 CSingleInstance 对象,在 theApp 声明之前。 并在主窗口句柄有效时,在 CAppName::InitInstance() 中的返回之前调用 SetWindow

如果要对源代码进行一些更改,请注意,全局对象是在应用程序实际开始运行(进入 WinMain)之前创建的。 所以不要调用 MFC 函数,因为 MFC 可能尚未正确初始化。

历史

  • 2009 年 4 月 29 日:初始发布
© . All rights reserved.