Windows C++: как перенаправить stderr для вызовов fprintf?

Я оборачиваю существующий код C++ из проекта BSD в нашу собственную настраиваемую оболочку и хочу интегрировать его в наш код с как можно меньшим количеством изменений. Этот код используется fprintf для печати в stderr , чтобы регистрировать / сообщать об ошибках.

Я хочу перенаправить это в другое место в рамках того же процесса. В Unix я сделал это с помощью a socketpair и a thread : один конец сокета - это то место, куда я отправляю stderr (через вызов dup2 ), а другой конец отслеживается в потоке, где я затем могу обработать вывод.

Это не работает в Windows, потому что сокет - это не то же самое, что дескриптор файла.

Все документы, которые я нашел в Интернете, показывают, как перенаправить вывод дочернего процесса, чего я не хочу. Как я могу перенаправить stderr в том же процессе, получив какой-то обратный вызов при записи вывода? (и, прежде чем вы это скажете, я пробовал, SetStdHandle но не могу найти способ заставить эту работу) ...

Ответов (3)

Решение

Вы можете использовать похожую технику в Windows, вам просто нужно использовать разные слова для обозначения одних и тех же понятий. :) Эта статья: http://msdn.microsoft.com/en-us/library/ms682499.aspx использует конвейер win32 для обработки ввода-вывода из другого процесса, вам просто нужно сделать то же самое с потоками внутри того же процесс. Конечно, в вашем случае весь вывод на stderr из любой точки процесса будет перенаправлен вашему потребителю.

На самом деле, вам могут понадобиться другие кусочки головоломки: _fdopen и _open_osfhandle . Фактически, вот связанный пример из кода, который я выпустил много лет назад:

DWORD CALLBACK DoDebugThread(void *)
{
    AllocConsole();
    SetConsoleTitle("Copilot Debugger");
    // The following is a really disgusting hack to make stdin and stdout attach
    // to the newly created console using the MSVC++ libraries. I hope other
    // operating systems don't need this kind of kludge.. :)
    stdout->_file = _open_osfhandle((long)GetStdHandle(STD_OUTPUT_HANDLE), _O_TEXT);
    stdin->_file  = _open_osfhandle((long)GetStdHandle(STD_INPUT_HANDLE), _O_TEXT);
    debug();
    stdout->_file = -1;
    stdin->_file  = -1;
    FreeConsole();
    CPU_run();
    return 0;
}   

В этом случае основным процессом был процесс с графическим интерфейсом пользователя, который вообще не запускался с дескрипторов stdio. Он открывает консоль, затем вставляет правые ручки в stdout и stdin, чтобы функция debug () (которая была разработана как интерактивная функция stdio) могла взаимодействовать с вновь созданной консолью. У вас должна быть возможность открыть несколько каналов и сделать то же самое для перенаправления stderr.

Вы должны помнить, что то, что MSVCRT называет «дескрипторами ОС», - это не дескрипторы Win32, а еще один уровень дескрипторов, добавленный только для того, чтобы вас запутать. MSVCRT пытается имитировать номера дескрипторов Unix, где stdin = 0, stdout = 1, stderr = 2 и так далее. Дескрипторы Win32 пронумерованы по-разному, и их значения всегда кратны 4. Открытие канала и правильная настройка всех дескрипторов потребует беспорядка в ваших руках. Использование исходного кода MSVCRT и отладчика, вероятно, является обязательным требованием.

Вы упоминаете, что не хотите использовать именованный канал для внутреннего использования; вероятно, стоит указать, что в документации для CreatePipe () указано: «Анонимные каналы реализованы с использованием именованного канала с уникальным именем. Поэтому вы часто можете передать дескриптор анонимного канала функции, которая требует дескриптора именованного канала. трубка." Итак, я предлагаю вам просто написать функцию, которая создает аналогичный канал с правильными настройками для асинхронного чтения. Я обычно использую GUID в виде строки (сгенерированной с помощью CoCreateGUID() и StringFromIID() ), чтобы дать мне уникальное имя, а затем создаю серверный и клиентский концы именованного канала с правильными настройками для перекрывающегося ввода-вывода (подробнее об этом и коде, здесь: http://www.lenholgate.com/blog/2008/02/process-management-using-jobs-on-windows.html ).

Как только у меня есть это, я подключаю некоторый код, который я должен прочитать файл, используя перекрывающийся ввод-вывод с портом завершения ввода-вывода, и, ну, тогда я просто получаю асинхронные уведомления о данных по мере их поступления ... Однако я у меня есть изрядное количество хорошо протестированного библиотечного кода, благодаря которому все это происходит ...

Вероятно, можно настроить именованный канал, а затем просто выполнить перекрывающееся чтение с событием в вашей OVERLAPPED структуре и проверить событие, чтобы узнать, были ли данные доступны ... Однако у меня нет кода, который бы это делал.