Я создаю службу Windows, с которой не может быть связана консоль.Поэтому я хочу перенаправить stdout и stderr в один и тот же файл.Вот что я обнаружил до сих пор:
- Перенаправление
cout
и cerr
в C ++ может быть сделано путем изменения буферов , но это не влияет на CI / O, напримерputs
или дескрипторы ввода / вывода Windows. - Следовательно, мы можем использовать
freopen
для повторного открытия stdout или stderr в виде файла, подобного здесь , но мы не можем указатьодин и тот же файл дважды. - Чтобы по-прежнему использовать один и тот же файл для обоих, мы можем перенаправить stderr в стандартный вывод, используя
dup2
как здесь .
Пока все хорошо, и когда мы запускаем этот код с /SUBSYSTEM:CONSOLE
(свойства проекта → Линкер → Система), все работает отлично:
#include <Windows.h>
#include <io.h>
#include <fcntl.h>
#include <cstdio>
#include <iostream>
void doit()
{
FILE *stream;
if (_wfreopen_s(&stream, L"log.log", L"w", stdout)) __debugbreak();
// Also works as service when uncommenting this line: if (_wfreopen_s(&stream, L"log2.log", L"w", stderr)) __debugbreak();
if (_dup2(_fileno(stdout), _fileno(stderr)))
{
const auto err /*EBADF if service; hover over in debugger*/ = errno;
__debugbreak();
}
// Seemingly can be left out for console applications
if (!SetStdHandle(STD_OUTPUT_HANDLE, reinterpret_cast<HANDLE>(_get_osfhandle(_fileno(stdout))))) __debugbreak();
if (!SetStdHandle(STD_ERROR_HANDLE, reinterpret_cast<HANDLE>(_get_osfhandle(_fileno(stderr))))) __debugbreak();
if (_setmode(_fileno(stdout), _O_WTEXT) == -1) __debugbreak();
if (_setmode(_fileno(stderr), _O_WTEXT) == -1) __debugbreak();
std::wcout << L"1☺a" << std::endl;
std::wcerr << L"1☺b" << std::endl;
_putws(L"2☺a");
fflush(stdout);
fputws(L"2☺b\n", stderr);
fflush(stderr);
const std::wstring a3(L"3☺a\n"), b3(L"3☺b\n");
if (!WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), a3.c_str(), a3.size() * sizeof(wchar_t), nullptr, nullptr))
__debugbreak();
if (!WriteFile(GetStdHandle(STD_ERROR_HANDLE), b3.c_str(), b3.size() * sizeof(wchar_t), nullptr, nullptr))
__debugbreak();
}
int main() { doit(); }
int WINAPI wWinMain(HINSTANCE, HINSTANCE, PWSTR, int) { return doit(), 0; }
Это приятно записывает следующий текст в log.log
:
1☺a
1☺b
2☺a
2☺b
3☺a
3☺b
( Конечно мы хотим смайликов, поэтому нам нужен какой-то юникод. В этом случае мы используем широкие символы, что означает, что нам нужно использовать setmode
или все остальноеиспортит.)
Но теперь вернемся к первоначальной проблеме: сделать это как сервис без консоли или, что то же самое, но простоотладка, приложение с графическим интерфейсом (/SUBSYSTEM:WINDOWS
).Проблема в том, что в этом случае dup2
завершается с ошибкой , потому что fileno(stderr)
не является допустимым дескриптором файла, поскольку приложение изначально не имеет связанных потоков.Как уже упоминалось здесь , fileno(stderr) == -2
в данном случае.
Обратите внимание, что когда мы впервые открываем stderr как другой файл, используя freopen
, все работает нормально, но мы создали пустой файл.
Итак, теперь мой вопрос: Каков наилучший способ перенаправить как stdout, так и stderr в один и тот же файл в приложении, в котором изначально нет потоков?
ПростоНапомним: проблема в том, что , когда stdout
или stderr
не связаны с выходным потоком, fileno
возвращает -2 , поэтому мы не можем передать его в dup2
.
(Я не хочу менять код, используемый для фактической печати, потому что это может означать, что некоторые выходные данные, создаваемые внешними функциями, не будут перенаправлены.)