带有 EXTENDED_STARTUPINFO_PRESENT 标志的 CreateProcessW() 返回错误代码 87?

Der*_*gee 1 c++ winapi visual-c++

我正在努力让 msvc 下的 CreateProcessW 使用 EXTENDED_STARTUPINFO_PRESENT 选项。我创建了一个最小的示例,它调用 ping.exe 并将标准输出重定向到句柄。如果没有 EXTENDED_STARTUPINFO_PRESENT 选项,这也可以正常工作(但不是线程安全的),因此尝试将句柄显式传递给子进程,如下面的最小示例所示。

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


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

    SECURITY_ATTRIBUTES saAttr;

    // Set the bInheritHandle flag so pipe handles are inherited. 
    saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
    saAttr.bInheritHandle = TRUE;
    saAttr.lpSecurityDescriptor = NULL;

    HANDLE          m_hChildStd_OUT_Rd;
    HANDLE          m_hChildStd_OUT_Wr;

    // Create a pipe for the child process's STDOUT. (m_hChildStd_OUT_Wr->m_hChildStd_OUT_Rd)
    if (!CreatePipe(&m_hChildStd_OUT_Rd, &m_hChildStd_OUT_Wr, &saAttr, 0)) {
        std::cerr << "CreatePipe Failed" << endl;
    }

    // Ensure the read handle to the pipe for STDOUT is not inherited.
    if (!SetHandleInformation(m_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)){
        std::cerr << "SetHandleInformation Failed" << endl;
    }


    startup_info.StartupInfo.hStdError = NULL;
    startup_info.StartupInfo.hStdOutput = m_hChildStd_OUT_Wr;
    startup_info.StartupInfo.hStdInput = NULL;
    startup_info.StartupInfo.dwFlags |= STARTF_USESTDHANDLES;


    // Newstuff relating to handle lists
    BOOL fSuccess = true;
    BOOL fInitialized = FALSE;
    SIZE_T size = 0;
    LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList = NULL;

    int cHandlesToInherit = 2;
    HANDLE rgHandlesToInherit[] = { m_hChildStd_OUT_Wr, m_hChildStd_OUT_Rd };

    if (fSuccess) {
        fSuccess = InitializeProcThreadAttributeList(NULL, 1, 0, &size) || GetLastError() == ERROR_INSUFFICIENT_BUFFER;
    }
    if (fSuccess) {
        lpAttributeList = reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(HeapAlloc(GetProcessHeap(), 0, size));
        fSuccess = lpAttributeList != NULL;
    }
    if (fSuccess) {
        fSuccess = InitializeProcThreadAttributeList(lpAttributeList, 1, 0, &size);
    }
    if (fSuccess) {
        fInitialized = TRUE;
        fSuccess = UpdateProcThreadAttribute(lpAttributeList,
            0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
            rgHandlesToInherit,
            cHandlesToInherit * sizeof(HANDLE), NULL, NULL);
    }

    if (!fSuccess) {
        cerr << "Handle list stuff failed" << endl;
        exit(-1);
    }

    startup_info.lpAttributeList = lpAttributeList;

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


    startup_info.StartupInfo.cb = sizeof(startup_info);

    DWORD flags = EXTENDED_STARTUPINFO_PRESENT;
    //DWORD flags = 0;

    std::wstring cmd(L"ping.exe");
    BOOL ret = CreateProcessW(NULL, (LPWSTR)cmd.c_str(), NULL, NULL, FALSE, flags, NULL, NULL, (LPSTARTUPINFOW)&startup_info, &process_info);
    if (!ret)
    {
        DWORD err = GetLastError();
        std::cout << "Failed: code " << err << std::endl;
    }
}

int main(int argc, char* argv)
{
    TestFunctionW();
}
Run Code Online (Sandbox Code Playgroud)

错误代码 87 似乎意味着参数无效。

我在 MSVC 2017 下编译了该示例。没有选项 DWORD flags = EXTENDED_STARTUPINFO_PRESENT; 它工作正常(如预期),使用该选项会失败并显示错误代码 87(不是预期的)。

Ben*_*igt 6

根据UpdateProcThreadAttribute文档,使用时必须启用句柄继承PROC_THREAD_ATTRIBUTE_HANDLE_LIST

请注意, 如果使用此属性,请传入函数参数TRUE的值。bInheritHandlesCreateProcess

这是您要传递的第五个参数FALSE。因此,句柄不会被继承,并且在子进程中句柄无效 -> 错误 87。


问题#2:您传递的句柄数量超过了必要的数量,其中包括无法继承的句柄:

// Ensure the read handle to the pipe for STDOUT is not inherited.
SetHandleInformation(m_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0);
HANDLE rgHandlesToInherit[] = { m_hChildStd_OUT_Wr, m_hChildStd_OUT_Rd };
Run Code Online (Sandbox Code Playgroud)

m_hChildStd_OUT_Rd仅父级需要,并且您已将其标记为禁止继承。所以不要将其放入PROC_THREAD_ATTRIBUTE_HANDLE_LIST.