Изменить 2021:
В Windows 10 теперь есть ConPTY API ( aka псевдоконсоль), который в основном позволяет любой программе действовать как консоль для другой программы, таким образом, позволяет захватывать вывод, который напрямую записывается на консоль.
Это делает мой исходный ответ устаревшим для версий Windows, поддерживающих ConPTY.
Оригинальный ответ:
Из справки:
WriteConsole не работает, если он используется со стандартным дескриптором, который перенаправляется в файл. Если приложение обрабатывает многоязычный вывод, который может быть перенаправлен, определите, является ли дескриптор вывода дескриптором консоли (один из методов - вызвать GetConsoleMode и проверьте, удалось ли это сделать). Если дескриптор является дескриптором консоли, вызовите WriteConsole. Если дескриптор не является дескриптором консоли, вывод перенаправляется, и вы должны вызвать WriteFile для выполнения ввода / вывода.
Это применимо только в том случае, если вы контролируете исходный код приложения, которое хотите перенаправить. Недавно мне пришлось перенаправить вывод из приложения с закрытым исходным кодом, которое безусловно вызывало WriteConsole()
, поэтому его нельзя было перенаправить в обычном режиме.
Чтение экранного буфера консоли (как было предложено в этом ответе) оказалось ненадежным, поэтому я использовал библиотека Microsoft Detours для подключения WriteConsole()
API к целевому процессу и вызова WriteFile()
при необходимости. В противном случае вызовите исходную функцию WriteConsole()
.
Я создал DLL-перехватчик на основе примера Использование обходов:
#include <windows.h>
#include <detours.h>
// Target pointer for the uninstrumented WriteConsoleW API.
//
auto WriteConsoleW_orig = &WriteConsoleW;
// Detour function that replaces the WriteConsoleW API.
//
BOOL WINAPI WriteConsoleW_hooked(
_In_ HANDLE hConsoleOutput,
_In_ const VOID *lpBuffer,
_In_ DWORD nNumberOfCharsToWrite,
_Out_ LPDWORD lpNumberOfCharsWritten,
_Reserved_ LPVOID lpReserved
)
{
// Check if this actually is a console screen buffer handle.
DWORD mode;
if( GetConsoleMode( hConsoleOutput, &mode ) )
{
// Forward to the original WriteConsoleW() function.
return WriteConsoleW_orig( hConsoleOutput, lpBuffer, nNumberOfCharsToWrite, lpNumberOfCharsWritten, lpReserved );
}
else
{
// This is a redirected handle (e. g. a file or a pipe). We multiply with sizeof(WCHAR), because WriteFile()
// expects the number of bytes, but WriteConsoleW() gets passed the number of characters.
BOOL result = WriteFile( hConsoleOutput, lpBuffer, nNumberOfCharsToWrite * sizeof(WCHAR), lpNumberOfCharsWritten, nullptr );
// WriteFile() returns number of bytes written, but WriteConsoleW() has to return the number of characters written.
if( lpNumberOfCharsWritten )
*lpNumberOfCharsWritten /= sizeof(WCHAR);
return result;
}
}
// DllMain function attaches and detaches the WriteConsoleW_hooked detour to the
// WriteConsoleW target function. The WriteConsoleW target function is referred to
// through the WriteConsoleW_orig target pointer.
//
BOOL WINAPI DllMain(HINSTANCE hinst, DWORD dwReason, LPVOID reserved)
{
if (DetourIsHelperProcess()) {
return TRUE;
}
if (dwReason == DLL_PROCESS_ATTACH) {
DetourRestoreAfterWith();
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(&(PVOID&)WriteConsoleW_orig, WriteConsoleW_hooked);
DetourTransactionCommit();
}
else if (dwReason == DLL_PROCESS_DETACH) {
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourDetach(&(PVOID&)WriteConsoleW_orig, WriteConsoleW_hooked);
DetourTransactionCommit();
}
return TRUE;
}
Примечание. В ветке WriteFile()
я не пишу BOM (отметку порядка байтов), потому что это не всегда нужно (например, при перенаправлении в канал вместо файла или при добавлении к существующему файлу ). Приложение, использующее DLL для перенаправления вывода процесса в файл, может просто самостоятельно записать спецификацию UTF-16 LE перед запуском перенаправленного процесса.
Целевой процесс создается с использованием DetourCreateProcessWithDllExW()
с указанием имени нашей DLL-перехватчика в качестве аргумента для параметр lpDllName
. Другие аргументы идентичны тому, как вы создаете перенаправленный процесс с помощью _ 9_ API. Я не буду вдаваться в подробности, потому что все это хорошо задокументировано.
person
zett42
schedule
22.07.2018