You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MSVC下调用带EXTENDED_STARTUPINFO_PRESENT的CreateProcessW返回错误码87

解决MSVC下CreateProcessW带EXTENDED_STARTUPINFO_PRESENT时的错误码87问题

错误根源分析

错误码87(ERROR_INVALID_PARAMETER)来自以下几个关键参数错误:

  1. STARTUPINFOEXW的cb成员设置时机错误:必须在初始化结构体后立即设置startup_info.StartupInfo.cb = sizeof(STARTUPINFOEXW),否则CreateProcessW会认为结构体大小不合法。
  2. 继承句柄列表包含无效项:你已经通过SetHandleInformation取消了m_hChildStd_OUT_Rd的继承属性,却仍将它加入PROC_THREAD_ATTRIBUTE_HANDLE_LIST,导致参数冲突。该读句柄不需要被子进程继承,应从列表中移除。
  3. bInheritHandles参数值错误:使用PROC_THREAD_ATTRIBUTE_HANDLE_LIST时,此参数必须设为TRUE,否则子进程无法继承指定的句柄。
  4. 命令行参数传递不安全:直接强制转换std::wstring::c_str()得到的const wchar_t*传入CreateProcessW会触发未定义行为,因为函数会修改传入的命令行缓冲区。
  5. 资源未正确清理:虽然不是直接引发错误的原因,但管道句柄、进程/线程句柄和属性列表需要在结束时释放,避免资源泄漏。

修复后的完整代码

#include <windows.h> 
#include <processthreadsapi.h>
#include <WinBase.h>
#include <iostream>
#include <vector>
using namespace std;

void TestFunctionW()
{
    STARTUPINFOEXW startup_info = { 0 };
    PROCESS_INFORMATION process_info = { 0 };

    // 必须先设置cb成员,这是使用STARTUPINFOEX的核心要求
    startup_info.StartupInfo.cb = sizeof(STARTUPINFOEXW);

    SECURITY_ATTRIBUTES saAttr;
    saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
    saAttr.bInheritHandle = TRUE;
    saAttr.lpSecurityDescriptor = NULL;

    HANDLE m_hChildStd_OUT_Rd = NULL;
    HANDLE m_hChildStd_OUT_Wr = NULL;

    if (!CreatePipe(&m_hChildStd_OUT_Rd, &m_hChildStd_OUT_Wr, &saAttr, 0)) {
        cerr << "CreatePipe Failed" << endl;
        goto Cleanup;
    }

    // 取消读句柄的继承属性,无需加入继承列表
    if (!SetHandleInformation(m_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)) {
        cerr << "SetHandleInformation Failed" << endl;
        goto Cleanup;
    }

    // 重定向标准输出和错误输出到管道,标准输入沿用父进程的输入
    startup_info.StartupInfo.hStdError = m_hChildStd_OUT_Wr;
    startup_info.StartupInfo.hStdOutput = m_hChildStd_OUT_Wr;
    startup_info.StartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
    startup_info.StartupInfo.dwFlags |= STARTF_USESTDHANDLES;

    BOOL fSuccess = TRUE;
    SIZE_T size = 0;
    LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList = NULL;

    // 仅将需要继承的管道写句柄加入列表
    int cHandlesToInherit = 1;
    HANDLE rgHandlesToInherit[] = { m_hChildStd_OUT_Wr };

    // 第一次调用获取属性列表所需内存大小
    if (!InitializeProcThreadAttributeList(NULL, 1, 0, &size) && GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
        fSuccess = FALSE;
        cerr << "InitializeProcThreadAttributeList (size query) Failed" << endl;
        goto Cleanup;
    }

    // 分配属性列表内存
    lpAttributeList = reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size));
    if (!lpAttributeList) {
        fSuccess = FALSE;
        cerr << "HeapAlloc Failed" << endl;
        goto Cleanup;
    }

    // 初始化属性列表
    if (!InitializeProcThreadAttributeList(lpAttributeList, 1, 0, &size)) {
        fSuccess = FALSE;
        cerr << "InitializeProcThreadAttributeList Failed" << endl;
        goto Cleanup;
    }

    // 更新属性列表,添加继承句柄
    if (!UpdateProcThreadAttribute(lpAttributeList,
        0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
        rgHandlesToInherit,
        cHandlesToInherit * sizeof(HANDLE), NULL, NULL)) {
        fSuccess = FALSE;
        cerr << "UpdateProcThreadAttribute Failed" << endl;
        goto Cleanup;
    }

    startup_info.lpAttributeList = lpAttributeList;

    cerr << "Handle stuff succeeded.." << endl;

    DWORD flags = EXTENDED_STARTUPINFO_PRESENT;

    // 使用vector创建可修改的命令行缓冲区,避免const指针问题
    vector<wchar_t> cmd_buf(L"ping.exe", L"ping.exe" + 8);
    BOOL ret = CreateProcessW(NULL, cmd_buf.data(), NULL, NULL, TRUE, flags, NULL, NULL, (LPSTARTUPINFOW)&startup_info, &process_info);
    if (!ret) {
        DWORD err = GetLastError();
        cout << "Failed: code " << err << endl;
    } else {
        // 等待子进程完成,读取管道输出
        WaitForSingleObject(process_info.hProcess, INFINITE);

        char buffer[4096];
        DWORD bytesRead;
        while (ReadFile(m_hChildStd_OUT_Rd, buffer, sizeof(buffer)-1, &bytesRead, NULL) && bytesRead > 0) {
            buffer[bytesRead] = '\0';
            cout << buffer;
        }
    }

Cleanup:
    // 释放所有资源
    if (lpAttributeList) {
        DeleteProcThreadAttributeList(lpAttributeList);
        HeapFree(GetProcessHeap(), 0, lpAttributeList);
    }
    if (m_hChildStd_OUT_Rd) CloseHandle(m_hChildStd_OUT_Rd);
    if (m_hChildStd_OUT_Wr) CloseHandle(m_hChildStd_OUT_Wr);
    if (process_info.hProcess) CloseHandle(process_info.hProcess);
    if (process_info.hThread) CloseHandle(process_info.hThread);
}

int main(int argc, char* argv[])
{
    TestFunctionW();
    return 0;
}

关键修复说明

  • 提前设置cb成员:确保CreateProcessW正确识别扩展启动信息结构体,这是使用EXTENDED_STARTUPINFO_PRESENT的必要前提。
  • 精简继承句柄列表:只保留需要被子进程继承的管道写句柄,避免无效句柄导致的参数错误。
  • 修正bInheritHandles参数:设为TRUE以允许子进程继承属性列表中指定的句柄。
  • 安全传递命令行:使用std::vector<wchar_t>创建可修改的缓冲区,符合CreateProcessW对命令行参数的要求。
  • 完善资源清理:通过goto Cleanup统一处理资源释放,避免内存和句柄泄漏。
  • 修复main函数参数:将错误的char* argv修正为标准的char* argv[]。

内容的提问来源于stack exchange,提问作者Derek Magee

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 07:57:33