或不!
我有一个相当简单的应用程序计时器程序。程序将启动一个用户选择(从文件对话框)可执行文件,然后在用户指定的分钟数后终止该过程。在测试期间,我发现当我调用Process.Kill()方法并且应用程序被最小化到系统托盘时发生崩溃。
有问题的可执行文件是我经常使用的Frap.exe,这也是我首先编写应用程序计时器的原因。我总是最小化到托盘的碎片,这是发生崩溃的时候。
我对Kill()的使用很直接......
while (true)
{
//keep checking if timer expired or app closed externally (ie. by user)
if (dtEndTime <= DateTime.Now || p.HasExited)
{
if (!p.HasExited)
p.Kill();
break;
}
System.Threading.Thread.Sleep(500);
}
在搜索以编程方式关闭外部应用程序的替代方法时,我发现只有Close()和Kill()(CloseMainWindow对我没有任何帮助)。我尝试使用Close(),它的工作原理是提供应用程序最小化托盘。如果应用程序最小化,Close()不会导致崩溃但应用程序仍保持打开和活动状态。 我在关于关闭外部应用程序的一些帖子帖子中注意到的一件事是评论:“就我个人而言,我试图找到一种更优雅的方式来关闭它。”在堆栈流程中找到的THIS线程中创建(对John没有冒犯)。事实上,我在一些网站上遇到了类似的评论,没有尝试过优雅或优雅(或无崩溃!!)的方法。
有什么建议吗?
经历的崩溃是不一致的,我没有提供细节。我无法使用VS2008进行调试,因为我收到消息 - 无法调试崩溃应用程序(或类似的东西),并且取决于我当时运行的其他程序,当Kill()被调用时,其中一些也崩溃(也是程序)只在托盘中运行)所以我认为这是与系统托盘特别相关的某种问题。
答案 0 :(得分:2)
您的代码是否可能以某种方式执行,以至于Kill()语句有时可能被调用两次?在Process.Kill()的文档中,它表示Kill异步执行。因此,当您调用Kill()时,将继续执行主线程。此外,文档声明如果您在已经处于关闭过程中的应用程序上调用它,Kill将抛出Win32Exception。文档声明您可以使用WaitForExit()等待进程退出。如果在调用Kill()之后立即调用WaitForExit()会发生什么。循环看起来没问题(使用break语句)。是否有可能让代码进入该循环两次?
如果这不是问题,也许还有另一种方法可以捕获该异常: 尝试挂钩AppDomain.CurrentDomain.UnhandledException事件 (currentDomain是静态成员)
问题是Kill是异步运行的,所以如果它抛出异常,它就会发生在另一个线程上。这就是你的异常处理程序没有捕获它的原因。进一步(我认为)未处理的异步异常(我相信你有)会立即卸载你的应用程序(这就是正在发生的事情)。
编辑:挂钩UnhandledExceptionEvent 的示例代码 这是一个简单的控制台应用程序,演示了如何使用AppDomain.UnhandledException:
using System;
public class MyClass
{
public static void Main()
{
System.AppDomain.CurrentDomain.UnhandledException += MyExceptionHandler;
System.Threading.ThreadPool.QueueUserWorkItem(DoWork);
Console.ReadLine();
}
private static void DoWork(object state)
{
throw new ApplicationException("Test");
}
private static void MyExceptionHandler(object sender, System.UnhandledExceptionEventArgs e)
{
// get the message
System.Exception exception = e.ExceptionObject as System.Exception;
Console.WriteLine("Unhandled Exception Detected");
if(exception != null)
Console.WriteLine("Message: {0}", exception.Message);
// for this console app, hold the window open until I press enter
Console.ReadLine();
}
}
答案 1 :(得分:1)
我的第一个想法是在Kill()调用周围放置一个try / catch块并记录你得到的异常(如果有的话)。它可能会让你知道什么是错的。类似的东西:
try
{
if(!p.HasExited)
{
p.Kill();
}
break;
}
catch(Exception ex)
{
System.Diagnostics.Trace.WriteLine(String.Format("Could not kill process {0}, exception {1}", p.ToString(), ex.ToString()));
}
答案 2 :(得分:0)
我不认为我应该声称这是“答案”,但它是一个体面的“解决方案”。将以下内容添加到代码行中......
p.WaitForInputIdle(10000);
am.hWnd = p.MainWindowHandle;
...停止了崩溃的问题。这些行紧跟在Process.Start()语句之后。这两行都是必需的,在使用它们时,我打开了一些其他问题的大门,我将在接下来的几天内进行调查。第一行只是一个长达10秒的等待,以使启动的进程“空闲”(即完成启动)。 am.hWnd是我的AppManagement类中IntPtr类型的属性,这是赋值双方的唯一用法。由于缺乏更好的解释,这两条线对于去抖动方法是有效的。
我稍微修改了while循环以允许调用CloseMainWindow()这似乎是更好的路径 - 尽管如果它失败了我然后Kill()应用程序:
while (true)
{
//keep checking if timer expired or app closed externally (ie. by user)
if (dtEndTime <= DateTime.Now || p.HasExited) {
try {
if (!p.HasExited) // if the app hasn't already exitted...
{
if (!p.CloseMainWindow()) // did message get sent?
{
if (!p.HasExited) //has app closed yet?
{
p.Kill(); // force app to exit
p.WaitForExit(2000); // a few moments for app to shut down
}
}
p.Close(); // free resources
}
}
catch { // blah blah }
break;
}
System.Threading.Thread.Sleep(500);
}
我获得MainWindowHandle的初衷是最小化/恢复应用程序,如果最小化,我可能会在不久的将来实现它。我决定看看其他程序是否像Fraps一样运行(即UI,但主要是在系统托盘中运行(如Yahoo等人的消息服务))。我用XFire测试过,我无能为力的东西会为MainWindowHandle返回一个值。无论如何,这是一个史无前例的问题,但我觉得很有趣。
PS。 JMarsch有点赞美,因为正是他的建议RE:Win32Exception实际上让我找到了这个工作 - 看起来不太可能。