CreateProcess 是 Windows 建立新進程的唯一核心 API (底層為 NtCreateUserProcess)。功能強大但參數繁多,常見錯誤包括:命令列字串緩衝區不足、安全屬性繼承錯誤、環境區塊格式錯誤、當前目錄未正確設定。本文提供完整、可直接用於生產環境的封裝範例與最佳實務。
BOOL CreateProcess(
LPCSTR lpApplicationName, // 模組名稱 (NULL 時從命令列解析)
LPSTR lpCommandLine, // **完整命令列** (需可寫入緩衝區)
LPSECURITY_ATTRIBUTES lpProcessAttributes, // 進程安全描述符 (NULL=預設)
LPSECURITY_ATTRIBUTES lpThreadAttributes, // 執行緒安全描述符
BOOL bInheritHandles, // TRUE: 子進程繼承可繼承控制代碼
DWORD dwCreationFlags, // 建立旗標 (CREATE_SUSPENDED, DETACHED_PROCESS, CREATE_NEW_CONSOLE...)
LPVOID lpEnvironment, // 環境區塊指標 (NULL=繼承父進程)
LPCSTR lpCurrentDirectory, // 子進程當前目錄 (NULL=繼承父進程)
LPSTARTUPINFO lpStartupInfo, // 啟動資訊 (視窗顯示、標準控制代碼重導向)
LPPROCESS_INFORMATION lpProcessInformation // 輸出: 進程/執行緒句柄、ID
);
STARTUPINFO si = { sizeof(STARTUPINFO) }; // cb 必須設定大小!
PROCESS_INFORMATION pi = {0};
// 常見啟動設定
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE; // 或 SW_SHOWDEFAULT, SW_MINIMIZE
// 重導向標準輸入/輸出/錯誤 (需 bInheritHandles=TRUE, 建立管道)
si.hStdInput = hChildStdinRead;
si.hStdOutput = hChildStdoutWrite;
si.hStdError = hChildStderrWrite;
si.dwFlags |= STARTF_USESTDHANDLES;
#include
#include
#include
// 例外類別
class ProcessException : public std::runtime_error {
public:
ProcessException(const char* msg, DWORD err)
: std::runtime_error(std::string(msg) + " Error: " + std::to_string(err)) {}
};
// 建立進程並等待結束,回傳退出碼
DWORD CreateChildProcessAndWait(const std::string& cmdLine, DWORD timeoutMs = INFINITE) {
// 命令列需可寫入緩衝區 (CreateProcess 可能修改)
std::vector cmdBuf(cmdLine.begin(), cmdLine.end());
cmdBuf.push_back('\0');
STARTUPINFO si = { sizeof(STARTUPINFO) };
PROCESS_INFORMATION pi = {0};
// 預設安全屬性、不繼承控制代碼、無特殊旗標、繼承環境與目錄
if (!CreateProcess(
NULL, // lpApplicationName
cmdBuf.data(), // lpCommandLine (可修改緩衝區)
NULL, NULL, // Process/Thread Security
FALSE, // bInheritHandles
0, // dwCreationFlags
NULL, // Environment
NULL, // CurrentDirectory
&si, &pi)) // StartupInfo, ProcessInfo
{
throw ProcessException("CreateProcess failed", GetLastError());
}
// 立即關閉我們不需要的控制代碼 (避免洩漏)
CloseHandle(pi.hThread); // 不需等待主執行緒單獨結束
// 等待進程結束
DWORD waitResult = WaitForSingleObject(pi.hProcess, timeoutMs);
if (waitResult == WAIT_FAILED)
throw ProcessException("WaitForSingleObject failed", GetLastError());
if (waitResult == WAIT_TIMEOUT)
throw ProcessException("Process timeout", ERROR_TIMEOUT);
// 取得退出碼
DWORD exitCode = 0;
if (!GetExitCodeProcess(pi.hProcess, &exitCode))
throw ProcessException("GetExitCodeProcess failed", GetLastError());
CloseHandle(pi.hProcess);
return exitCode;
}
// 使用範例
int main() {
try {
DWORD exitCode = CreateChildProcessAndWait("notepad.exe \"C:\\Test\\ReadMe.txt\"", 30000);
printf("Notepad exited with code: %lu\n", exitCode);
} catch (const ProcessException& e) {
fprintf(stderr, "Error: %s\n", e.what());
return 1;
}
return 0;
}
DWORD CreateChildProcessAndWait(const AnsiString& cmdLine) {
PROCESS_INFORMATION pi = {0};
STARTUPINFO si = { sizeof(STARTUPINFO) };
// AnsiString::c_str() 回傳 const char*,需複製到可寫緩衝區
std::vector buf(cmdLine.Length() + 1);
strcpy(buf.data(), cmdLine.c_str());
if (!CreateProcess(NULL, buf.data(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
throw Exception("CreateProcess 失敗,錯誤碼: " + IntToStr(GetLastError()));
CloseHandle(pi.hThread);
WaitForSingleObject(pi.hProcess, INFINITE);
DWORD exitCode = 0;
GetExitCodeProcess(pi.hProcess, &exitCode);
CloseHandle(pi.hProcess);
return exitCode;
}
lpApplicationName 為 NULL 時,系統從 lpCommandLine 首個 token 解析執行檔路徑。**路徑含空白必須雙引號括住**:"C:\Program Files\App\app.exe" arg1 arg2。lpApplicationName**,lpCommandLine 僅放參數 (或重複完整命令列)。CreateProcessW 支援長路徑 (\\?\ 前綴) 與完整 Unicode 參數。bInheritHandles=TRUE 時,**所有**標記為可繼承的控制代碼 (建立時 SECURITY_ATTRIBUTES.bInheritHandle=TRUE) 都會被子進程繼承。FALSE。需重導向標準 I/O 時,僅對管道控制代碼設定可繼承,並設定 STARTUPINFO.dwFlags |= STARTF_USESTDHANDLES。lpEnvironment=NULL 繼承父進程環境 (複製一份)。NAME=VALUE\0NAME2=VALUE2\0\0 字串陣列。Unicode 版需 WCHAR 陣列。GetEnvironmentStrings → 修改 → CreateProcess(..., newEnv, ...) → FreeEnvironmentStrings。| 旗標 | 用途 |
|---|---|
| CREATE_SUSPENDED | 主執行緒暫停建立,需 ResumeThread 繼續 (注入 DLL、記憶體修改常用) |
| DETACHED_PROCESS | 無主控台視窗 (適用服務/背景工作者) |
| CREATE_NEW_CONSOLE | 強制建立新主控台視窗 |
| CREATE_UNICODE_ENVIRONMENT | 環境區塊為 Unicode (搭配 CreateProcessW) |
| DEBUG_PROCESS / DEBUG_ONLY_THIS_PROCESS | 以除錯器身分建立 |
| EXTENDED_STARTUPINFO_PRESENT | 使用 STARTUPINFOEX (支援屬性清單: 親進程、CPU 群組、Mitigation 政策) |
不等待結束,改用 WaitForSingleObject / WaitForMultipleObjects / RegisterWaitForSingleObject (執行緒池回呼) / CreateIoCompletionPort 監控進程句柄。結束後務必 GetExitCodeProcess + CloseHandle。
SECURITY_ATTRIBUTES 指定自訂 DACL,限制誰可操作子進程物件。SetTokenInformation + CreateProcessAsUser / CreateProcessWithTokenEx。GetLastError() 常見碼:2 (檔案不存在/路徑錯誤)、5 (存取被拒/權限不足)、87 (參數錯誤/命令列格式)、193 (非有效 Win32 應用程式/位元數不符)、206 (命令列過長 > 32768 字元)。DebugView / Process Monitor (ProcMon) 過濾 CreateProcess / Process Start 事件,觀察實際命令列、環境、當前目錄、完整性等級。STARTUPINFO.cb 是否正確設定 (最常見新手錯誤)。CreateProcess 雖參數繁多,但掌握「命令列格式、安全屬性繼承、環境區塊、啟動資訊、旗標組合」五大核心面向,並遵循「最小權限、明確指定路徑、資源即釋放」原則,即能穩健建立、控制、監控子進程。本文提供的封裝函式可直接整合至 C++ Builder、Visual C++、標準 C++17 專案,作為進程管理基礎設施。