【问题标题】:How do I process the output of "dir"?如何处理“dir”的输出?
【发布时间】:2019-10-02 18:21:33
【问题描述】:

我编写了一个程序,它实现了一个小外壳来处理来自用户的命令。 如果输入的命令被识别为内部命令,我的程序就会执行这个命令。

这些命令是作为内部函数实现的,它们的输出正在由另一个内部函数处理,该函数能够将文本发送到控制台和/或文件以进行日志记录。

如果输入的命令未被识别,我尝试将输入的命令作为 windows 命令 shell 的一部分执行,例如:cmd dir 将执行dir 命令,输出将打印在控制台上。这是通过CreateProcess 完成的。到目前为止,我没有指定STARTUPINFO 参数的成员hStdErrorhStdOutputhStdInput

我尝试实现和改编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 &lt;iostream&gt; 除外(未使用其提供的功能)。您实际使用的是哪种语言?
  • 我要指出,在 Windows 中枚举目录内容的惯用方法有很多,即FindFirstFileWFindNextFileWFindClose。您可以只记下当前目录以及用户键入dir 并以这种方式处理内容的事实。
  • 你说dwRead永远不会变成0。如果是这样,dir命令停止输出后你会得到什么?
  • 知道输出何时完成的唯一方法是检测生成输出的程序何时完成,即退出。
  • @JimRhodes:我可以将我的程序的输出与命令行本身发出的 dir 命令的输出进行比较。当我的程序读取了管道的所有字符(或字节)时,它只是在函数 ReadFile() 中阻塞。

标签: c windows redirect readfile


【解决方案1】:

好的,在我在谷歌搜索了一些不同的术语之后,我想出了这个指向 stackoverflow 的链接;): How to read output from cmd.exe using CreateProcess() and CreatePipe()

伊恩·博伊德在那里写道:

一旦您启动了您的子进程:请务必关闭您不再需要的管道末端。

result = CreateProcess(...);



//CreateProcess demands that we close these two populated handles when we're done with them. We're done with them.

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 console app.

   When the app closes, it will close the pipe, and ReadFile will return 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;

大多数解决方案的常见问题是人们试图等待进程句柄。这有很多问题;最主要的是,如果您等待孩子终止,孩子将永远无法终止。

关闭不需要的句柄ReadFile 后按预期工作。

【讨论】:

    猜你喜欢
    • 2018-05-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-22
    • 2012-09-01
    • 1970-01-01
    相关资源
    最近更新 更多