Как настроить канал между двумя дочерними процессами в Win32? - PullRequest
2 голосов
/ 30 ноября 2010

За свою жизнь я не могу понять, почему это не работает. По сути, я создал канал с битом наследования, установленным в true, и создал два дочерних процесса, и использовал структуру STARTUPINFO для установки дескрипторов ввода и вывода так, как это необходимо, но канал кажется разорванным (второй процесс не записывает выходные данные в консоль, даже если ожидается вывод)

Я знаю, что проблема не в моей тестовой программе (BitTwiddler.exe), потому что я выполнил ту же операцию, используя CMD.exe, и все работает как положено.

Ниже приведено минимальное воспроизведение того, что у меня есть. Что я сделал неправильно?

#include "windows.h"

int main()
{
    PROCESS_INFORMATION piSource, piDest;
    HANDLE hPipeIn, hPipeOut;
    HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    STARTUPINFOW suSource, suDest;
    ZeroMemory(&suSource, sizeof(suSource));
    ZeroMemory(&suDest, sizeof(suDest));
    suSource.cb = suDest.cb = sizeof(STARTUPINFOW);
    suSource.dwFlags = suDest.dwFlags = STARTF_USESTDHANDLES;
    SECURITY_ATTRIBUTES sa;
    sa.nLength = sizeof(sa);
    sa.lpSecurityDescriptor = 0;
    sa.bInheritHandle = TRUE;
    if (CreatePipe(&hPipeIn, &hPipeOut, &sa, 0) == 0)
    {
        return GetLastError();
    }
    suSource.hStdInput = hIn;
    suSource.hStdError = suSource.hStdOutput = hPipeIn;
    suDest.hStdInput = hPipeOut;
    suDest.hStdError = suDest.hStdOutput = hOut;
    std::wstring cmdLineA(L"BitTwiddler 1"), cmdLineB(L"BitTwiddler 0");
    cmdLineA.push_back(0); cmdLineB.push_back(0);
    if (CreateProcessW(0, &cmdLineA[0], 0, 0, TRUE, 0, 0, 0, &suSource, &piSource) == 0)
    {
        return GetLastError();
    }
    CloseHandle(piSource.hThread);
    if (CreateProcessW(0, &cmdLineB[0], 0, 0, TRUE, 0, 0, 0, &suDest, &piDest) == 0)
    {
        return GetLastError();
    }
    CloseHandle(piDest.hThread);
    HANDLE hArray[2];
    hArray[0] = piSource.hProcess;
    hArray[1] = piDest.hProcess;
    WaitForMultipleObjects(2, hArray, TRUE, INFINITE);
    CloseHandle(hArray[0]);
    CloseHandle(hArray[1]);
    return 0;
}

(Если кому-то интересно, BitTwiddler:

#include <windows.h>
#include <sstream>
#include <iostream>
#include <string>

int main(int argc, char *argv[])
{
    std::size_t opt = 0;
    argc--; argv++;
    if (argc == 0)
    {
        return 0;
    }
    else
    {
        std::istringstream converter(*argv);
        converter >> opt;
    }
    switch(opt)
    {
    case 0:
        {
            std::wstring currentLine;
            while(std::getline(std::wcin, currentLine))
            {
                std::wcout << "Got somepin: " << currentLine << std::endl;
            }
        }
        break;
    case 1:
        for (;;)
        {
            std::wcout << L"Hello World!" << std::endl;
            Sleep(1000);
        }
        break;
    case 2:
        return -1;
    default:
        std::wcout << "Unknown option.";
        return 0;
    }
    return 0;
}

), но я действительно не думаю, что это имеет значение.

1 Ответ

5 голосов
/ 30 ноября 2010

Вы потеряли место для чтения и записи:)

CreatePipe имеет прототип

BOOL CreatePipe(
  PHANDLE hReadPipe,  // can only read from this
  PHANDLE hWritePipe,  // can only write to this
  LPSECURITY_ATTRIBUTES lpPipeAttributes,
  DWORD nSize
);

Вы не можете вызвать ReadFile (или в вашем случае std :: getline) из дескриптора только для записи, и наоборот. Если вы замените свои std::getline вызовы на простой ReadFile вызов, вы получите ошибку ACCESS_DENIED, подтверждающую этот факт, поскольку ваш STD_INPUT_HANDLE в дочернем процессе не был открыт для GENERIC_READ.

Исправление выглядит следующим образом:

suSource.hStdError = suSource.hStdOutput = hPipeOut;  // must be the write pipe!
suDest.hStdInput = hPipeIn;  // must be the read pipe.

Возможно, назначенные вами имена сбивают с толку. Если бы вы вызывали их согласно формальным параметрам, ошибка была бы более ясной:

suSource.hStdError = suSource.hStdOutput = hReadPipe;  // clearly wrong.
suDest.hStdInput = hWritePipe;  // as above -- expects a read-handle.
...