我正在努力让 CreateProcessW 在 msvc 下使用 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();
}
错误代码 87 似乎意味着参数无效。
我在 MSVC 2017 下编译了该示例。没有选项 DWORD flags = EXTENDED_STARTUPINFO_PRESENT;它工作正常(如预期),使用该选项会失败并显示错误代码 87(不是预期的)。
使用时必须启用句柄继承
PROC_THREAD_ATTRIBUTE_HANDLE_LIST
注意,如果使用此属性,请为
函数的TRUE
参数传入bInheritHandles
值。CreateProcess
这是第五个参数,您将在其中传递
FALSE
。因此句柄不会被继承,并且在子进程中句柄无效 -> 错误 87。