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

CreateProcess 深度解析:從參數構建到進程同步的最佳實踐

     編輯:VC 教學

CreateProcess 核心簽名

BOOL CreateProcess(
    LPCSTR lpApplicationName,        // 模塊名(可為NULL)
    LPSTR lpCommandLine,             // 命令行(可修改緩衝區)
    LPSECURITY_ATTRIBUTES lpProcessAttributes,  // 進程安全屬性
    LPSECURITY_ATTRIBUTES lpThreadAttributes,   // 主線程安全屬性
    BOOL bInheritHandles,            // 句柄繼承
    DWORD dwCreationFlags,           // 創建標誌
    LPVOID lpEnvironment,            // 環境塊
    LPCSTR lpCurrentDirectory,       // 工作目錄
    LPSTARTUPINFO lpStartupInfo,     // 啟動信息
    LPPROCESS_INFORMATION lpProcessInformation  // 返回信息
);

關鍵參數詳解

1. lpApplicationName vs lpCommandLine

  • lpApplicationName 指定:lpCommandLine 僅作為參數傳遞(argv[1...]),第一個參數通常重複程序名
  • lpApplicationName = NULL:lpCommandLine 第一個 token 解析為模塊名(支持帶空格路徑需加引號)
  • 最佳實踐:同時指定兩者,lpApplicationName 為完整路徑,lpCommandLine 為完整命令行(含程序名),避免路徑解析歧義

2. 安全屬性與句柄繼承

SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; // 允許繼承
CreateProcess(..., &sa, &sa, TRUE, ...); // bInheritHandles=TRUE

子進程將繼承父進程所有可繼承句柄(文件、管道、Socket等)。

3. 創建標誌 dwCreationFlags

標誌用途
CREATE_NEW_CONSOLE分配新控制台窗口
CREATE_NO_WINDOW無窗口(GUI程序隱藏控制台)
DETACHED_PROCESS無控制台、無窗口(服務型)
CREATE_SUSPENDED主線程掛起創建,需 ResumeThread
CREATE_UNICODE_ENVIRONMENT環境塊為 Unicode
DEBUG_PROCESS以調試器身份創建
EXTENDED_STARTUPINFO_PRESENT使用 STARTUPINFOEX

4. STARTUPINFO 精細控制

STARTUPINFO si = { sizeof(STARTUPINFO) };
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.wShowWindow = SW_HIDE;           // 隱藏窗口
si.hStdInput = hChildStdinRead;     // 重定向標準輸入
si.hStdOutput = hChildStdoutWrite;  // 重定向標準輸出
si.hStdError = hChildStderrWrite;   // 重定向標準錯誤

完整同步等待模式

bool LaunchAndWait(const char* cmdLine, DWORD timeoutMs = INFINITE) {
    STARTUPINFO si = { sizeof(STARTUPINFO) };
    PROCESS_INFORMATION pi = {0};

    // 安全屬性:不繼承句柄
    SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, FALSE };

    BOOL success = CreateProcess(
        NULL,                   // 模塊名從命令行解析
        (LPSTR)cmdLine,         // 命令行(需可修改緩衝區!)
        &sa, &sa,               // 進程/線程安全屬性
        FALSE,                  // 不繼承句柄
        CREATE_NO_WINDOW,       // 無控制台窗口
        NULL,                   // 繼承環境
        NULL,                   // 繼承工作目錄
        &si, &pi                // 輸入/輸出
    );

    if (!success) return false;

    // 關閉不需要的句柄
    CloseHandle(pi.hThread);

    // 等待進程退出
    DWORD waitResult = WaitForSingleObject(pi.hProcess, timeoutMs);
    DWORD exitCode = 0;
    GetExitCodeProcess(pi.hProcess, &exitCode);
    CloseHandle(pi.hProcess);

    return waitResult == WAIT_OBJECT_0;
}

高級場景:匿名管道雙向通信

// 1. 創建管道
HANDLE hChildStdinRead, hChildStdinWrite;
HANDLE hChildStdoutRead, hChildStdoutWrite;
SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };

CreatePipe(&hChildStdinRead, &hChildStdinWrite, &sa, 0);
CreatePipe(&hChildStdoutRead, &hChildStdoutWrite, &sa, 0);

// 2. 設置父進程不繼承寫端/讀端
SetHandleInformation(hChildStdinWrite, HANDLE_FLAG_INHERIT, 0);
SetHandleInformation(hChildStdoutRead, HANDLE_FLAG_INHERIT, 0);

// 3. 配置 STARTUPINFO
STARTUPINFO si = { sizeof(STARTUPINFO) };
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.hStdInput = hChildStdinRead;
si.hStdOutput = hChildStdoutWrite;
si.hStdError = hChildStdoutWrite;
si.wShowWindow = SW_HIDE;

// 4. 創建進程
CreateProcess(NULL, cmdLine, &sa, &sa, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi);

// 5. 父進程通信
WriteFile(hChildStdinWrite, inputData, len, &written, NULL);
ReadFile(hChildStdoutRead, buffer, size, &read, NULL);

常見陷阱與避坑指南

  1. 命令行緩衝區不可為常量字符串:lpCommandLine 可能被修改,必須傳遞可寫緩衝區(strdup 或 std::vector)
  2. 路徑含空格必須加引號:"C:\\Program Files\\App\\app.exe" arg1 arg2
  3. 句柄泄漏:CreateProcess 成功時必須關閉 pi.hProcess 和 pi.hThread
  4. 工作目錄陷阱:lpCurrentDirectory=NULL 繼承父進程目錄,相對路徑解析基於此目錄
  5. 環境塊格式:lpEnvironment 為 Key=Value\0Key=Value\0\0 雙重空終止,Unicode 標誌下為寬字符
  6. APC 注入風險:CREATE_SUSPENDED + QueueUserAPC 可實現代碼注入,防護需監控此標誌組合

C# 封裝最佳實踐

public static class ProcessLauncher {
    public static ProcessResult Run(string fileName, string arguments, 
        string workingDir = null, int timeoutMs = -1, 
        Dictionary envVars = null) {
        
        var psi = new ProcessStartInfo(fileName, arguments) {
            UseShellExecute = false,
            CreateNoWindow = true,
            RedirectStandardOutput = true,
            RedirectStandardError = true,
            RedirectStandardInput = true,
            WorkingDirectory = workingDir ?? Environment.CurrentDirectory
        };
        
        if (envVars != null) {
            foreach (var kv in envVars) psi.EnvironmentVariables[kv.Key] = kv.Value;
        }

        using (var proc = Process.Start(psi)) {
            var output = proc.StandardOutput.ReadToEnd();
            var error = proc.StandardError.ReadToEnd();
            
            bool exited = proc.WaitForExit(timeoutMs);
            return new ProcessResult {
                ExitCode = proc.ExitCode,
                Output = output,
                Error = error,
                TimedOut = !exited
            };
        }
    }
}

掌握 CreateProcess 的每個參數含義與組合邏輯,是 Windows 系統編程、沙箱構建、自動化測試框架開發的核心技能。

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