2015-03-10 2 views
0

Я создаю процесс и читаю его stdout и err через win32 api. Проблема заключается в том, что после первого успешного чтения с ReadFile(), я всегда получаю ошибку разбитого трубопровода (109) для следующего для данных, оставшихся в трубе. (поэтому я только читаю содержимое размером с буфер в моем первом вызове) Я подозреваю, что это возможно потому, что я читаю трубку синхронно, но после того, как ребенок завершен?Win32 анонимный труба сломанный после первого чтения

void read_pipes(std::string &output, HANDLE read_pipe) { 
     assert(read_pipe != nullptr && read_pipe != INVALID_HANDLE_VALUE); 
     char buffer[4096]; 
     DWORD bytes_left_to_read, err_code; int i = 0; 
     do { 
      memset(&buffer, 0, 4096); 
      err_code = 0; 
      if (!ReadFile(read_pipe, buffer, 4096 - 1, &bytes_left_to_read, NULL)) { 
       err_code = GetLastError(); 
      } 
      assert(err_code != ERROR_IO_PENDING); 
      if (err_code != 0 && err_code != ERROR_MORE_DATA) { 
       char sys_err_msg[128]; 
       sys_err_msg[128 - 1] = '\0'; 
       FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, err_code, MAKELANGID(0x09, 0x01), sys_err_msg, 127, nullptr); 
       std::cerr << "Failed to read failed mmdb_importer log, because " << sys_err_msg << ";" << err_code 
         << std::endl; 
       break; 
      } 
      output.append(buffer); 
     } while (true); 

И часть кода, которая вызывает чтение (полный под http://pastebin.com/vakLULyf)

if (WaitForSingleObject(mddb_importer_info.hProcess, 60000) == WAIT_TIMEOUT) { 
     //Kill the importer, probably hangs 
     TerminateProcess(mddb_importer_info.hProcess, EXIT_FAILURE); 
    } 
    GetExitCodeProcess(mddb_importer_info.hProcess, &mddb_importer_return); 
    //assert (mddb_importer_return != STILL_ACTIVE); 

    switch (mddb_importer_return) { 
     case EXIT_SUCCESS: 
      file_to_upload.uploaded = true; 
      break; 
     default: 
      assert(file_to_upload.uploaded == false); 
     { 
      read_pipes(std::ref(file_to_upload.log), mddb_importer_stderr_r); 
      std::clog << "MDDB_Importer failed with err: : " << file_to_upload.log << std::endl; 
      read_pipes(std::ref(file_to_upload.log), mddb_importer_stdout_r); 
      std::clog << "And total size of log " << file_to_upload.log.length() << std::endl; 
     } 
      break; 
    } 
+0

Вы не проверить значение '' bytes_left_to_read' после ReadFile() '. Может быть, там что-то неожиданное ... – rodrigo

+0

Как говорит @rodrigo, вы не проверяете, сколько данных было получено - и вы не заканчиваете нулевым буфером, что означает, что ваш вызов 'string :: append' может легко убежать конец буфера. –

+0

@rodrigo @ jonathan-potter Буфер имеет нулевое завершение, потому что я начинаю его в начале каждого цикла ('memset') и вызывая ReadFile() с размером буфера - 1. Также проверка является IMHO не nesscessary потому что ReadFile возвращает false и устанавливает номер ошибки в 'ERROR_MORE_DATA'. Но даже тогда, когда я изменил условие while на 'while (bytes_left_to_read)', у меня такое же поведение. (И я знаю, что текст на трубе слева) – Superlokkus

ответ

0

Да выглядит, как я получил заказ неправильно, но так как я не хочу ждать вечно на возможно висящий процесс, теперь я читаю в трубах по потокам. Я запускаю свои функции read_pipes до ожидания с таймаутом, с std :: async. Snipett как пример:

if (!CloseHandle(mddb_importer_stderr_w)) throw std::runtime_error("Can not create mddb_importer pipes"); 


auto err = std::async(std::launch::async, read_pipes, mddb_importer_stderr_r); 
auto out = std::async(std::launch::async, read_pipes, mddb_importer_stdout_r); 

if (WaitForSingleObject(mddb_importer_info.hProcess, 60000) == WAIT_TIMEOUT) { 
    //Kill the importer, probably hangs 
    TerminateProcess(mddb_importer_info.hProcess, EXIT_FAILURE); 
} 
GetExitCodeProcess(mddb_importer_info.hProcess, &mddb_importer_return); 
//assert (mddb_importer_return != STILL_ACTIVE); 

const std::string err_string = err.get();