【发布时间】:2019-10-02 18:21:33
【问题描述】:
我编写了一个程序,它实现了一个小外壳来处理来自用户的命令。 如果输入的命令被识别为内部命令,我的程序就会执行这个命令。
这些命令是作为内部函数实现的,它们的输出正在由另一个内部函数处理,该函数能够将文本发送到控制台和/或文件以进行日志记录。
如果输入的命令未被识别,我尝试将输入的命令作为 windows 命令 shell 的一部分执行,例如:cmd dir 将执行dir 命令,输出将打印在控制台上。这是通过CreateProcess 完成的。到目前为止,我没有指定STARTUPINFO 参数的成员hStdError、hStdOutput 和hStdInput。
我尝试实现和改编Creating a Child Process with Redirected Input and Output的例子。
我没有使用他们的子进程实现,而是尝试将 dir 命令的输出获取到我的应用程序中:
#include "pch.h"
#include <windows.h>
#define BUFSIZE 512
HANDLE g_hChildStd_IN_Rd = NULL;
HANDLE g_hChildStd_IN_Wr = NULL;
HANDLE g_hChildStd_OUT_Rd = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
void CreateChildProcess()
// Create a child process that uses the previously created pipes for STDIN and STDOUT.
{
TCHAR szCmdline[] = TEXT("cmd /c dir q:\\Sicherung\\Bilder /s");
BOOL bSuccess = FALSE;
// Set up members of the PROCESS_INFORMATION structure.
ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
// 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.hStdError = g_hChildStd_OUT_Wr;
siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
siStartInfo.hStdInput = g_hChildStd_IN_Rd;
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
// If an error occurs, exit the application.
if (!bSuccess)
return; // ErrorExit(("CreateProcess"));
else
{
// Close handles to the child process and its primary thread.
// Some applications might keep these handles to monitor the status
// of the child process, for example.
//CloseHandle(piProcInfo.hProcess);
//CloseHandle(piProcInfo.hThread);
}
}
void ReadFromPipe(void)
// Read output from the child process's pipe for STDOUT
// and write to the parent process's pipe for STDOUT.
// Stop when there is no more data.
{
DWORD dwRead, dwWritten;
CHAR chBuf[BUFSIZE];
BOOL bSuccess = FALSE;
HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
for (;;)
{
DWORD objectstat = WAIT_TIMEOUT;
//do
//{
// objectstat = WaitForSingleObject(piProcInfo.hProcess, 0);
//} while (objectstat != WAIT_OBJECT_0);
memset(&chBuf[0], 0x00, BUFSIZE);
bSuccess = ReadFile(g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
if (!bSuccess)
break;
bSuccess = WriteFile(hParentStdOut, chBuf,
dwRead, &dwWritten, NULL);
if (!bSuccess)
break;
if (dwRead == 0)
break;
}
}
int main()
{
SECURITY_ATTRIBUTES saAttr;
printf("\n->Start of parent execution.\n");
// 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.
if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0))
return -1;// ErrorExit("StdoutRd CreatePipe");
// Ensure the read handle to the pipe for STDOUT is not inherited.
if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0))
return -2;// ErrorExit(("Stdout SetHandleInformation"));
// Create a pipe for the child process's STDIN.
if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0))
return -3 ;// ErrorExit(("Stdin CreatePipe"));
// Ensure the write handle to the pipe for STDIN is not inherited.
if (!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0))
return -4;// ErrorExit(("Stdin SetHandleInformation"));
// Create the child process.
CreateChildProcess();
ReadFromPipe();
CloseHandle(piProcInfo.hProcess);
CloseHandle(piProcInfo.hThread);
return 0;
}
我知道,问题出在 ReadFile 上。我无法确定 dir 命令的所有输出何时都已处理完毕。检查dwRead 是否为0 或BUFSIZE 不起作用。 dwReadnever 变为 0,也有可能小于 BUFSIZE,因为 dir 命令不够快。
那么,我应该如何结束管道数据的处理?
【问题讨论】:
-
这显然是 C 的代码,
#include <iostream>除外(未使用其提供的功能)。您实际使用的是哪种语言? -
我要指出,在 Windows 中枚举目录内容的惯用方法有很多,即
FindFirstFileW、FindNextFileW和FindClose。您可以只记下当前目录以及用户键入dir并以这种方式处理内容的事实。 -
你说
dwRead永远不会变成0。如果是这样,dir命令停止输出后你会得到什么? -
知道输出何时完成的唯一方法是检测生成输出的程序何时完成,即退出。
-
@JimRhodes:我可以将我的程序的输出与命令行本身发出的 dir 命令的输出进行比较。当我的程序读取了管道的所有字符(或字节)时,它只是在函数 ReadFile() 中阻塞。
标签: c windows redirect readfile