需求:確保同一時間僅有一個程式實例運行 (單實例),或多個不同程式互斥運行 (如「工具箱」與「主程式」不可同時開啟)。本文採用 記憶體對應檔案 實現跨進程同步,無需命名 Mutex 即可達成互斥與狀態傳遞。
| 函數 | 用途 |
|---|---|
CreateFileMapping | 建立/開啟命名共享記憶體區域 |
MapViewOfFile | 將共享記憶體映射至進程位址空間,取得指標 |
UnmapViewOfFile | 解除映射 |
CloseHandle | 關閉檔案對應物件控制碼 |
GetLastError | 檢查 ERROR_ALREADY_EXISTS 判斷是否已存在 |
BOOL CMyApp::InitInstance() {
// 1. 建立/開啟共享記憶體 (大小 128 位元組,足夠存放提示訊息)
HANDLE hMap = CreateFileMapping(
(HANDLE)0xFFFFFFFF, // INVALID_HANDLE_VALUE:使用分頁檔支援
NULL, // 預設安全性
PAGE_READWRITE, // 讀寫權限
0, 128, // 高/低 32 位大小 = 128 bytes
_T("MutexRunning") // 全域命名物件名稱
);
if (hMap == NULL) {
AfxMessageBox(_T("建立互斥共享記憶體失敗!"), MB_OK | MB_ICONSTOP);
return FALSE;
}
// 2. 判斷是否已存在
if (GetLastError() == ERROR_ALREADY_EXISTS) {
// 已有其他實例運行
LPVOID lpMem = MapViewOfFile(hMap, FILE_MAP_WRITE, 0, 0, 0);
CString strMsg = (LPCTSTR)lpMem; // 讀取既有實例寫入的訊息
UnmapViewOfFile(lpMem);
CloseHandle(hMap);
AfxMessageBox(strMsg, MB_OK | MB_ICONSTOP);
return FALSE; // 終止啟動
}
// 3. 首個實例:寫入提示訊息供後續實例讀取
LPVOID lpMem = MapViewOfFile(hMap, FILE_MAP_WRITE, 0, 0, 0);
strcpy((char*)lpMem, _T("XXX程式正在運行!"));
UnmapViewOfFile(lpMem);
// 注意:hMap 不在此關閉,維持映射直到程式結束
// ... 繼續標準初始化 ...
AfxEnableControlContainer();
// ...
// 程式結束前清理 (可在 ExitInstance 或主視窗 OnDestroy)
// CloseHandle(hMap);
return TRUE;
}
(HANDLE)0xFFFFFFFF:INVALID_HANDLE_VALUE,告知系統使用分頁檔建立共享記憶體,不關聯實體檔案。GetLastError() == ERROR_ALREADY_EXISTS:CreateFileMapping 成功時,若物件已存在會設定此錯誤碼,必須緊接呼叫檢查。CloseHandle(hMap),系統自動釋放共享記憶體。異常終止時核心物件隨進程自動回收。// 定義互斥組名稱陣列
LPCTSTR g_szMutexGroup[] = {
_T("MyApp_Toolbox_Mutex"),
_T("MyApp_Main_Mutex"),
_T("MyApp_Config_Mutex")
};
BOOL CheckMutexGroup() {
for (int i = 0; i < 3; i++) {
HANDLE hMap = CreateFileMapping((HANDLE)0xFFFFFFFF, NULL, PAGE_READWRITE, 0, 128, g_szMutexGroup[i]);
if (hMap && GetLastError() == ERROR_ALREADY_EXISTS) {
// 讀取訊息並提示
CloseHandle(hMap);
return FALSE;
}
if (hMap) CloseHandle(hMap);
}
// 全部可用,建立自身互斥
HANDLE hMyMap = CreateFileMapping((HANDLE)0xFFFFFFFF, NULL, PAGE_READWRITE, 0, 128, g_szMutexGroup[MY_INDEX]);
// 寫入自身提示訊息...
return TRUE;
}
| 方案 | 優點 | 缺點 |
|---|---|---|
命名 Mutex (CreateMutex) | 語意明確、內建擁有權語意、支援 WaitForSingleObject 等待 | 需處理遺棄 Mutex (擁有者異常終止) |
| 記憶體對應檔案 (本文) | 可攜帶自定義資料 (提示訊息、PID、版本)、無遺棄問題 | 需手動管理映射生命週期 |
尋找視窗 (FindWindow) | 簡單、無需核心物件 | 僅適用 GUI 程式、視窗類別名可被偽裝、不可靠 |
檔案鎖定 (LockFile) | 跨網路共享可用 | 殘留鎖定檔案風險、效能較差 |
記憶體對應檔案實現程式互斥,核心優勢在於「同步 + 資料傳遞」一體化:單一 CreateFileMapping 呼叫同時達成「檢查是否已存在」與「讀取/寫入狀態資訊」。代碼簡潔、無遺棄同步物件風險、支援豐富狀態傳遞,是單實例/互斥組場景的優選方案。