ReadFileEx, длина переменной - несколько вопросов

0

Я пытаюсь прочитать из stderr дочернего процесса. Данные представляют собой строки текста, созданные с помощью sprintf(stderr, "some debug info\n"). Я использую ReadFileE x с процедурой завершения. Я не знаю, сколько строк текста или как долго каждая строка будет. Итак, что я ставлю как параметр nNumberOfBytesToRead?

Я предполагаю, что я установил максимальный размер моего буфера, который я сделаю 4k; хотя я не знаю, если это оптимальный размер. Я предполагаю, что если строка, написанная на stderr, короче 4k, процедура завершения не будет срабатывать. Я предполагаю, что когда 4k достигнуто, но осталось больше данных, я должен запустить другой ReadFileEx в рамках процедуры завершения. Я знаю, что это так, потому что GetLastError вернет ERROR_MORE_DATA. Я надеюсь, что я получу вызов, когда буфер не заполнен, но дочерний процесс завершен. Я не уверен, что получаю обратный вызов завершения, когда дочерний процесс завершается, потому что я передал дескриптор stderr write ребенку, когда я его создал; возможно, я получаю обратный вызов, когда я закрываю этот дескриптор. Существует ли какое - либо условие гонки, когда ребенок закрывает wrt на мое чтении stderr?

Вот psuedo-код того, как создаются процесс и дескрипторы:

Attr.bInheritHandle = true
CreatePipe(&hr, &hw, &Attr, 0) and SetHandleInformation(hX, HANDLE_FLAG_INHERIT) on hX the child uses.
Si.hStdXXX = handles from CreatePipe that child uses
CreateProcess(inherit=true, &Si)

Подробности (расширение Tx - это оболочка, которая вызывает ошибки):

HANDLE Create() {
    STARTUPINFO SI, *pSI = NULL;
    bool fInherit = m_fInherit;
    if (m_fStdOut || m_fStdIn || m_fStdErr) {
        fInherit = true;
        SECURITY_ATTRIBUTES Attr;
        Attr.nLength = sizeof(SECURITY_ATTRIBUTES); 
        Attr.bInheritHandle = TRUE; 
        Attr.lpSecurityDescriptor = NULL;
        if (m_fStdOut) // Create a pipe for the child process STDOUT. The child will use the write.
            CHandle::CreatePipe(m_hStdOutR, m_hStdOutW, &Attr, CP_INHERIT_WRITE);
        if (m_fStdErr) // Create a pipe for the child process STDERR. The child will use the write.
            CHandle::CreatePipe(m_hStdErrR, m_hStdErrW, &Attr, CP_INHERIT_WRITE);
        if (m_fStdIn) // Create a pipe for the child process STDIN. The child will use the read.
            CHandle::CreatePipe(m_hStdInR, m_hStdInW, &Attr, CP_INHERIT_READ);
        // Set up members of the STARTUPINFO structure. 
        // This structure specifies the STDIN and STDOUT handles for redirection.
        ZeroStruct(SI);
        SI.cb = sizeof(STARTUPINFO); 
        SI.hStdError = m_hStdErrW, SI.hStdOutput = m_hStdOutW, SI.hStdInput = m_hStdInR;
        SI.dwFlags |= STARTF_USESTDHANDLES;
        pSI = &SI;
    }
    //  m_fCpu, m_fNuma are masks to set affinity to cpus or numas
    CreateProcessTx(NULL, m_szCmdLine, fInherit, m_fFlags, pSI, &m_pi, m_fCpu, m_fNuma, 5);
    m_hProc = m_pi.hProcess;
    m_hThread = m_pi.hThread;
    if (!m_fThread)
        m_hThread.Close();
    return m_hProc;
}

static void CreatePipe(CHandle &hRead, CHandle &hWrite, SECURITY_ATTRIBUTES* pAttr, BYTE fInheritMask) {
    HANDLE hReadTmp = NULL, hWriteTmp = NULL;
    CreatePipeTx(hReadTmp, hWriteTmp, pAttr);
    SetHandleInformation(hReadTmp, HANDLE_FLAG_INHERIT, (fInheritMask&CP_INHERIT_READ) ? HANDLE_FLAG_INHERIT : 0); 
    SetHandleInformation(hWriteTmp, HANDLE_FLAG_INHERIT, (fInheritMask&CP_INHERIT_WRITE) ? HANDLE_FLAG_INHERIT : 0);
    hRead = hReadTmp;
    hWrite = hWriteTmp;
}
  • 0
    Просто чтобы прояснить, вы используете дескрипторы, созданные с помощью CreatePipe ?
  • 0
    @RR хорошая мысль. Я отредактировал пост, добавив некоторые части кода, которые создают каналы и процесс.
Теги:
file-io
64bit

1 ответ

2
Лучший ответ

Анонимные трубы, созданные с помощью CreatePipe не могут использоваться асинхронно. Из документации Windows SDK:

Асинхронные (перекрывающиеся) операции чтения и записи не поддерживаются анонимными трубами. Это означает, что вы не можете использовать функции ReadFileEx и WriteFileEx с анонимными трубами. > Кроме того, параметр lpOverlapped ReadFile и WriteFile игнорируется, когда эти функции используются с анонимными трубами.

В основном CreatePipe не принимает флаг FILE_FLAG_OVERLAPPED, и для асинхронного ввода-вывода требуется, чтобы вы использовали флаг при создании дескриптора файла.

Вам нужно будет использовать CreateNamedPipe для создания именованных каналов. В вопросе Overlapped I/O на анонимном канале есть ответ со ссылкой на заменяемую функцию MyCreatePipeEx вы можете использовать.

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

Чтобы прочитать переменное количество данных из клиентского процесса, просто выдайте запросы на чтение любого размера, который вы найдете удобным, и будьте готовы обработать события чтения, которые короче, чем вы просили. Не интерпретируйте короткую, но ненулевую длину как EOF. Продолжайте выдавать запросы на чтение до получения нулевой длины или ошибки.

Также WaitForMultipleObjects не будет работать с процедурами завершения, поскольку они вызываются только тогда, когда поток находится в переменном состоянии. Используйте WaitForMultipleObjectEx с аргументом bAlertable установленным в true. Эта функция вернет WAIT_IO_COMPLETION после выполнения одной или нескольких процедур завершения. В этом случае вы, вероятно, захотите снова вызвать WaitForMultipleObjectEx снова.

  • 0
    Я просто пришел к такому выводу, так как мой вызов ReadFileEx блокировался. Я думал, что может быть облажался, пока вы не сказали, чтобы попробовать CreateNamedPipe, что я сейчас и делаю. Вы знаете что-нибудь о чтении переменной длины?
  • 0
    Хм ... пропустил ту часть вашего вопроса, я обновил свой ответ, чтобы ответить на этот вопрос.
Показать ещё 7 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню