c# - 重定向运行进程的输出 (Visual C#)

标签 c# output io-redirection

我有一个正在运行的控制台,我需要获取输出。我无法使用 startprocess 启动控制台,因为它是单独生成的。我无权访问源代码,我只是想在控制台已经运行时重定向输出。

最佳答案

事实证明,使用托管框架附加到已经运行的单独进程是不可能的。

但是,可以使用 kernel32.dll 下的 Console Api Functions 来实现这一点。

编辑:改进代码以获得更好的可用性

为了实现这一点,我们需要使用 FreeConsoleAttachConsoleReadConsoleOutputCharacterGetConsoleScreenBufferInfo 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 函数,用于同步标准输出的 currentPosition
  • GetConsoleScreenBufferInfo 用于获取控制台的lineSize
    • short lineSize = outInfo.dwSize.X 为 lineSize 添加
  • uint numberofLinesToRead = (uint) (outInfo.dwCursorPosition.Y - currentPosition) 用于使用控制台的实际位置与光标的当前位置之间的差异来计算要读取的行数。
  • 考虑 lpNumberOfCharsRead 以避免垃圾行结尾

关于c# - 重定向运行进程的输出 (Visual C#),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54317061/

相关文章:

c# - 屏幕抓取 : unable to authenticate into a site utilizing ASP . NET Forms 身份验证

c# - WPF 应用程序中的按钮响应较慢

c# - 如何在 C# 中获取 System.Numerics.Vector 的元素?

c - MacOS 终端与 Xcode 中的不同输出(C 程序)

c - 输入重定向不适用于我的程序

c# - 从特定 URL 获取 ip

使用 DSH 将输出编译到文件

powershell - Powershell计算机的上次启动时间,显示两个不同的输出

Perl,重定向标准输出但保持父级

linux - 为什么在 bash 中的 while read 循环中重定向 stdin?