Windows 進程是資源容器 (虛擬地址空間、句柄表、執行緒)。VC++ 透過 CreateProcess 創建子進程,實現多任務隔離、權限分離、穩定性保護。本文系統講解 CreateProcess 完整參數、啟動流程、環境變量繼承、WaitForSingleObject 同步等待、GetExitCodeProcess 獲取退出碼、錯誤處理最佳實踐。
CreateProcess 完整原型
BOOL CreateProcess(
LPCTSTR lpApplicationName, // 模組路徑 (NULL 時從命令列解析)
LPTSTR lpCommandLine, // 完整命令列字串 (可修改)
LPSECURITY_ATTRIBUTES lpProcessAttr, // 進程安全屬性 (NULL=預設, 不可繼承)
LPSECURITY_ATTRIBUTES lpThreadAttr, // 執行緒安全屬性
BOOL bInheritHandles, // TRUE: 子進程繼承父進程可繼承句柄
DWORD dwCreationFlags, // 創建標誌
LPVOID lpEnvironment, // 新環境塊指標 (NULL=繼承父進程)
LPCTSTR lpCurrentDirectory, // 子進程工作目錄 (NULL=繼承)
LPSTARTUPINFO lpStartupInfo, // 主視窗顯示/標準輸入輸出/標準錯誤重導向
LPPROCESS_INFORMATION lpProcInfo // 輸出: hProcess, hThread, dwProcessId, dwThreadId
);
關鍵參數深度解析
1. lpApplicationName vs lpCommandLine
- 兩者至少一個非 NULL。
- 最佳實踐:
lpApplicationName = NULL,完整路徑與參數全放 lpCommandLine (如 L"C:\\App\\Child.exe --arg1 value"),系統自動解析第一個 token 為模組路徑,支援空格路徑 (需加雙引號)。
2. dwCreationFlags 常用值
| 旗標 | 效果 |
CREATE_NEW_CONSOLE | 子進程擁有獨立主控台視窗 |
CREATE_NO_WINDOW | 無主控台 (GUI 程式適用) |
DETACHED_PROCESS | 無主控台,且不繼承父主控台 |
CREATE_SUSPENDED | 主執行緒建立後暫停,需 ResumeThread 繼續 (注入/調試用) |
CREATE_UNICODE_ENVIRONMENT | lpEnvironment 為 Unicode 環境塊 |
DEBUG_PROCESS / DEBUG_ONLY_THIS_PROCESS | 父進程作為除錯器附加 |
3. lpEnvironment 環境塊
NULL:子進程繼承父進程環境變數 (複製一份)。
- 自定義:指向
Key=Value\0Key2=Value2\0\0 格式的記憶體區塊 (雙 Null 結尾)。
- 獲取當前環境:
GetEnvironmentStrings() (需 FreeEnvironmentStrings)。
4. STARTUPINFO 標準輸入/輸出/錯誤重導向 (管道通訊核心)
STARTUPINFO si = { sizeof(si) };
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdInput = hChildStdinRead; // 父寫入 -> 子讀取
si.hStdOutput = hChildStdoutWrite; // 子寫入 -> 父讀取
si.hStdError = hChildStdoutWrite; // 合併錯誤輸出
// 管道建立需: CreatePipe(&hRead, &hWrite, &sa, 0); sa.bInheritHandle = TRUE;
完整創建、等待、獲取退出碼範例
bool LaunchAndWait(const CString& cmdLine, DWORD& exitCode, DWORD timeoutMs = INFINITE) {
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi = {0};
// 命令列需可修改緩衝區
CString cmd = cmdLine; // 複本
LPTSTR pCmd = cmd.GetBuffer();
BOOL ok = CreateProcess(NULL, pCmd, NULL, NULL, FALSE,
CREATE_NO_WINDOW, // 或 CREATE_NEW_CONSOLE
NULL, NULL, &si, &pi);
cmd.ReleaseBuffer();
if (!ok) return false;
// 關閉父進程不需要的句柄
CloseHandle(pi.hThread); // 不需要執行緒句柄
// 等待進程結束
DWORD wait = WaitForSingleObject(pi.hProcess, timeoutMs);
if (wait == WAIT_OBJECT_0) {
GetExitCodeProcess(pi.hProcess, &exitCode);
} else if (wait == WAIT_TIMEOUT) {
// 可選: TerminateProcess(pi.hProcess, 1);
}
CloseHandle(pi.hProcess);
return (wait == WAIT_OBJECT_0);
}
// 使用
DWORD code;
if (LaunchAndWait(_T("\"C:\\Program Files\\MyApp\\Worker.exe\" /task 123"), code, 30000)) {
TRACE(_T("Child exited with code %lu\n"), code);
}
進程與執行緒的區別回顧
- 進程:獨立地址空間、資源擁有者、開銷大、隔離性強 (一個崩潰不影響其他)。
- 執行緒:共享進程地址空間/資源、開銷小、切換快、同步複雜 (鎖/死鎖)、一個崩潰導致全進程倒下。
- 選擇原則: 需隔離 (不穩定模組、不同權限、不同用戶) 用進程;需高頻共享數據、緊密協作用執行緒。
常見錯誤與避坑
lpCommandLine 指向常量字串字面值 → 崩潰 (系統嘗試修改)。必須用可寫緩衝區 (CString::GetBuffer/std::vector)。
- 路徑含空格未加雙引號 → 啟動錯誤檔案。格式:
L"\"C:\\Program Files\\App.exe\" arg"。
- 忘記
CloseHandle(pi.hProcess) / pi.hThread → 核心物件洩漏,導致進程無法完全退出 (殭屍進程)。
bInheritHandles=TRUE 但未在 SECURITY_ATTRIBUTES 設置 bInheritHandle=TRUE → 句柄實際不繼承。
- 等待無限制
INFINITE 導致父進程凍結 → 設置超時或另起執行緒等待。
掌握 CreateProcess 細節,是 Windows 系統級開發的必修課。
- 上一篇文章: VC++ 實現程式自我重啟:CreateProcess 在 OnClose 中啟動新實例
- 下一篇文章: VC++ 串口通訊完整指南:MSComm ActiveX 控件 vs 直接 Win32 API (CreateFile/ReadFile/WriteFile) 兩大方案對比
Copyright ©
程式碼之家 All Rights Reserved