Проблема с I/O в процессе или трубе(pipe)

236
20 марта 2019, 22:00

Задача - выполнить последовательность команд в дочерней консоли Windows и записать их вывод в родительскую консоль. Алгоритм программы, если я не ошибаюсь, таков:

  1. Записать команду в input pipe, output которой связан с input дочернего процесса в котором открыта консоль
  2. Из output процесса записать уже результат вывода команды в input второго pipe
  3. Из output второго pipe взять данные и вывести их в консоль родительского процесса

Код который должен это выполнить:

include windows.h include stdio.h include strsafe.h define BUFSIZE 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; HANDLE g_hInputFile = NULL; void CreateChildProcess(void); void WriteToPipe(CHAR *); void ReadFromPipe(void); void ErrorExit(LPCWSTR); 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)) ErrorExit(L"StdoutRd CreatePipe"); // Ensure the read handle to the pipe for STDOUT is not inherited. if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)) ErrorExit(L"Stdout SetHandleInformation"); // Create a pipe for the child process's STDIN. if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0)) ErrorExit(L"Stdin CreatePipe"); // Ensure the write handle to the pipe for STDIN is not inherited. if (!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0)) ErrorExit(L"Stdin SetHandleInformation"); // Create the child process. CreateChildProcess(); // Write to the pipe that is the standard input for a child process. // Data is written to the pipe's buffers, so it is not necessary to wait // until the child process is running before writing data. WriteToPipe((CHAR *)"cd /"); WriteToPipe((CHAR *)"dir"); // Read from pipe that is the standard output for child process. printf("\n->Contents of child process STDOUT:\n\n"); ReadFromPipe(); printf("\n->End of parent execution.\n"); // The remaining open handles are cleaned up when this process terminates. // To avoid resource leaks in a larger application, close handles explicitly. return 0; } void CreateChildProcess() { WCHAR szCmdline[] = L"cmd"; PROCESS_INFORMATION piProcInfo; STARTUPINFO siStartInfo; 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) ErrorExit(L"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 WriteToPipe(CHAR *chBuf) // Read from a file and write its contents to the pipe for the child's STDIN. // Stop when there is no more data. { DWORD dwRead, dwWritten; BOOL bSuccess = FALSE; bSuccess = WriteFile(g_hChildStd_IN_Wr, chBuf, sizeof(chBuf), &dwWritten, NULL); } 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); ReadFile(g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL); WriteFile(hParentStdOut, chBuf, dwRead, &dwWritten, NULL); } void ErrorExit(LPCWSTR lpszFunction) // Format a readable error message, display a message box, // and exit from the application. { LPVOID lpMsgBuf; LPVOID lpDisplayBuf; DWORD dw = GetLastError(); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&lpMsgBuf, 0, NULL); lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, (lstrlen((LPCWSTR)lpMsgBuf) + lstrlen(lpszFunction) + 40) * sizeof(TCHAR)); StringCchPrintf((LPWSTR)lpDisplayBuf, LocalSize(lpDisplayBuf) / sizeof(TCHAR), L"%s failed with error %d: %s", lpszFunction, dw, lpMsgBuf); MessageBox(NULL, (LPCWSTR)lpDisplayBuf, L"Error", MB_OK); LocalFree(lpMsgBuf); LocalFree(lpDisplayBuf); ExitProcess(1); }

Проблема заключается в том что программа выводит в родительскую консоль текст аналогичный команде "cmd", могу предположить что WriteFile() не записывает команду, но вроде как результат выполнения этой функции положительный Не пойму в чем дело, помогите)

Answer 1

Сейчас наконец-то разобрался, вот рабочий код который делает все как мне надо:

#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <strsafe.h>
#define BUFSIZE 4096 
CONST HANDLE TMutex = CreateMutex(NULL, FALSE, NULL); // Мут для потоков
HANDLE g_hChildStd_IN_Rd = NULL;
HANDLE g_hChildStd_IN_Wr = NULL;
HANDLE g_hChildStd_OUT_Rd = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;
void CreateChildProcess(void);
void WriteToPipe(char *);
DWORD WINAPI ReadFromPipe(CONST LPVOID);
void ErrorExit(const wchar_t *);
int stmain(void);
int _tmain()
{
    stmain();
    //HANDLE shellProcess = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&stmain, TMutex, 0, NULL);
    CONST HANDLE TMutex = CreateMutex(NULL, FALSE, NULL); // Мут для потоков
    HANDLE shellThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&ReadFromPipe, TMutex, 0, NULL);
    WriteToPipe((char *)"cd / d E:/ \r\ndir\r\ncd / \r\n");
    WriteToPipe((char *)"dir \r\n");
    system("pause");
    return 0;
}
int stmain(){
    SECURITY_ATTRIBUTES saAttr;
    printf("->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))
        ErrorExit(TEXT("StdoutRd CreatePipe"));
    // Ensure the read handle to the pipe for STDOUT is not inherited.
    if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0))
        ErrorExit(TEXT("Stdout SetHandleInformation"));
    // Create a pipe for the child process's STDIN. 
    if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0))
        ErrorExit(TEXT("Stdin CreatePipe"));
    // Ensure the write handle to the pipe for STDIN is not inherited. 
    if (!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0))
        ErrorExit(TEXT("Stdin SetHandleInformation"));
    // Create the child process. 
    CreateChildProcess();

    // Write to the pipe that is the standard input for a child process. 
    // Data is written to the pipe's buffers, so it is not necessary to wait
    // until the child process is running before writing data.

    return 0;
}
void CreateChildProcess()
// Create a child process that uses the previously created pipes for STDIN and STDOUT.
{
    TCHAR szCmdline[] = TEXT("ConsoleApplication1.exe");
    PROCESS_INFORMATION piProcInfo;
    STARTUPINFO siStartInfo;
    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)
        ErrorExit(TEXT("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 WriteToPipe(char *chBuf)
// Read from a file and write its contents to the pipe for the child's STDIN.
// Stop when there is no more data. 
{
    DWORD dwRead, dwWritten;
    BOOL bSuccess = FALSE;
    dwRead = lstrlenA(chBuf);
    bSuccess = WriteFile(g_hChildStd_IN_Wr, chBuf, dwRead, &dwWritten, NULL);
    // Close the pipe handle so the child process stops reading. 
    //if (!CloseHandle(g_hChildStd_IN_Wr))
    //  ErrorExit(TEXT("StdInWr CloseHandle"));
}
DWORD WINAPI ReadFromPipe(CONST LPVOID lpParam) {
    CONST HANDLE sMutex = (CONST HANDLE)lpParam;
    WaitForSingleObject(sMutex, INFINITE);
    DWORD dwRead, dwWritten;
    CHAR chBuf[BUFSIZE];
    BOOL bSuccess = FALSE;
    HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
    for (;;)
    {
        bSuccess = ReadFile(g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
        if (!bSuccess || dwRead == 0) break;
        bSuccess = WriteFile(hParentStdOut, chBuf,
            dwRead, &dwWritten, NULL);
        if (!bSuccess) break;
    }
    return 0;
}
void ErrorExit(const wchar_t * lpszFunction)
// Format a readable error message, display a message box, 
// and exit from the application.
{
    LPVOID lpMsgBuf;
    LPVOID lpDisplayBuf;
    DWORD dw = GetLastError();
    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER |
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        dw,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR)&lpMsgBuf,
        0, NULL);
    lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
        (lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR));
    StringCchPrintf((LPTSTR)lpDisplayBuf,
        LocalSize(lpDisplayBuf) / sizeof(TCHAR),
        TEXT("%s failed with error %d: %s"),
        lpszFunction, dw, lpMsgBuf);
    MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
    LocalFree(lpMsgBuf);
    LocalFree(lpDisplayBuf);
    ExitProcess(1);
}

ConsoleApplication1.exe - ексешник собраный из другого проекта, он будет дочерним процессом. Вот его код:

#include <windows.h>
#include <stdio.h>
#define BUFSIZE 4096 
int main(void)
{
    CHAR chBuf[BUFSIZE];
    DWORD dwRead, dwWritten;
    HANDLE hStdin, hStdout;
    BOOL bSuccess;
    hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
    hStdin = GetStdHandle(STD_INPUT_HANDLE);
    if (
        (hStdout == INVALID_HANDLE_VALUE) ||
        (hStdin == INVALID_HANDLE_VALUE)
        )
        ExitProcess(1);
    // Send something to this process's stdout using printf.
    // This simple algorithm uses the existence of the pipes to control execution.
    // It relies on the pipe buffers to ensure that no data is lost.
    // Larger applications would use more advanced process control.
    system("cmd");
    for (;;)
    {
        // Read from standard input and stop on error or no data.
        bSuccess = ReadFile(hStdin, chBuf, BUFSIZE, &dwRead, NULL);
        if (!bSuccess || dwRead == 0)
            break;
        system(chBuf);
        // Write to standard output and stop on error.
        bSuccess = WriteFile(hStdout, chBuf, dwRead, &dwWritten, NULL);
        if (!bSuccess)
            break;
    }
    return 0;
}
READ ALSO
Объясните пожалуйста код [закрыт]

Объясните пожалуйста код [закрыт]

Я немного не понимаю данный код, а точнее кусок кода, связанный с массивом и циклом for

145
Как вставить строку в строку после определенного символа?

Как вставить строку в строку после определенного символа?

Необходимо вставить строку в другую строку, но после определенного символаБез использования String и других библиотек

224
Динамическая смена ui файлов в Qt

Динамическая смена ui файлов в Qt

Изучаю Qt, наткнулся на такую проблемуМне требуется быстро менять ui файлы по ходу выполнения программы

181
Скалярное произведение чисел (С++)

Скалярное произведение чисел (С++)

Есть код скалярного произведения:

145