c# - 重定向 N 级子进程的标准输出

标签 c# winapi console-application redirectstandardoutput

创建单个进程时我成功重定向输出。但是,当该进程生成其他进程时,不会重定向任何输出。除了创建初始进程之外,我无法控制孙进程的实例化方式/时间。似乎唯一的其他选择可能是 Win32 API 调用,但我没有发现这种方法的证据。

最佳答案

当我们无法控制生成子进程时,恐怕没有简单的方法可以实现这一点。这里想到了两种方法:

1-Man in the middle:我们用我们自己的 IO 重定向器可执行文件替换子可执行文件,然后它将启动真正的子进程并将其 IO 重定向到我们自己的。

2-Hooking CreateProcess 或 Std:我们可以拦截进程创建并强制所有中间进程重定向,或者拦截 Std 调用并将其报告给 root。

注意:请注意,这些方法都不会 100% 有效,它们非常依赖于父级和子级可执行行为。

我选择中间人,因为它更容易实现并且不那么脆弱。

假设我们有 3 个可执行文件:

PA:我们的根可执行文件,我们可以完全控制代码和行为。

PB:我们的直接子可执行文件,我们可以轻松地将 IO 重定向到我们自己的进程,但我们对代码和行为没有任何控制。

PC:PB 间接产生子进程,我们无法控制它,也无法正常重定向 IO,因为它是由 PB< 创建的/strong> 过程。

process

我们在这里可以做的是引入另一个进程PI,它将成为PC的直接父进程,并将其IO重定向到自身,然后将其发送到我们的根进程(示例中为PA)

process 2

这是我们 4 个可执行文件的代码

为了实现这一目标,我们有 4 个可执行文件(PA、PB、PC 和 PI)和一个库,其中包含用于进程重定向的共享代码以及 PAPI 之间的通信 channel 我用过NetMQ示例中由于其易于使用且可靠,但可以通过类似 channel 替换,因此我们通过Nuget PM> Install-Package NetMQ 获取该包.

以下是我们的类库的代码,PAPI 都会引用它:

public class RedirectProcess
{
    public static Process StartRedirected(string filename, string args = "")
    {
        var process = Process.Start(new ProcessStartInfo
        {
            Arguments = args,
            UseShellExecute = false,
            RedirectStandardOutput = true,
            FileName = filename
        });

        process.OutputDataReceived += (s, e) => Console.WriteLine(e.Data);
        process.BeginOutputReadLine();

        return process;
    }

    public static Process StartGrab(string filename, Action<string> dataHandle, string args = "")
    {
        var process = Process.Start(new ProcessStartInfo
        {
            Arguments = args,
            UseShellExecute = false,
            RedirectStandardOutput = true,
            FileName = filename
        });

        process.OutputDataReceived += (s, e) => dataHandle(e.Data);
        process.BeginOutputReadLine();

        return process;
    }
}


public class RedirectClient:IDisposable
{
    private readonly NetMQContext _context;
    private readonly RequestSocket _socket;

    public int Id { get; set; }

    public RedirectClient()
    {
        _context = NetMQContext.Create();
        _socket = _context.CreateRequestSocket();
        _socket.Connect("ipc://redirectCollector");
    }

    public void Send(string data)
    {
        _socket.Send(string.Format("{0},{1}", Id, data));
        _socket.Receive();//ack
    }

    public void Dispose()
    {
        _context.Dispose();
    }
}


public class RediretServer:IDisposable
{
    private bool _start;
    public event EventHandler<ProcessDataArgs> ProcessDataReceived;

    public RediretServer()
    {
        _start = true;

       new Thread(() =>
       {
           using (var context = NetMQContext.Create())
           {
               var socket = context.CreateResponseSocket();
               socket.Bind("ipc://redirectCollector");

               while (_start)
               {
                   var res = socket.ReceiveString(TimeSpan.FromSeconds(1));
                   if (string.IsNullOrEmpty(res))
                       continue;

                   if (ProcessDataReceived != null)
                       ProcessDataReceived(this, new ProcessDataArgs(res));

                   socket.Send(new byte[]{ });//ack
               }
           }
       }).Start();
    }

    public void Dispose()
    {
        _start = false;
    }
}


public class ProcessDataArgs:EventArgs
{
    public string Data { get;private set; }
    public int Id { get;private set; }

    public ProcessDataArgs(string result)
    {
        var i = result.IndexOf(','); //first comma

        Id = int.Parse(result.Substring(0, i));
        Data=result.Substring(i+1);
    }
}

这是我们的可执行文件:

PA: (我们的主要可执行文件,所有 IO 都应该重定向到它)

internal class Program
{
    private static void Main(string[] args)
    {
        var server = new RediretServer();
        server.ProcessDataReceived += (s, e) => Console.WriteLine("pid:{0}, data:{1}", e.Id, e.Data);

        RedirectProcess.StartGrab("PB.exe", Console.WriteLine);

        while (true)
        {
            Console.WriteLine("Process A, ID: {0},- Hello", Process.GetCurrentProcess().Id);
            Thread.Sleep(1000);
        }
    }
}

PB: (只是为了模拟情况,我们的直接 child 我们可以轻松地将其 IO 重定向到我们自己的)

class Program
{
    static void Main(string[] args)
    {

        var process = Process.Start(new ProcessStartInfo
        {
            Arguments = "-argument",
            FileName = "PC.exe"
        });

        while (true)
        {
            Console.WriteLine("Process B, ID: {0},- Hello", Process.GetCurrentProcess().Id);
            Thread.Sleep(1000);
        }
    }
}

PC: (我们要重定向其 IO 的 PB 中的目标嵌套子级)

class Program
{
    static void Main(string[] args)
    {
        var a = args.Length == 0 ? "default" : args[0];

        while (true)
        {
            Console.WriteLine("Process C, ID: {0}, Args: {1}- Hello", Process.GetCurrentProcess().Id, a);
            Thread.Sleep(1000);
        }
    }
}

PI:(我们应该用我们的重定向器可执行文件替换PC)

class Program
{
    private static void Main(string[] args)
    {
        Console.WriteLine("**Process Redirector for PC**");

        var client = new RedirectClient();

        var interceptArgs = args
            .Aggregate(string.Empty, (current, a) => current + (string.Format("\"{0}\"", a)));

        var process=RedirectProcess.StartGrab("PC-real.exe", s =>
        {
            Console.WriteLine(s);
            client.Send(s);
        },
            interceptArgs);

        client.Id = process.Id;
        process.WaitForExit();

        client.Dispose();
    }
}

}

现在让我们构建它并将所有可执行文件放在一个文件夹中,如果我们运行PA,我们应该会看到两个控制台弹出,PA,但不是PC控制台输出应该是这样的:

PA 输出:(连续流)

Process A, ID: 85568,- Hello

Process B, ID: 85640,- Hello

PC 输出: (连续流)

Process C, ID: 87012, Args: -argument- Hello

我们不要将PC.exe重命名为PC-Real.exe,并将拦截器PI.exe重命名为PC.exe.

如果我们再次运行PA,拦截器会弹出,而不是原来的PC,我们将在PA进程中获取它的IO。

Process A, ID: 85144,- Hello

Process B, ID: 86436,- Hello

pid:83660, data:Process C, ID: 83660, Args: -argument- Hello

关于c# - 重定向 N 级子进程的标准输出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26199441/

相关文章:

c++ - 为什么 GetServiceDisplayNameW() 和 GetServiceDisplayNameA() 在字符中返回不同的所需缓冲区大小?

winapi - 在 C++ Windows API 运行时调整窗口大小?

windows - 如何从 Wave Out 中捕获 PCM 数据

C#控制台应用程序无法调试,很奇怪

c# - System.Windows.Forms.Application.ThreadException 等同于控制台应用程序或 Windows 服务或任何一般进程

c# - CSCore:从纯数据播放 float 组

algorithm - 这种模式在任何语言中

c# - 我如何使用 C# 在控制台中进行是/否提示?

C# 非矩形按钮

c# - Windows 服务器/数据中心 : set CPU affinity with > 64 cores