進程是作業系統中載入記憶體、正在執行的應用程式實例。每個進程包含核心物件(供系統管理統計資訊)與位址空間(含程式碼、資料、執行緒堆疊、堆積配置等動態空間)。進程本身無法執行操作,必須擁有至少一個執行緒(主執行緒)在其位址空間內執行程式碼。主執行緒結束即意味著進程終止,系統會自動釋放其位址空間。
每個載入進程的可執行檔或 DLL 映像會獲得唯一的實例控制碼 HINSTANCE,實際上記錄該模組在進程中的基底記憶體位址。VC++ 連結器產生的程式預設基底位址為 0x00400000,通常無需修改。可透過 GetModuleHandle() 取得指定模組的基底位址。
CreateProcess() 會建立新進程核心物件(參考計數初始為 1)、配置虛擬位址空間、載入可執行檔與必要 DLL、建立主執行緒核心物件並啟動 WinMain() 入口函式。
BOOL CreateProcess(
LPCTSTR lpApplicationName, // 可執行模組名稱
LPTSTR lpCommandLine, // 命令列字串
LPSECURITY_ATTRIBUTES lpProcessAttributes, // 進程安全屬性
LPSECURITY_ATTRIBUTES lpThreadAttributes, // 執行緒安全屬性
BOOL bInheritHandles, // 控制碼繼承旗標
DWORD dwCreationFlags, // 建立旗標(可設優先權)
LPVOID lpEnvironment, // 環境區塊指標
LPCTSTR lpCurrentDirectory, // 目前目錄
LPSTARTUPINFO lpStartupInfo, // 啟動資訊結構
LPPROCESS_INFORMATION lpProcessInformation // 進程資訊結構
);
CString sCmdLine;
char cWinDir[MAX_PATH], cCmdLine[MAX_PATH];
DWORD dwExitCode;
PROCESS_INFORMATION pi;
STARTUPINFO si = { sizeof(si) };
GetWindowsDirectory(cWinDir, MAX_PATH);
sCmdLine = CString(cWinDir) + _T("\\Notepad.exe");
_tcscpy(cCmdLine, sCmdLine);
BOOL ret = CreateProcess(NULL, cCmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
if (ret) {
CloseHandle(pi.hThread); // 關閉主執行緒控制碼
WaitForSingleObject(pi.hProcess, INFINITE); // 等待子進程結束
GetExitCodeProcess(pi.hProcess, &dwExitCode); // 取得結束代碼
CloseHandle(pi.hProcess); // 關閉進程控制碼
}
此模式下子進程如同同步函式呼叫,父進程在 WaitForSingleObject 處阻塞,子進程結束後繼續執行。
BOOL ret = CreateProcess(NULL, cCmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
if (ret) {
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess); // 立即關閉控制碼,不等待
}
適用於獨立功能模組,大型軟體常採用此架構將功能拆分為獨立執行檔。
dwCreationFlags 設為 HIGH_PRIORITY_CLASS 等常數SetPriorityClass(pi.hProcess, HIGH_PRIORITY_CLASS);SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);某些程式(如獨佔序列埠)要求系統中僅存在一個實例。核心邏輯:啟動時檢查是否已有實例,若有則退出。
HANDLE hMutex = CreateMutex(NULL, FALSE, _T("MyUniqueAppName"));
if (GetLastError() == ERROR_ALREADY_EXISTS) {
CloseHandle(hMutex);
return FALSE; // 已有實例,終止啟動
}
CreateMutex 若發現同名互斥量已存在,會傳回既有控制碼並設定錯誤碼 ERROR_ALREADY_EXISTS,以此實現單一實例檢查。
#pragma data_seg("Shared")
volatile long g_lAppInstance = 0;
#pragma data_seg()
#pragma comment(linker, "/section:Shared,RWS")
// 在 InitInstance 開頭:
if (++g_lAppInstance > 1)
return FALSE;
利用編譯器指令建立跨進程共享資料區,所有進程實例共用同一變數計數器。
關鍵點: 以上程式碼必須放在 CWinApp::InitInstance() 最開頭,確保在任何視窗建立前完成檢查。
| 函式 | 適用範圍 | 特性 | 風險 |
|---|---|---|---|
ExitProcess(uExitCode) | 本進程 | 正常終止,通知 DLL、清理執行緒資源 | 強制性強,後續程式碼不執行,若有 new 未 delete 會記憶體洩漏 |
TerminateProcess(hProcess, uExitCode) | 任意進程(需控制碼) | 非同步、強制終止,不通知被終止進程 | 極度危險,無法執行清理程式碼,僅作最後手段 |
若呼叫 TerminateProcess 後需確認進程確實結束,可搭配 WaitForSingleObject(hProcess, INFINITE) 等待。
多進程管理是多工程式設計的核心。本文詳細說明子進程建立與同步/非同步模式、優先權控制、單一實例互斥機制(具名互斥量與共享變數兩種做法)、以及進程終止的正常與強制方式。掌握這些技術可構建穩健、模組化的 Windows 應用程式架構。