Перенаправление ввода-вывода по именованным каналам дочернего процесса (асинхронное)

#c #windows #winapi #named-pipes #systems-programming

Вопрос:

я работаю над проектом, в котором мне нужно запустить внешнюю программу (процесс), отправить сообщения в ее STDIN и прочитать ее STDOUT и STDERR.

Я не могу использовать анонимные каналы, потому что я нахожусь в многопоточной асинхронной среде, я прочитал много документации и примеров MSDN, и у меня есть кое-что, что работает.

Я создал именованный канал и создал несколько дескрипторов с одинаковым именем, каждый для разных потоков, ожидание входящих сообщений от STDOUT и STDERR (дочернего процесса) выполняется в другом потоке.

Я хотел реализовать часть «отправить сообщение», в которой я могу писать в STDIN ребенка, но я не смог заставить ее работать, по крайней мере, я так думал. Я написал небольшую программу, чтобы видеть, что происходит, и использовал ее в качестве своего дочернего процесса.

программа предоставляет вам три варианта: 1 запись в STDOUT, 2 запись в STDERR и 3 выход.

У меня есть вопросы/проблемы :

1 — когда я использую std::cin для чтения того, что поступает от моей родительской/серверной программы, я не могу получить значение, которое отправляю, но при использовании fread для чтения байта из STDIN это работает, и я могу получить свое значение.

2 — я создал каналы с FILE_FLAG_OVERLAPPED, должен ли я использовать перекрывающуюся структуру для своей операции записи (я не мог заставить ее работать), потому что я просто использовал функцию записи и поставил НУЛЬ в перекрывающейся структуре.

     STARTUPINFO si;
    PROCESS_INFORMATION pi;
    SECURITY_ATTRIBUTES sa;

    ZeroMemory(amp;si, sizeof(si));
    si.cb = sizeof(si);
    if (fHideConsole)
    {
        si.dwFlags |= STARTF_USESHOWWINDOW;
        si.wShowWindow = SW_HIDE;
    }

    ZeroMemory(amp;pi, sizeof(pi));

    // Sets the bInheritHandle flag so that pipes are inherited (TODO depends on what is open)
    SECURITY_DESCRIPTOR saDesc;
    InitializeSecurityDescriptor(amp;saDesc, SECURITY_DESCRIPTOR_REVISION);
    SetSecurityDescriptorDacl(amp;saDesc, TRUE, NULL, FALSE);

    sa.nLength = sizeof(sa);
    sa.bInheritHandle = TRUE;
    sa.lpSecurityDescriptor = amp;saDesc;

    si.dwFlags |= STARTF_USESTDHANDLES;
    si.hStdOutput = INVALID_HANDLE_VALUE;
    si.hStdError = INVALID_HANDLE_VALUE;
    si.hStdInput = INVALID_HANDLE_VALUE;

    if (inReadCallback)
    {
        //if (!CreateOverlappedPipe(amp;fOutPipe, amp;outPipeWrite, amp;sa))
        //  err = GetLastError();
        //if (!SetHandleInformation(fOutPipe, HANDLE_FLAG_INHERIT, 0))
        //  err = GetLastError();

        fOutPipe = CreateNamedPipe(
            L"\\.\Pipe\RemoteExeOut",
            PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
            PIPE_TYPE_BYTE | PIPE_WAIT,
            PIPE_UNLIMITED_INSTANCES,           // Number of pipes
            kSizeRead,  // Out buffer size
            kSizeRead,  // In buffer size
            0,          // Timeout in ms
            amp;sa);


        si.hStdOutput = CreateFileW(
            L"\\.\Pipe\RemoteExeOut",
            FILE_WRITE_DATA,
            0,                         // No sharing
            amp;sa,
            OPEN_EXISTING,
            0,
            NULL);
    }

    if (inReadErrorCallback)
    {
        fErrPipe = CreateNamedPipe(
            L"\\.\Pipe\RemoteExeErr",
            PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
            PIPE_TYPE_BYTE | PIPE_WAIT,
            PIPE_UNLIMITED_INSTANCES,           // Number of pipes
            kSizeRead,  // Out buffer size
            kSizeRead,  // In buffer size
            0,          // Timeout in ms
            amp;sa);

        si.hStdError = CreateFileW(
            L"\\.\Pipe\RemoteExeErr",
            FILE_WRITE_DATA,
            0,                         // No sharing
            amp;sa,
            OPEN_EXISTING,
            0,
            NULL);
    }

    fInPipe = CreateNamedPipe(
        L"\\.\Pipe\RemoteExeIn",
        PIPE_ACCESS_OUTBOUND,   //PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
        PIPE_TYPE_BYTE | PIPE_WAIT,
        PIPE_UNLIMITED_INSTANCES,           // Number of pipes
        kSizeRead,  // Out buffer size
        kSizeRead,  // In buffer size
        0,          // Timeout in ms
        amp;sa
    );
    si.hStdInput = CreateFileW(
        L"\\.\Pipe\RemoteExeIn",
        FILE_READ_DATA,
        0,                         // No sharing
        amp;sa,
        OPEN_EXISTING,
        0,
        NULL); 

    if (CreateProcessW(nullptr, cmd, nullptr, nullptr, TRUE, 0, inEnvironment, currDir, amp;si, amp;pi))
    {
        // custom code to send to thread
        fIsRunning = true;
        fPid = pi.dwProcessId;

        WaitData* wData = new WaitData{ pi.dwProcessId, static_cast<DWORD>(inTimeoutSeconds), this, inTerminatedCallback };
        wData->readHandle = fOutPipe;
        wData->readCallback = inReadCallback;
        wData->errorHandle = fErrPipe;
        wData->errorCallback = inReadErrorCallback;

        fWaitingTask = new VTask(NULL, 0, eTaskStylePreemptive, WaitFct);
        fWaitingTask->SetKind('WLEP');
        fWaitingTask->SetKindData(reinterpret_cast<sLONG_PTR>(wData));
        fWaitingTask->Run();
    }
    else
    {
        vThrowWin32Error(GetLastError());   // custom error handling
    }

    delete[] cmd;
    delete[] currDir;
    // Close process and thread handles.
    if (pi.hProcess != INVALID_HANDLE_VALUE)
        CloseHandle(pi.hProcess);
    if (pi.hThread != INVALID_HANDLE_VALUE)
        CloseHandle(pi.hThread);

    if (si.hStdInput != INVALID_HANDLE_VALUE)
        CloseHandle(si.hStdInput);
    if (si.hStdOutput != INVALID_HANDLE_VALUE)
        CloseHandle(si.hStdOutput);
    if (si.hStdError != INVALID_HANDLE_VALUE)
        CloseHandle(si.hStdError);
 

моя функция записи :

 DWORD dwWritten = 0;
OVERLAPPED ovr;

BOOL success = WriteFile(fInPipe, inBuffer, inSize, amp;dwWritten, nullptr);

if (!success)
{
    DWORD err = GetLastError();
    if (err == ERROR_BROKEN_PIPE)
        DebugMsg(" !!!!!!!!! BROKEN PIPE !!!!!!!");
    if (err == ERROR_IO_PENDING)
        DebugMsg(" IO IS PENDING ASYNC USE");
}
 

заранее спасибо.

Комментарии:

1. Я не уверен, что вижу проблему в использовании анонимных каналов. Не могли бы вы поподробнее, пожалуйста?

2. для чего вы создаете 3 пары труб, когда достаточно 1 ? (если вы только не хотите разделять stdout и stderr ). тогда для чего вы создаете файлы для асинхронного ввода-вывода, когда вы действительно им не пользуетесь ? (std::cin, fread, поместите NULL в перекрывающуюся структуру)

3. @Someprogrammerdude вы не можете использовать анонимные каналы для асинхронного ввода-вывода.

4. @RbMm у меня большие проблемы с пониманием того, что ты пытаешься сказать

5. @BobMaza — мы можем использовать анонимные каналы для асинхронного ввода-вывода. Другой вопрос, что ZwCreateNamedPipeFile для этого нужно использовать. я пытаюсь спросить — для чего вы используете так много пар труб, когда одной пары достаточно. и для чего вам нужен асинхронный ввод-вывод, когда вы действительно этого не делаете