Как мне обработать вывод «dir»? - PullRequest
0 голосов
/ 02 октября 2019

Я написал программу, которая реализует крошечную оболочку для обработки команд от пользователя. Если введенная команда распознается как внутренняя команда, моя программа выполняет эту команду.

Эти команды реализованы как внутренние функции, и их вывод обрабатывается другой внутренней функцией, которая может отправлять текст на консоль и/ или в файл для целей ведения журнала.

Если введенная команда не распознана, я пытаюсь выполнить введенную команду как часть командной оболочки Windows, например: cmd dir выполнит команду dir и выходные данные будут напечатаны на консоли. Это делается через CreateProcess. До сих пор я не указывал члены hStdError, hStdOutput и hStdInput параметра STARTUPINFO.

Я попытался реализовать и адаптировать пример Создание дочернего процесса с перенаправленным вводом и выводом .

Я не использовал их реализацию дочернего процесса, но попыталсячтобы получить вывод команды 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 не работает. dwRead никогда не становится 0, и может случиться так, что оно меньше BUFSIZE, потому что команда dir не достаточно быстра.

Итак, как мне закончить обработку данных канала?

1 Ответ

0 голосов
/ 02 октября 2019

Хорошо, после того, как я искал несколько разных терминов в Google, я пришел с этой ссылкой на stackoverflow;): Как прочитать вывод из cmd.exe, используя CreateProcess () и 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 работает как положено.

...