Перенаправить вывод из запущенного процесса (Visual C #) - PullRequest
0 голосов
/ 23 января 2019

У меня есть консоль, которая работает, и мне нужно получить вывод. Я не могу использовать startprocess для запуска консоли, так как она создается отдельно. У меня нет доступа к исходному коду, я просто пытаюсь перенаправить вывод с консоли, когда она уже запущена.

Ответы [ 2 ]

0 голосов
/ 23 января 2019

Оказывается, что присоединение к уже запущенному отдельному процессу с использованием управляемой инфраструктуры невозможно.

Однако этого можно добиться, используя Console Api Functions в kernel32.dll.

Редактировать: Код улучшен для удобства использования

Для достижения этого нам необходимо использовать FreeConsole, AttachConsole, ReadConsoleOutputCharacter, GetConsoleScreenBufferInfo и AttachConsole из WinApi

Объявления статических внешних библиотек:

[DllImport("kernel32.dll")]
private extern static IntPtr GetStdHandle(int nStdHandle);

[DllImport("kernel32.dll")]
static extern bool ReadConsoleOutputCharacter(IntPtr hConsoleOutput,
  [Out] StringBuilder lpCharacter, uint nLength, COORD dwReadCoord,
  out uint lpNumberOfCharsRead);

[DllImport("kernel32.dll")]
static extern bool FreeConsole();
[DllImport("kernel32.dll")]
static extern bool AttachConsole(int dwProcessId);

[DllImport("kernel32.dll")]
static extern bool GetConsoleScreenBufferInfo(
    IntPtr hConsoleOutput,
    out CONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo
);

[StructLayout(LayoutKind.Sequential)]
struct COORD
{
    public short X;
    public short Y;
}

[StructLayout(LayoutKind.Sequential)]
struct CONSOLE_SCREEN_BUFFER_INFO
{

    public COORD dwSize;
    public COORD dwCursorPosition;
    public short wAttributes;
    public SMALL_RECT srWindow;
    public COORD dwMaximumWindowSize;

}

[StructLayout(LayoutKind.Sequential)]
struct SMALL_RECT
{

    public short Left;
    public short Top;
    public short Right;
    public short Bottom;

}

const int STD_OUTPUT_HANDLE = -11;
const Int64 INVALID_HANDLE_VALUE = -1;

Сначала нам нужно освободить текущий дескриптор консоли, потому что мы можем подключаться только к одной консоли

private static string ReadALineOfConsoleOutput(IntPtr stdout, ref short currentPosition)
{

    if (stdout.ToInt32() == INVALID_HANDLE_VALUE)
        throw new Win32Exception();

    //Get Console Info
    if (!GetConsoleScreenBufferInfo(stdout, out CONSOLE_SCREEN_BUFFER_INFO outInfo))
        throw new Win32Exception();

    //Gets Console Output Line Size
    short lineSize = outInfo.dwSize.X;

    //Calculates Number of Lines to be read
    uint numberofLinesToRead = (uint)(outInfo.dwCursorPosition.Y - currentPosition);

    if (numberofLinesToRead < 1) return null;

    // read from the first character of the first line (0, 0).
    COORD dwReadCoord;
    dwReadCoord.X = 0;
    dwReadCoord.Y = currentPosition;

    //total characters to be read
    uint nLength = (uint)lineSize * numberofLinesToRead + 2*numberofLinesToRead;

    StringBuilder result = new StringBuilder((int)nLength);
    StringBuilder lpCharacter = new StringBuilder(lineSize);
    for (int i = 0; i < numberofLinesToRead; i++)
    {
        if (!ReadConsoleOutputCharacter(stdout, lpCharacter, (uint) lineSize, dwReadCoord, out uint lpNumberOfCharsRead))
            throw new Win32Exception();
        result.AppendLine(lpCharacter.ToString(0, (int)lpNumberOfCharsRead-1));
        dwReadCoord.Y++;
    }

    currentPosition = outInfo.dwCursorPosition.Y;
    return result.ToString();
}

public static async Task Main()
{
    var processId = 8560;
    if (!FreeConsole()) return ;
    if (!AttachConsole(processId)) return;

    IntPtr stdout = GetStdHandle(STD_OUTPUT_HANDLE);
    short currentPosition = 0;

    while (true)
    {
        var r1 = ReadALineOfConsoleOutput(stdout, ref currentPosition);
        if (r1 != null)
            //write to file or somewhere => //Debug.WriteLine(r1);
    }
}

Улучшения

  • ref short currentPosition добавлено к ReadALineOfConsoleOutput функция для синхронизации текущего положения стандартного вывода
  • GetConsoleScreenBufferInfo используется для получения lineSize консоли
    • short lineSize = outInfo.dwSize.X добавлено для lineSize
  • uint numberofLinesToRead = (uint) (outInfo.dwCursorPosition.Y - currentPosition) используется для расчета количества строк, которые нужно прочитать, используя разницу между фактическим положением консоли и текущим положением курсора.
  • с учетом lpNumberOfCharsRead доизбегать концов строки мусора
0 голосов
/ 23 января 2019

нужно прочитать https://support.microsoft.com/en-us/help/318804/how-to-set-a-windows-hook-in-visual-c-net В частности, нижняя часть

Глобальные хуки не поддерживаются в .NET Framework За исключением низкоуровневого хука WH_KEYBOARD_LL и низкоуровневого хука WH_MOUSE_LL, вы не можете реализовать глобальные хуки в Microsoft .NET Framework. Чтобы установить глобальный хук, хук должен иметь собственный экспорт DLL, чтобы внедрить себя в другой процесс, для которого требуется допустимая, согласованная функция для вызова. Это поведение требует экспорта DLL. .NET Framework не поддерживает экспорт DLL. Управляемый код не имеет понятия согласованного значения для указателя функции, поскольку эти указатели на функции являются прокси, которые создаются динамически. Процедуры низкоуровневого хука вызываются на нити, в которой установлен хук

...