2010-05-05 88 views
2

我正在使用重定向的I/O与模拟器可执行文件进行通信,从VB.NET GUI控制一个旧的FORTRAN模拟器。 GUI弹出一个带有进度条,估计时间和“停止”按钮的“状态”窗口(Button_Stop)。.NET Process.Kill()以安全的方式

现在,我想Button_Stop立即终止仿真程序。执行此操作的显而易见的方法是在Child Process对象上调用Kill()。如果在进程退出后这样做会发出异常,但是我可以在尝试终止进程之前测试该进程是否已退出,对不对?

OK,所以我做的是单击按钮时以下几点:

If Not Child.HasExited Then 
    Child.Kill() 
    Button_Stop.Enabled = False 
End If 

但是,如果过程发生的测试和调用Kill()之间退出呢?在那种情况下,我得到一个例外。

发生在我接下来的事情是,我可以在Process.Exited事件处理程序做Button_Stop.Enabled = False,从而防止Button_Stop.Clicked处理程序Child.Kill()电话。但由于Process.Exited处理程序被调用在不同的线程中,仍有以下可能的交织:

  1. 子进程退出。
  2. Process.Exited火灾,呼吁Invoke安排上Button_StopButton_Stop.Enabled = False
  3. 用户点击,引发Child.Kill()
  4. Button_Stop.Enabled = False实际情况。

一个例外然后一步抛出3.

如何杀过程中没有任何竞争条件?我在想这个完全错误吗?

回答

3

只需捕获异常,并关闭按钮finally

Try      
    Child.Kill() 
Catch ex As Exception 
    MsgBox(ex.ToString()) 
Finally 
    Button_Stop.Enabled = False 
End Try 

相反捕获所有类型的异常的它当然会更好,只赶上InvalidOperationExceptionWin32Exception这些过程是否终止抛出或已经退出。

如果程序中发生异常,并且您应该设计程序以避免异常,您可能认为这是一件“坏事”。但是,有不同类型的异常和异常处理,其中一些是不好的设计决定,而另一些(如本文所述)则是强制性的,因为异常(即终止另一个进程)的原因超出了您的控制范围。

如果你想继续读下去,我建议你在不同类型的异常埃里克Lipperts帖子:

Fabulous Adventures In Coding: Vexing exceptions

+0

这将工作,但它似乎很尴尬,因为它s意味着在地板上抛出异常。构建在正常操作中永远不会遇到异常的程序不是很好的做法吗? – 2010-05-05 21:40:48

+0

@Orbode:你可能看起来很尴尬,但是这样的外部异常应该使用try-catch块来处理。进一步阅读:http://blogs.msdn.com/ericlippert/archive/2008/09/10/vexing-exceptions.aspx – 2010-05-05 21:42:18

+0

@Orborde这只是推荐的事情,如果你捕捉.NET异常。在这种情况下,您正在捕获非托管的异常,因此可以捕获所有内容 – 2010-05-05 21:47:57

1

您可以P/Invoke到TerminateProcess它不会抛出,如果该过程已经退出:

Sub Main() 
    Dim p = Process.Start("C:\Windows\system32\notepad.exe") 
    Thread.Sleep(1000) 
    TerminateProcess(p.Handle, 0) 
    TerminateProcess(p.Handle, 0) ''# This call won't throw, it will just fail silently. 
End Sub 

<DllImport("kernel32.dll", SetLastError:=True)> 
Private Function TerminateProcess(ByVal hProcess As IntPtr, ByVal uExitCode As UInteger) As <MarshalAs(UnmanagedType.Bool)> Boolean 
End Function 
+0

实际上,如果返回值为零或进程句柄不再有效,则Process.Kill()简单地调用TerminateProcess并抛出。但是,不需要像样例中那样调用TerminateProcess两次。 – 2010-05-05 21:53:11

+1

没错,只是证明它不会抛出:)编辑清晰。 – 2010-05-05 21:54:31