创建单个进程时我成功重定向输出。但是,当该进程生成其他进程时,不会重定向任何输出。除了创建初始进程之外,我无法控制孙进程的实例化方式/时间。似乎唯一的其他选择可能是 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> 过程。
我们在这里可以做的是引入另一个进程PI,它将成为PC的直接父进程,并将其IO重定向到自身,然后将其发送到我们的根进程(示例中为PA)
这是我们 4 个可执行文件的代码
为了实现这一目标,我们有 4 个可执行文件(PA、PB、PC 和 PI)和一个库,其中包含用于进程重定向的共享代码以及 PA 和 PI 之间的通信 channel 我用过NetMQ
示例中由于其易于使用且可靠,但可以通过类似 channel 替换,因此我们通过Nuget PM> Install-Package NetMQ
获取该包.
以下是我们的类库的代码,PA 和 PI 都会引用它:
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/