[英]How to read output from cmd.exe using CreateProcess() and CreatePipe()
How to read output from cmd.exe using CreateProcess() and CreatePipe()如何使用 CreateProcess() 和 CreatePipe() 从 cmd.exe 读取输出
I have been trying to create a child process executing cmd.exe
with a command-line designating /K dir
.我一直在尝试使用指定/K dir
的命令行创建一个执行cmd.exe
的子进程。 The purpose is to read the output from the command back into the parent process using pipes.目的是使用管道将命令的输出读回父进程。
I've already got CreateProcess()
working, however the step involving pipes are causing me trouble.我已经让CreateProcess()
工作了,但是涉及管道的步骤给我带来了麻烦。 Using pipes, the new console window is not displaying (like it was before), and the parent process is stuck in the call to ReadFile()
.使用管道,新的控制台窗口不会显示(就像以前一样),并且父进程卡在对ReadFile()
的调用中。
Does anyone have an idea of what I'm doing wrong?有谁知道我做错了什么?
#include <Windows.h>
#include <stdio.h>
#include <tchar.h>
#define BUFFSZ 4096
HANDLE g_hChildStd_IN_Rd = NULL;
HANDLE g_hChildStd_IN_Wr = NULL;
HANDLE g_hChildStd_OUT_Rd = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;
int wmain(int argc, wchar_t* argv[])
{
int result;
wchar_t aCmd[BUFFSZ] = TEXT("/K dir"); // CMD /?
STARTUPINFO si;
PROCESS_INFORMATION pi;
SECURITY_ATTRIBUTES sa;
printf("Starting...\n");
ZeroMemory(&si, sizeof(STARTUPINFO));
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
// Create one-way pipe for child process STDOUT
if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &sa, 0)) {
printf("CreatePipe() error: %ld\n", GetLastError());
}
// Ensure read handle to pipe for STDOUT is not inherited
if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)) {
printf("SetHandleInformation() error: %ld\n", GetLastError());
}
// Create one-way pipe for child process STDIN
if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &sa, 0)) {
printf("CreatePipe() error: %ld\n", GetLastError());
}
// Ensure write handle to pipe for STDIN is not inherited
if (!SetHandleInformation(g_hChildStd_IN_Rd, HANDLE_FLAG_INHERIT, 0)) {
printf("SetHandleInformation() error: %ld\n", GetLastError());
}
si.cb = sizeof(STARTUPINFO);
si.hStdError = g_hChildStd_OUT_Wr;
si.hStdOutput = g_hChildStd_OUT_Wr;
si.hStdInput = g_hChildStd_IN_Rd;
si.dwFlags |= STARTF_USESTDHANDLES;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL;
// Pipe handles are inherited
sa.bInheritHandle = true;
// Creates a child process
result = CreateProcess(
TEXT("C:\\Windows\\System32\\cmd.exe"), // Module
aCmd, // Command-line
NULL, // Process security attributes
NULL, // Primary thread security attributes
true, // Handles are inherited
CREATE_NEW_CONSOLE, // Creation flags
NULL, // Environment (use parent)
NULL, // Current directory (use parent)
&si, // STARTUPINFO pointer
&pi // PROCESS_INFORMATION pointer
);
if (result) {
printf("Child process has been created...\n");
}
else {
printf("Child process could not be created\n");
}
bool bStatus;
CHAR aBuf[BUFFSZ + 1];
DWORD dwRead;
DWORD dwWrite;
// GetStdHandle(STD_OUTPUT_HANDLE)
while (true) {
bStatus = ReadFile(g_hChildStd_OUT_Rd, aBuf, sizeof(aBuf), &dwRead, NULL);
if (!bStatus || dwRead == 0) {
break;
}
aBuf[dwRead] = '\0';
printf("%s\n", aBuf);
}
// Wait until child process exits
WaitForSingleObject(pi.hProcess, INFINITE);
// Close process and thread handles
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
printf("Stopping...\n");
return 0;
}
The subtle way out of your problem is to make sure you close the ends of the pipe you don't need.解决问题的巧妙方法是确保关闭不需要的管道末端。
Your parent process has four handles:你的父进程有四个句柄:
g_hChildStd_IN_Wr
g_hChildStd_OUT_Rd
g_hChildStd_IN_Rd
g_hChildStd_OUT_Wr
╔══════════════════╗ ╔══════════════════╗
║ Parent Process ║ ║ Child Process ║
╠══════════════════╣ ╠══════════════════╣
║ ║ ║ ║
║ g_hChildStd_IN_Wr╟───────────────>║g_hChildStd_IN_Rd ║
║ ║ ║ ║
║g_hChildStd_OUT_Rd║<───────────────╢g_hChildStd_OUT_Wr║
║ ║ ║ ║
╚══════════════════╝ ╚══════════════════╝
Your parent process only needs one end of each pipe:你的父进程只需要每根管子的一端:
g_hChildStd_IN_Wr
子输入管道的可写端: g_hChildStd_IN_Wr
g_hChildStd_OUT_Rd
子输出管道的可读端: g_hChildStd_OUT_Rd
Once you've launched your child process: be sure to close those ends of the pipe you no longer need:启动子进程后:确保关闭不再需要的管道末端:
CloseHandle(g_hChildStd_IN_Rd)
CloseHandle(g_hChildStd_OUT_Wr)
Leaving:离开:
╔══════════════════╗ ╔══════════════════╗
║ Parent Process ║ ║ Child Process ║
╠══════════════════╣ ╠══════════════════╣
║ ║ ║ ║
║ g_hChildStd_IN_Wr╟───────────────>║ ║
║ ║ ║ ║
║g_hChildStd_OUT_Rd║<───────────────╢ ║
║ ║ ║ ║
╚══════════════════╝ ╚══════════════════╝
Or more fully:或者更完整:
STARTUP_INFO si;
PROCESS_INFO pi;
result = CreateProcess(..., ref si, ref pi);
//Bonus chatter: A common bug among a lot of programmers:
// they don't realize they are required to call CloseHandle
// on the two handles placed in PROCESS_INFO.
// That's why you should call ShellExecute - it closes them for you.
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
/*
We've given the console app the writable end of the pipe during CreateProcess; we don't need it anymore.
We do keep the handle for the *readable* end of the pipe; as we still need to read from it.
The other reason to close the writable-end handle now is so that there's only one out-standing reference to the writeable end: held by the child process.
When the child processes closes, it will close the pipe, and
your call to ReadFile will fail with error code:
109 (The pipe has been ended).
That's how we'll know the console app is done. (no need to wait on process handles with buggy infinite waits)
*/
CloseHandle(g_hChildStd_OUT_Wr);
g_hChildStd_OUT_Wr = 0;
CloseHandle(g_hChildStd_IN_Rd);
g_hChildStd_OUT_Wr = 0;
The common problem with most solutions is that people try to wait on a process handle.大多数解决方案的常见问题是人们试图等待进程句柄。
That's wrong.那是错误的。 That's all wrong .那都是错的。
There are many problems with these ideas;这些想法有很多问题; the main one being:主要是:
If the child is trying to send you output through the pipe, and you're INFINITE
waiting, you're not emptying your end of the pipe.如果孩子试图通过管道向您发送输出,而您正在INFINITE
等待,那么您并没有清空管道的末端。 Eventually the pipe the child is writing to becomes full.最终孩子正在写的管道变满。 When the child tries to write to a pipe that is full, its WriteFile
call waits for the pipe to have some room.当子进程尝试写入已满的管道时,它的WriteFile
调用会等待管道有一些空间。
As a result the child process will never terminate;结果子进程永远不会终止; you've deadlocked everything.你让一切陷入僵局。
The correct solution comes by simply reading from the pipe .正确的解决方案来自简单地从管道中读取。
CloseHandle
on its end of the pipes.它将CloseHandle
管子上的尾声。ERROR_BROKEN_PIPE
).您会被告知管道已关闭( ERROR_BROKEN_PIPE
)。String outputText = "";
//Read will return when the buffer is full, or if the pipe on the other end has been broken
while (ReadFile(stdOutRead, aBuf, Length(aBuf), &bytesRead, null)
outputText = outputText + Copy(aBuf, 1, bytesRead);
//ReadFile will either tell us that the pipe has closed, or give us an error
DWORD le = GetLastError;
//And finally cleanup
CloseHandle(g_hChildStd_IN_Wr);
CloseHandle(g_hChildStd_OUT_Rd);
if (le != ERROR_BROKEN_PIPE) //"The pipe has been ended."
RaiseLastOSError(le);
All without a dangerous MsgWaitForSingleObject - which is error-prone, difficult to use correctly, and causes the very bug you want to avoid.所有这些都没有危险的MsgWaitForSingleObject - 它容易出错,难以正确使用,并且会导致您想要避免的错误。
Ian Boyd's answer had this gem: Once you've launched your child process: be sure to close those ends of the pipe you no longer need.伊恩博伊德的回答有一个宝石:一旦你启动了你的子进程:一定要关闭你不再需要的管道末端。
I've produced another version of the CreatePipe
+ CreateProcess
solution which, I hope, is more clear:我已经制作了另一个版本的CreatePipe
+ CreateProcess
解决方案,我希望它更清晰:
int main()
{
BOOL ok = TRUE;
HANDLE hStdInPipeRead = NULL;
HANDLE hStdInPipeWrite = NULL;
HANDLE hStdOutPipeRead = NULL;
HANDLE hStdOutPipeWrite = NULL;
// Create two pipes.
SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
ok = CreatePipe(&hStdInPipeRead, &hStdInPipeWrite, &sa, 0);
if (ok == FALSE) return -1;
ok = CreatePipe(&hStdOutPipeRead, &hStdOutPipeWrite, &sa, 0);
if (ok == FALSE) return -1;
// Create the process.
STARTUPINFO si = { };
si.cb = sizeof(STARTUPINFO);
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdError = hStdOutPipeWrite;
si.hStdOutput = hStdOutPipeWrite;
si.hStdInput = hStdInPipeRead;
PROCESS_INFORMATION pi = { };
LPCWSTR lpApplicationName = L"C:\\Windows\\System32\\cmd.exe";
LPWSTR lpCommandLine = (LPWSTR)L"C:\\Windows\\System32\\cmd.exe /c dir";
LPSECURITY_ATTRIBUTES lpProcessAttributes = NULL;
LPSECURITY_ATTRIBUTES lpThreadAttribute = NULL;
BOOL bInheritHandles = TRUE;
DWORD dwCreationFlags = 0;
LPVOID lpEnvironment = NULL;
LPCWSTR lpCurrentDirectory = NULL;
ok = CreateProcess(
lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttribute,
bInheritHandles,
dwCreationFlags,
lpEnvironment,
lpCurrentDirectory,
&si,
&pi);
if (ok == FALSE) return -1;
// Close pipes we do not need.
CloseHandle(hStdOutPipeWrite);
CloseHandle(hStdInPipeRead);
// The main loop for reading output from the DIR command.
char buf[1024 + 1] = { };
DWORD dwRead = 0;
DWORD dwAvail = 0;
ok = ReadFile(hStdOutPipeRead, buf, 1024, &dwRead, NULL);
while (ok == TRUE)
{
buf[dwRead] = '\0';
OutputDebugStringA(buf);
puts(buf);
ok = ReadFile(hStdOutPipeRead, buf, 1024, &dwRead, NULL);
}
// Clean up and exit.
CloseHandle(hStdOutPipeRead);
CloseHandle(hStdInPipeWrite);
DWORD dwExitCode = 0;
GetExitCodeProcess(pi.hProcess, &dwExitCode);
return dwExitCode;
}
Some notes:一些注意事项:
DIR
command doesn't require user input (but, I left it in the code, since it is a good template for running other commands)这是因为DIR
命令不需要用户输入(但是,我把它留在了代码中,因为它是运行其他命令的好模板)hStdInPipeRead
& hStdInPipeWrite
can be omitted与hStdInPipeRead
和hStdInPipeWrite
都可以省略si.hStdInput
can be omitted设置si.hStdInput
可以省略L"C:\\\\Windows\\\\System32\\\\cmd.exe"
with reading the COMSPEC
environment variable.将硬编码的L"C:\\\\Windows\\\\System32\\\\cmd.exe"
替换为读取COMSPEC
环境变量。cmd.exe /k DIR
with cmd.exe /c DIR
since when the DIR
command finishes we don't really want the cmd.exe
to stick around.将cmd.exe /k DIR
替换为cmd.exe /c DIR
因为当DIR
命令完成时,我们真的不希望cmd.exe
继续存在。I too have same scenario.我也有同样的场景。 in my case from Lib, need to execute internal exe and read output.在我的情况下,来自 Lib,需要执行内部 exe 并读取输出。 The following works without any issues.以下工作没有任何问题。
void executeCMDInNewProcessAndReadOutput(LPSTR lpCommandLine)
{
STARTUPINFO si;
SECURITY_ATTRIBUTES sa;
PROCESS_INFORMATION pi;
HANDLE g_hChildStd_IN_Rd, g_hChildStd_OUT_Wr, g_hChildStd_OUT_Rd, g_hChildStd_IN_Wr; //pipe handles
char buf[1024]; //i/o buffer
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
if (CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &sa, 0)) //create stdin pipe
{
if (CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &sa, 0)) //create stdout pipe
{
//set startupinfo for the spawned process
/*The dwFlags member tells CreateProcess how to make the process.
STARTF_USESTDHANDLES: validates the hStd* members.
STARTF_USESHOWWINDOW: validates the wShowWindow member*/
GetStartupInfo(&si);
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
//set the new handles for the child process
si.hStdOutput = g_hChildStd_OUT_Wr;
si.hStdError = g_hChildStd_OUT_Wr;
si.hStdInput = g_hChildStd_IN_Rd;
//spawn the child process
if (CreateProcess(NULL, lpCommandLine, NULL, NULL, TRUE, CREATE_NEW_CONSOLE,
NULL, NULL, &si, &pi))
{
unsigned long bread; //bytes read
unsigned long avail; //bytes available
memset(buf, 0, sizeof(buf));
for (;;)
{
PeekNamedPipe(g_hChildStd_OUT_Rd, buf, 1023, &bread, &avail, NULL);
//check to see if there is any data to read from stdout
if (bread != 0)
{
if (ReadFile(g_hChildStd_OUT_Rd, buf, 1023, &bread, NULL))
{
break;
}
}
}
//clean up all handles
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
CloseHandle(g_hChildStd_IN_Rd);
CloseHandle(g_hChildStd_OUT_Wr);
CloseHandle(g_hChildStd_OUT_Rd);
CloseHandle(g_hChildStd_IN_Wr);
}
else
{
CloseHandle(g_hChildStd_IN_Rd);
CloseHandle(g_hChildStd_OUT_Wr);
CloseHandle(g_hChildStd_OUT_Rd);
CloseHandle(g_hChildStd_IN_Wr);
}
}
else
{
CloseHandle(g_hChildStd_IN_Rd);
CloseHandle(g_hChildStd_IN_Wr);
}
}
}
Here is an example (taken from a larger program) of a thread that does what you are looking for.这是一个执行您正在寻找的线程的示例(取自一个更大的程序)。 It creates pipes for stdout and stderr for the process it creates then goes into a loop reading those pipes until the program finishes.它为它创建的进程创建 stdout 和 stderr 管道,然后进入循环读取这些管道,直到程序完成。
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
#define EVENT_NAME "Global\\RunnerEvt"
HANDLE hev;
SECURITY_ATTRIBUTES psa;
InitSAPtr(&psa);
DWORD waitRc;
DWORD bytesRead;
int manual_triggered = 1;
hev = CreateEvent(&psa, FALSE, FALSE, EVENT_NAME);
// Create pipes we'll read
for(;;)
{
if (manual_triggered)
{
waitRc = WAIT_OBJECT_0;
manual_triggered = 0;
}
else
{
waitRc = WaitForSingleObject(hev, 500);
}
if (waitRc == WAIT_OBJECT_0)
{
`logprint`f(LOG_DBG, "Received command to run process\n");
CreateChildOutFile();
stdOutEvt = CreateEvent(&psa, TRUE, FALSE, 0);
stdOutOvl.hEvent = stdOutEvt;
stdErrEvt = CreateEvent(&psa, TRUE, FALSE, 0);
stdErrOvl.hEvent = stdErrEvt;
gStdOutReadHand = CreateNamedPipe(STD_OUT_PIPE_NAME, PIPE_ACCESS_DUPLEX + FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE + PIPE_READMODE_BYTE,
PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &psa);
if (gStdOutReadHand == INVALID_HANDLE_VALUE)
{
log(LOG_DBG, "Error %d on create STDOUT pipe\n", GetLastError());
}
gStdErrReadHand = CreateNamedPipe(STD_ERR_PIPE_NAME, PIPE_ACCESS_DUPLEX + FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE + PIPE_READMODE_BYTE,
PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &psa);
if (gStdErrReadHand == INVALID_HANDLE_VALUE)
{
log(LOG_DBG, "Error %d on create STDERR pipe\n", GetLastError());
}
runProcess();
log(LOG_DBG, "After runProcess, new PID is %d/%x\n", piProcInfo.dwProcessId, piProcInfo.dwProcessId);
if (piProcInfo.dwProcessId == 0)
{
log(LOG_DBG, "runProcess failed, closing child STDIN/STDERR\n");
closeChildPipes();
#define FAIL_MSG "Child process failed to start\n"
writeChildOutFile(FAIL_MSG, strlen(FAIL_MSG) );
CloseHandle(hChildOut);
}
else
{
log(LOG_DBG, "Child process created, setting up for redir/restart/termination\n");
issueRead(gStdOutReadHand, &stdOutOvl, stdOutBuff, &stdOutBytesAvail);
//log(LOG_DBG, "After read set on STDOUT\n");
issueRead(gStdErrReadHand, &stdErrOvl, stdErrBuff, &stdErrBytesAvail);
//log(LOG_DBG, "After read set on STDERR\n");
HANDLE harr[4];
for(;;)
{
harr[0] = hev;
harr[1] = piProcInfo.hProcess;
harr[2] = stdOutEvt;
harr[3] = stdErrEvt;
DWORD waitRc2 = WaitForMultipleObjects(4, harr, FALSE, 500);
#if 0
if (waitRc2 == -1)
{
log(LOG_DBG, "Wait error %d\n", GetLastError());
Sleep(500);
}
log(LOG_DBG, "waitRc2 %d\n", waitRc2);
#endif
if ((waitRc2 - WAIT_OBJECT_0) == 0)
{
log(LOG_DBG, "Woke up because another trigger command was received\n");
#define NEW_CMD_MSG "Child process is being terminated because new trigger received\n"
writeChildOutFile(NEW_CMD_MSG, strlen(NEW_CMD_MSG));
terminateChild();
CloseHandle(hChildOut);
manual_triggered = 1;
break;
}
else if ((waitRc2 - WAIT_OBJECT_0) == 1)
{
//log(LOG_DBG, "Woke up because child has terminated\n");
closeChildPipes();
#define NORM_MSG "Normal child process termination\n"
writeChildOutFile(NORM_MSG, strlen(NORM_MSG));
CloseHandle(hChildOut);
break;
}
else if ((waitRc2 - WAIT_OBJECT_0) == 2)
{
//log(LOG_DBG, "Woke up because child has stdout\n");
if (GetOverlappedResult(gStdOutReadHand, &stdOutOvl, &bytesRead, TRUE))
{
writeChildOutFile(stdOutBuff, bytesRead);
ResetEvent(stdOutEvt);
issueRead(gStdOutReadHand, &stdOutOvl, stdOutBuff, &stdOutBytesAvail);
}
}
else if ((waitRc2 - WAIT_OBJECT_0) == 3)
{
//log(LOG_DBG, "Woke up because child has stderr\n");
if (GetOverlappedResult(gStdErrReadHand, &stdErrOvl, &bytesRead, TRUE))
{
writeChildOutFile(stdErrBuff, bytesRead);
ResetEvent(stdErrEvt);
issueRead(gStdErrReadHand, &stdErrOvl, stdErrBuff, &stdErrBytesAvail);
}
}
else
{
if (gShuttingDown)
{
log(LOG_DBG, "Woke with active child and service is terminating\n");
#define SHUTDOWN_MSG "Child process is being terminated because the service is shutting down\n"
writeChildOutFile(SHUTDOWN_MSG, strlen(SHUTDOWN_MSG));
terminateChild();
CloseHandle(hChildOut);
break;
}
}
if (gShuttingDown)
{
break;
}
}
}
}
else if (gShuttingDown)
{
break;
}
CloseHandle(gStdOutReadHand);
CloseHandle(gStdErrReadHand);
}
return 0;
}
void writeChildOutFile(char *msg, int len)
{
DWORD bytesWritten;
WriteFile(hChildOut, msg, len, &bytesWritten, 0);
}
void terminateChild(void)
{
if (piProcInfo.dwProcessId != 0)
{
TerminateProcess(piProcInfo.hProcess, -1);
CloseHandle(piProcInfo.hThread);
CloseHandle(piProcInfo.hProcess);
closeChildPipes();
}
}
void closeChildPipes(void)
{
CloseHandle(g_hChildStd_OUT_Wr);
CloseHandle(g_hChildStd_ERR_Wr);
}
void runProcess(void)
{
SECURITY_ATTRIBUTES saAttr;
// Set the bInheritHandle flag so pipe handles are inherited.
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
// Create a pipe for the child process's STDOUT.
TCHAR szCmdline[]=TEXT("cmd.exe /C C:\\temp\\RunnerService.bat");
STARTUPINFO siStartInfo;
BOOL bSuccess = FALSE;
// Set up members of the PROCESS_INFORMATION structure.
ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
g_hChildStd_OUT_Wr = CreateFile (STD_OUT_PIPE_NAME,
FILE_WRITE_DATA,
0,
&saAttr,
OPEN_EXISTING,
0,
NULL);
if (g_hChildStd_OUT_Wr == INVALID_HANDLE_VALUE)
{
log(LOG_DBG, "Error creating child proc stdout file %d\n", GetLastError());
}
g_hChildStd_ERR_Wr = CreateFile (STD_ERR_PIPE_NAME,
FILE_WRITE_DATA,
0,
&saAttr,
OPEN_EXISTING,
0,
NULL);
if (g_hChildStd_ERR_Wr == INVALID_HANDLE_VALUE)
{
log(LOG_DBG, "Error creating child proc stderr file %d\n", GetLastError());
}
// Set up members of the STARTUPINFO structure.
// This structure specifies the STDIN and STDOUT handles for redirection.
ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
siStartInfo.hStdError = g_hChildStd_ERR_Wr;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
// Create the child process.
bSuccess = CreateProcess(NULL,
szCmdline, // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
0, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo); // receives PROCESS_INFORMATION
}
void CreateChildOutFile(void)
{
SYSTEMTIME st;
SECURITY_ATTRIBUTES sa;
char fName[_MAX_PATH];
InitSAPtr(&sa);
GetLocalTime(&st);
sprintf(fName, "C:\\TEMP\\runsvcchild_%02d_%02d_%02d_%04d.out", st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
hChildOut = CreateFile(fName, GENERIC_WRITE, FILE_SHARE_READ, &sa, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
}
void issueRead(HANDLE hFile, OVERLAPPED *overLapped, char *buf, DWORD *dwRead)
{
//log(LOG_DBG, "Start of issueRead, hfile %08x, ovl is %08x\n", hFile, overLapped);
BOOL brc = ReadFile(hFile, buf, 4096, dwRead, overLapped);
if (!brc)
{
DWORD dwle = GetLastError();
if (dwle != ERROR_IO_PENDING)
{
log(LOG_DBG, "Error %d on ReadFile\n", dwle);
}
}
else
{
// log(LOG_DBG, "Read issued\n");
}
}
I think you did everything right.我认为你做的一切都是正确的。 But cmd.exe prints nothing or very little amount of data after start and your ReadFile blocks.但是 cmd.exe 在启动和您的 ReadFile 块后不打印任何数据或打印很少量的数据。 If you move your cycle如果你移动你的周期
while (true) {
bStatus = ReadFile(g_hChildStd_OUT_Rd, aBuf, sizeof(aBuf), &dwRead, NULL);
if (!bStatus || dwRead == 0) {
break;
}
aBuf[dwRead] = '\0';
printf("%s\n", aBuf);
}
into background thread and run other cycle which will read your input and send it to cmd.exe, I think you can see any effect.进入后台线程并运行其他循环,它将读取您的输入并将其发送到 cmd.exe,我想您可以看到任何效果。 Either you can make read buffer smaller (16 bytes eg).您可以将读取缓冲区变小(例如 16 字节)。
I tried Stephen Quan's answer out and got a segfault.我尝试了 Stephen Quan 的答案并得到了一个段错误。 Perhaps someone with more experience might know why that is.也许有更多经验的人可能知道这是为什么。 At any rate, this should be a more correct example of what he was trying to do:无论如何,这应该是他试图做的更正确的例子:
#include <windows.h>
#include <cstddef>
#include <string>
#include <vector>
#include <cwchar>
using std::string;
using std::wstring;
using std::vector;
using std::size_t;
static inline wstring widen(string str) {
size_t wchar_count = str.size() + 1;
vector<wchar_t> buf(wchar_count);
return wstring{ buf.data(), (size_t)MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, buf.data(), (int)wchar_count) };
}
static inline string narrow(wstring wstr) {
int nbytes = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.length(), NULL, 0, NULL, NULL);
vector<char> buf(nbytes);
return string{ buf.data(), (size_t)WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.length(), buf.data(), nbytes, NULL, NULL) };
}
string evaluate_shell(string command) {
string output;
wstring wstr_command = widen(command);
wchar_t cwstr_command[32768];
wcsncpy(cwstr_command, wstr_command.c_str(), 32768);
BOOL ok = TRUE;
HANDLE hStdInPipeRead = NULL;
HANDLE hStdInPipeWrite = NULL;
HANDLE hStdOutPipeRead = NULL;
HANDLE hStdOutPipeWrite = NULL;
SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
ok = CreatePipe(&hStdInPipeRead, &hStdInPipeWrite, &sa, 0);
if (ok == FALSE) return "";
ok = CreatePipe(&hStdOutPipeRead, &hStdOutPipeWrite, &sa, 0);
if (ok == FALSE) return "";
STARTUPINFOW si = { };
si.cb = sizeof(STARTUPINFOW);
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdError = hStdOutPipeWrite;
si.hStdOutput = hStdOutPipeWrite;
si.hStdInput = hStdInPipeRead;
PROCESS_INFORMATION pi = { };
if (CreateProcessW(NULL, cwstr_command, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) {
while (WaitForSingleObject(pi.hProcess, 5) == WAIT_TIMEOUT) {
MSG msg;
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
CloseHandle(hStdOutPipeWrite);
CloseHandle(hStdInPipeRead);
char buffer[4096] = { };
DWORD dwRead = 0;
ok = ReadFile(hStdOutPipeRead, buffer, 4095, &dwRead, NULL);
while (ok == TRUE) {
buffer[dwRead] = 0;
ok = ReadFile(hStdOutPipeRead, buffer, 4095, &dwRead, NULL);
}
CloseHandle(hStdOutPipeRead);
CloseHandle(hStdInPipeWrite);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
output = narrow(widen(buffer));
while (output.back() == '\r' || output.back() == '\n')
output.pop_back();
}
return output;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.