如何使用CreateProcess()和CreatePipe()从cmd.exe读取输出
我一直在尝试创建一个子进程来执行cmd.exe
使用命令行指定/K dir
..。其目的是使用管道将命令的输出读回到父进程中。
我已经有了CreateProcess()
工作,但是涉及管道的步骤给我带来了麻烦。使用管道,新的控制台窗口不会显示(与以前一样),并且父进程停滞在调用ReadFile()
..。
有人知道我做错了什么吗?
#include
#include
#include
#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;
}
发布于 2019-04-17 08:26:24
伊恩·博伊德的答案是这样的:一旦你启动了你的子进程:确保关闭那些你不再需要的管道的末端。
我制作了另一个版本的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;
}
一些注意事项:
这是因为DIR
命令不需要用户输入(但是,我把它留在了代码中,因为它是运行其他命令的
一个很好的模板)
所有与此相关的事情hStdInPipeRead
&hStdInPipeWrite
可以省略
设置si.hStdInput
可以省略
L"C:\\Windows\\System32\\cmd.exe"
通过阅读COMSPEC
环境变量。cmd.exe /k DIR
使用cmd.exe /c DIR
从什么时候开始DIR
命令结束时,我们并不真正想要cmd.exe
留下来。发布于 2019-02-14 19:31:12
我也有同样的情况。在我使用Lib的情况下,需要执行内部exe并读取输出。下面的代码可以正常工作,没有任何问题。
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);
}
}
}
发布于 2016-03-13 21:08:07
下面是一个线程的例子(取自一个更大的程序),它可以完成你想要做的事情。它为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");
}
}
https://stackoverflow.com/questions/35969730
复制相似问题