我有一个Windows窗体应用程序,可同时启动多达X个控制台进程。我从每个输出中获取JSON输出(一个对象),并将其解析为统计数据。我一直在跟踪我已经启动了多少个进程并在中捕获它们的输出OutputDataReceived()
。我ConcurrentBag<object>
使用流程ID作为关键字将输出附加到。
通常,我的JSON最终会变成两个对象,从而引发解析错误。我不确定如何最终获得来自同一对象中两个不同进程的数据。好像OutputDataReceived()
事件正在从与其报告的ID不同的过程中获取数据。我尝试实现一些锁定而没有任何运气(由于我来自Classic VB背景,这对我来说是个新手)。
这是一些相关的代码:
private object _lockObj = new object();
private ConcurrentBag<ProcData> _procDatas;
// This is called up to X times
private void LaunchProc(int itemId)
{
var proc = new Process();
proc.StartInfo.CreateNoWindow = true;
proc.StartInfo.ErrorDialog = false;
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.RedirectStandardError = true;
proc.StartInfo.RedirectStandardInput = true;
proc.StartInfo.RedirectStandardOutput = true;
proc.EnableRaisingEvents = true;
proc.Exited += proc_Exited;
proc.OutputDataReceived += proc_OutputDataReceived;
proc.ErrorDataReceived += proc_ErrorDataReceived;
proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
proc.StartInfo.FileName = "someapp.exe";
proc.StartInfo.Arguments = "/id=" + itemId;
proc.Start();
proc.BeginErrorReadLine();
proc.BeginOutputReadLine();
}
// I assume I'm screwing something up here since this is the only place where I set OutputData
void proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
var proc = sender as System.Diagnostics.Process;
if (proc == null) return;
if (e.Data == null) return;
lock (_lockObj)
{
var item = _procDatas.FirstOrDefault(pi => pi.Id == proc.Id);
if (item == null)
_procDatas.Add(new ProcData() {Id = proc.Id, OutputData = e.Data});
else
item.OutputData += e.Data;
}
}
void proc_Exited(object sender, EventArgs e)
{
var proc = sender as System.Diagnostics.Process;
ProcMessage procMsg = null;
lock (_lockObj)
{
var procInfo = _procDatas.FirstOrDefault(pi => pi.Id == proc.Id);
// JSON is parsed here and error is thrown because of multiple objects (eg {"ProcessId":1,"Msg":"Success"}{"ProcessId":2,"Msg":"Success"})
procMsg = new ProcMessage(procInfo.OutputData);
}
}
public class ProcData
{
public int Id { get; set; }
public string OutputData { get; set; }
public string ErrorData { get; set; }
}
在此先感谢您对解决此问题或提出其他(更好)方法的帮助。
该ID一个进程可以得到重用。因此,我建议在进程退出后,不要将进程ID用作标识符。
相反,您可以将您itemId
的标识符用作标识符,将流程输出与之关联,并将流程封装itemId
在某个容器中(例如,经过轻微测试,看起来还可以):
public class ProcessExitedEventArgs<TKey> : EventArgs
{
public ProcessExitedEventArgs(TKey key, string[] output)
{
this.Key = key;
this.Output = output;
}
public TKey Key { get; private set; }
public string[] Output { get; private set; }
}
public delegate void ProcessExitedEventHandler<TKey>(object sender, ProcessExitedEventArgs<TKey> e);
public class ProcessLauncher<TKey>
{
public string FileName { get; private set; }
public string Arguments { get; private set; }
public TKey Key { get; private set; }
object locker = new object();
readonly List<string> output = new List<string>();
Process process = null;
bool launched = false;
public ProcessLauncher(string fileName, string arguments, TKey key)
{
this.FileName = fileName;
this.Arguments = arguments;
this.Key = key;
}
public event ProcessExitedEventHandler<TKey> Exited;
public bool Start()
{
lock (locker)
{
if (launched)
throw new InvalidOperationException();
launched = true;
process = new Process();
process.StartInfo.CreateNoWindow = true;
process.StartInfo.ErrorDialog = false;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardOutput = true;
process.EnableRaisingEvents = true;
process.Exited += new EventHandler(proc_Exited);
process.OutputDataReceived += proc_OutputDataReceived;
process.ErrorDataReceived += proc_ErrorDataReceived;
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.StartInfo.FileName = FileName;
process.StartInfo.Arguments = Arguments;
try
{
var started = process.Start();
process.BeginErrorReadLine();
process.BeginOutputReadLine();
return started;
}
catch (Exception)
{
process.Dispose();
process = null;
throw;
}
}
}
void proc_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data != null)
{
// Fill in as appropriate.
Debug.WriteLine(string.Format("Error data received: {0}", e.Data));
}
}
void proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data == null)
return;
lock (locker)
{
output.Add(e.Data);
}
}
void proc_Exited(object sender, EventArgs e)
{
lock (locker)
{
var exited = Exited;
if (exited != null)
{
exited(this, new ProcessExitedEventArgs<TKey>(Key, output.ToArray()));
// Prevent memory leaks by removing references to listeners.
Exited -= exited;
}
}
var process = Interlocked.Exchange(ref this.process, null);
if (process != null)
{
process.OutputDataReceived -= proc_OutputDataReceived;
process.ErrorDataReceived -= proc_ErrorDataReceived;
process.Exited -= proc_Exited;
process.Dispose();
}
}
}
(此类还确保处理被处置。)然后按以下方式使用它:
public void LaunchProc(int itemId)
{
var launcher = new ProcessLauncher<int>("someapp.exe", "/id=" + itemId, itemId);
launcher.Exited += launcher_Exited;
launcher.Start();
}
void launcher_Exited(object sender, ProcessExitedEventArgs<int> e)
{
var itemId = e.Key;
var output = e.Output;
// Process output and associate it to itemId.
}
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句