首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

VC++ 實現程式互斥執行:記憶體對應檔案實現單實例/多程式互斥

     編輯:關於VC++

前言

需求:確保同一時間僅有一個程式實例運行 (單實例),或多個不同程式互斥運行 (如「工具箱」與「主程式」不可同時開啟)。本文採用 記憶體對應檔案 實現跨進程同步,無需命名 Mutex 即可達成互斥與狀態傳遞。

核心 API

函數用途
CreateFileMapping建立/開啟命名共享記憶體區域
MapViewOfFile將共享記憶體映射至進程位址空間,取得指標
UnmapViewOfFile解除映射
CloseHandle關閉檔案對應物件控制碼
GetLastError檢查 ERROR_ALREADY_EXISTS 判斷是否已存在

完整實作代碼 (CWinApp::InitInstance)

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

關鍵技術細節

  1. (HANDLE)0xFFFFFFFF:INVALID_HANDLE_VALUE,告知系統使用分頁檔建立共享記憶體,不關聯實體檔案。
  2. 命名物件「MutexRunning」:全域核心物件命名空間,跨進程可見。多程式互斥時使用相同名稱。
  3. GetLastError() == ERROR_ALREADY_EXISTS:CreateFileMapping 成功時,若物件已存在會設定此錯誤碼,必須緊接呼叫檢查。
  4. 訊息傳遞:首個實例寫入提示字串,後續實例讀取並顯示,實現「誰先跑、誰佔用、晚來者顯示提示」完整語意。
  5. 資源清理:程式正常結束時呼叫 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 呼叫同時達成「檢查是否已存在」與「讀取/寫入狀態資訊」。代碼簡潔、無遺棄同步物件風險、支援豐富狀態傳遞,是單實例/互斥組場景的優選方案。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved