C#进程不会停止执行
本文关键字:执行 进程 | 更新日期: 2023-09-27 17:59:43
我遇到一个C#2008 windows应用程序无法完成执行的问题,我正在尝试确定如何解决该问题。最初,编写了一个C#2010控制台应用程序来调用C#2008控制台应用程序,该应用程序反过来调用web服务。我把这两个应用程序都改成了windows应用程序,因为我不想让dos弹出窗口。
问题是被称为C#2008的windows应用程序永远无法完成执行。该过程将保留在内存中。
下面列出的代码是C#2010应用程序中代码的一部分。
private static Logger logger = LogManager.GetCurrentClassLogger();
try
{
Process eProcess = new Process();
strConsoleAppLocation = ConfigurationManager.AppSettings["client_location"];
String Process_Arguments = null;
eRPT_Process.StartInfo.UseShellExecute = false;
eRPT_Process.StartInfo.FileName = strConsoleAppLocation;
Process_Arguments = " 1 CLI";
eProcess.StartInfo.Arguments = Process_Arguments;
eProcess.Start();
eProcess.WaitForExit(1800);
Process_Arguments = null;
eProcess.StartInfo.UseShellExecute = false;
Process_Arguments = " 2 TIM";
eProcess.StartInfo.Arguments = Process_Arguments;
eProcess.Start();
eProcess.WaitForExit(1800);
eProcess.Dispose();
Process_Arguments = null;
}
catch (Exception e)
{
logger.Error(e.Message + "'n" + e.StackTrace);
}
我知道C#2008应用程序永远不会通过查看内存中的进程来完成。此外,如果我将代码行更改为以下内容:eProcess.WaitForExit();
,则应用程序永远不会返回到被调用的程序。
在C#2008调用的应用程序中,执行的最后一行代码如下:
Environment.Exit(1);
因此,为了解决这个问题,我有以下问题:
如果你对我如何更改上面列出的代码有建议,你会告诉我你的建议是什么吗?
由于这两个程序目前正在生产中,我想知道你是否对我如何解决这个"创可贴"修复问题有建议?有没有一种方法可以让我在C#2010程序完成执行时停止正在运行的C#2008进程?有没有一种方法可以让C#2008应用程序在完成执行后杀死自己的进程?如果是,你能给我看一下如何解决这个问题的代码吗?
对于长期修复,你能告诉我如何确定为什么C#2008进程没有停止,以及我如何修复它吗?我会使用profiler,但是我的公司只有visualstudio2010的专业版。你能告诉我你的建议是什么吗?
WaitForExit()
,即无限期等待它正在等待的进程结束,而WaitForExit(int milliseconds)
等待指定的持续时间,然后超时。
根据您所写的内容,您从C#2010程序启动的C#2008程序永远不会终止。这可能有几个原因。
-
它可能正在等待用户输入。
-
它可能会陷入无限循环。
-
如果它是多线程的,那么其中一个线程可能尚未完成执行,这将使进程保持活动状态(以防该线程未设置为后台线程)。
试着直接从命令行运行它,看看它在做什么。
如果从命令行执行C#2008程序时其行为正确/如预期,但从C#2010程序执行时其行为不同,则验证两种情况下的参数是否匹配。
您可以使用pskill终止正在运行的进程。你可以做一些类似的事情:
if (!process.WaitForExit(1800))
{
// launch a process for pskill to kill the C# 2008 program
}
最后,您可以调试正在运行的程序,方法是打开它的C#解决方案/项目,然后使用Attach to Process
命令,您可以在Visual Studio的Debug
菜单栏项下找到该命令。
-
不可以,不能直接在代码中键入pskill。您将不得不启动一个进程,就像您当前使用System.Diagnostics.process类为C#2008程序所做的那样。
-
你是对的,你将使用Visual Studio附加到进程,无论项目是在哪个版本中创建的。打开程序的解决方案后,单击
Debug'Attach to Process
。它将向您显示机器上正在运行的进程的列表。其中一列(通常是第一列)显示可执行文件的名称,该名称将与C#2008应用程序的名称相匹配。在可疑代码行上放置断点后,在列表中选择C#2008程序,然后单击Attach
按钮。 -
我不知道你的意思。
-
调试几乎是你能够弄清楚发生了什么的唯一方法
最后一件事我刚刚在重读你的问题时注意到了。您已将C#2008应用程序转换为Windows应用程序,对吗?这不是你想要的。Windows应用程序必须以某种方式终止,并且需要交互。您应该将两个应用程序转换回控制台应用程序,并确保在创建Process
对象以启动C#2008应用程序时,将ProcessStartInfo
参数的CreateNoWindow
属性设置为Process
构造函数的true。
所以,类似于:
public class Processor
{
private static Logger logger = LogManager.GetCurrentClassLogger();
private ProcessStartInfo MakeProcessStartInfo(string fileName, string arguments)
{
return new ProcessStartInfo
{
CreateNoWindow = true,
UseShellExecute = false,
FileName = fileName,
Arguments = appArguments
};
}
public void CallExternalApplications()
{
try
{
var fileName = ConfigurationManager.AppSettings["client_location"];
using (var process = new Process { StartInfo = MakeProcessStartInfo(fileName, " 1 CLI") })
{
process.Start();
if (!process.WaitForExit(1800))
{
// create a Process here for pskill to kill the "process" using process.Id.
}
}
using (var process = new Process { StartInfo = MakeProcessStartInfo(fileName, " 2 TIM") })
{
process.Start();
if (!process.WaitForExit(1800))
{
// create a Process here for pskill to kill the "process" using process.Id.
}
}
}
catch (Exception e)
{
// you really should be using logger.ErrorException(e.Message, e) here
// and be using the ${exception} layoutrenderer in the layout in the NLog.config file
logger.Error(e.Message + "'n" + e.StackTrace);
}
}
}
您可能可以进一步重构此代码,因为我正在重复创建process
对象的代码。我使用了using
块来隐式调用process
对象上的Dispose
;这使得代码更加清晰易读。
关键是设置ProcessStartInfo
对象的CreateNoWindow
属性,一旦将应用程序从windows应用程序转换回控制台应用程序,该属性将阻止控制台窗口弹出。
这是我向进程发送ctrl-c的解决方案。仅供参考,我从来没有得到process.WaitForExit
工作。
以下是我发现如何将CTRL-C发送到进程的方法,而不是使用GenerateConsoleCtrlEvent。仅供参考,在这种情况下,我根本不需要找到组进程ID。
using System;
using System.Diagnostics;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
public class ConsoleAppManager
{
private readonly string appName;
private readonly Process process = new Process();
private readonly object theLock = new object();
private SynchronizationContext context;
private string pendingWriteData;
public ConsoleAppManager(string appName)
{
this.appName = appName;
this.process.StartInfo.FileName = this.appName;
this.process.StartInfo.RedirectStandardError = true;
this.process.StartInfo.StandardErrorEncoding = Encoding.UTF8;
this.process.StartInfo.RedirectStandardInput = true;
this.process.StartInfo.RedirectStandardOutput = true;
this.process.EnableRaisingEvents = true;
this.process.StartInfo.CreateNoWindow = true;
this.process.StartInfo.UseShellExecute = false;
this.process.StartInfo.StandardOutputEncoding = Encoding.UTF8;
this.process.Exited += this.ProcessOnExited;
}
public event EventHandler<string> ErrorTextReceived;
public event EventHandler ProcessExited;
public event EventHandler<string> StandartTextReceived;
public int ExitCode
{
get { return this.process.ExitCode; }
}
public bool Running
{
get; private set;
}
public void ExecuteAsync(params string[] args)
{
if (this.Running)
{
throw new InvalidOperationException(
"Process is still Running. Please wait for the process to complete.");
}
string arguments = string.Join(" ", args);
this.process.StartInfo.Arguments = arguments;
this.context = SynchronizationContext.Current;
this.process.Start();
this.Running = true;
new Task(this.ReadOutputAsync).Start();
new Task(this.WriteInputTask).Start();
new Task(this.ReadOutputErrorAsync).Start();
}
public void Write(string data)
{
if (data == null)
{
return;
}
lock (this.theLock)
{
this.pendingWriteData = data;
}
}
public void WriteLine(string data)
{
this.Write(data + Environment.NewLine);
}
protected virtual void OnErrorTextReceived(string e)
{
EventHandler<string> handler = this.ErrorTextReceived;
if (handler != null)
{
if (this.context != null)
{
this.context.Post(delegate { handler(this, e); }, null);
}
else
{
handler(this, e);
}
}
}
protected virtual void OnProcessExited()
{
EventHandler handler = this.ProcessExited;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
protected virtual void OnStandartTextReceived(string e)
{
EventHandler<string> handler = this.StandartTextReceived;
if (handler != null)
{
if (this.context != null)
{
this.context.Post(delegate { handler(this, e); }, null);
}
else
{
handler(this, e);
}
}
}
private void ProcessOnExited(object sender, EventArgs eventArgs)
{
this.OnProcessExited();
}
private async void ReadOutputAsync()
{
var standart = new StringBuilder();
var buff = new char[1024];
int length;
while (this.process.HasExited == false)
{
standart.Clear();
length = await this.process.StandardOutput.ReadAsync(buff, 0, buff.Length);
standart.Append(buff.SubArray(0, length));
this.OnStandartTextReceived(standart.ToString());
Thread.Sleep(1);
}
this.Running = false;
}
private async void ReadOutputErrorAsync()
{
var sb = new StringBuilder();
do
{
sb.Clear();
var buff = new char[1024];
int length = await this.process.StandardError.ReadAsync(buff, 0, buff.Length);
sb.Append(buff.SubArray(0, length));
this.OnErrorTextReceived(sb.ToString());
Thread.Sleep(1);
}
while (this.process.HasExited == false);
}
private async void WriteInputTask()
{
while (this.process.HasExited == false)
{
Thread.Sleep(1);
if (this.pendingWriteData != null)
{
await this.process.StandardInput.WriteLineAsync(this.pendingWriteData);
await this.process.StandardInput.FlushAsync();
lock (this.theLock)
{
this.pendingWriteData = null;
}
}
}
}
}
然后,在实际运行过程中,并在我的主应用程序中发送CTRL-C
DateTime maxStartDateTime = //... some date time;
DateTime maxEndDateTime = //... some later date time
var duration = maxEndDateTime.Subtract(maxStartDateTime);
ConsoleAppManager appManager = new ConsoleAppManager("myapp.exe");
string[] args = new string[] { "args here" };
appManager.ExecuteAsync(args);
await Task.Delay(Convert.ToInt32(duration.TotalSeconds * 1000) + 20000);
if (appManager.Running)
{
// If stilll running, send CTRL-C
appManager.Write("'x3");
}
有关详细信息,请参阅重定向控制台应用程序的标准输入和Windows如何获取已运行进程的进程组?
这就像一个符咒
我使用的方法不是process.WaitForExit()
:
while (!process.StandardOutput.EndOfStream)
{
Console.WriteLine(process.StandardOutput.ReadLine());
}