原理:CWnd::FindWindow 尋找特定類別名稱或標題的視窗。32 位元 Windows 下 hPrevInstance 永為 NULL,不可用。
BOOL CMyApp::FirstInstance() {
CWnd* pPrev = CWnd::FindWindow(NULL, _T("MyAppTitle")); // 以標題尋找
if (pPrev) {
// 找到既有視窗
CWnd* pPopup = pPrev->GetLastActivePopup(); // 取得最上層彈出視窗
if (pPrev->IsIconic()) // 若最小化則還原
pPrev->ShowWindow(SW_RESTORE);
pPopup->SetForegroundWindow(); // 置前顯示
return FALSE; // 非首次實例
}
return TRUE; // 首次實例
}
BOOL CMyApp::InitInstance() {
if (!FirstInstance()) return FALSE; // 直接退出
// ... 正常初始化 ...
}
| 函數 | 用途 |
|---|---|
| FindWindow | 依類別名/標題尋找頂層視窗 |
| GetLastActivePopup | 取得視窗擁有的最後啟動彈出視窗 |
| IsIconic | 判斷是否最小化 |
| ShowWindow(SW_RESTORE) | 還原視窗大小位置並啟動 |
| SetForegroundWindow | 將視窗帶到最前端並取得焦點 |
原理:CreateMutex 建立全系統唯一命名互斥體。若已存在同名互斥體,GetLastError() == ERROR_ALREADY_EXISTS。
#include <windows.h>
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR, int) {
const char* APP_MUTEX = "Global\\MyUniqueAppMutex_12345678"; // Global\ 可跨 Session
HANDLE hMutex = CreateMutex(NULL, FALSE, APP_MUTEX);
if (hMutex) {
if (GetLastError() == ERROR_ALREADY_EXISTS) {
MessageBox(NULL, "程式已在執行中!", "提示", MB_OK | MB_ICONINFORMATION);
CloseHandle(hMutex);
return 1;
}
// 首次實例,繼續執行...
MessageBox(NULL, "首次啟動", "提示", MB_OK);
}
// 程式結束前釋放
if (hMutex) CloseHandle(hMutex);
return 0;
}
BOOL CMyApp::InitInstance() {
const TCHAR* MUTEX_NAME = _T("Global\\MyApp_{GUID_HERE}");
m_hMutex = CreateMutex(NULL, FALSE, MUTEX_NAME);
if (GetLastError() == ERROR_ALREADY_EXISTS) {
// 找到既有視窗並置前 (輔助視窗法)
CWnd* pWnd = CWnd::FindWindow(NULL, _T("MyAppTitle"));
if (pWnd) {
CWnd* pPopup = pWnd->GetLastActivePopup();
if (pWnd->IsIconic()) pWnd->ShowWindow(SW_RESTORE);
pPopup->SetForegroundWindow();
}
CloseHandle(m_hMutex);
return FALSE;
}
// ... 正常初始化 ...
return TRUE;
}
int CMyApp::ExitInstance() {
if (m_hMutex) CloseHandle(m_hMutex);
return CWinApp::ExitInstance();
}
在 DLL 中使用 #pragma data_seg(".shared") 定義共享變數計數實例數。缺點:需額外 DLL、安全性較差 (任何進程可修改)、不支援 ASLR。現代開發不推薦。
Global\\ 前綴確保跨 Session (終端機服務/RDP) 唯一,並加入 GUID 避免衝突program OnlyOne;
uses Windows, Forms, uMain in 'uMain.pas' {FormMain};
{$R *.res}
var hMutex: THandle;
begin
hMutex := CreateMutex(nil, False, 'Global\\MyDelphiApp_UniqueName');
if (hMutex = 0) or (GetLastError = ERROR_ALREADY_EXISTS) then begin
MessageBox(0, '程式已在執行中!', '提示', MB_OK + MB_ICONINFORMATION);
if hMutex <> 0 then CloseHandle(hMutex);
Exit;
end;
Application.Initialize;
Application.CreateForm(TFormMain, FormMain);
Application.Run;
CloseHandle(hMutex);
end.
掌握互斥體 + 視窗尋找雙重機制,您的 VC++ 程式即擁有專業級單一實例保護能力。