处理Windows应用程序的结束过程

是否可以在同一个Windows应用程序本身内捕获Windows应用程序的任务管理器结束进程? 我正在使用C#2.0 win应用程序,并且我希望在结束进程发生时进行一些数据库处理(在数据库中将标志从“Y”更改为“N”)。

不,不可能挂钩操作系统结束进程的决定。 注意,这不是由任务管理器完成的,结束一个进程是内核的责任。

你需要在这里做两件事:

  1. 将事件处理程序连接到告诉应用程序退出的普通用户界面消息。 使用这些事件来保存数据,释放资源,以及以其他方式彻底退出。
  2. 尽可能处理exception以捕获错误并清理和保存数据。

以下是Raymond博客的三个链接,解释了为什么你不能按照你的要求去做。

  • 为什么不能捕获TerminateProcess?
  • 为什么有些进程在被杀后会留在任务管理器中?
  • 计划和用户之间的军备竞赛

此外,我在这里解决了类似的StackOverflow问题。

稍微不同的方法怎么样:

让您的应用程序在运行时经常更新日期时间字段,例如LastPollDate,并且有一个单独的字段,例如“AppTerminatedNormally”,您设置为N,如果您收到表单关闭事件,则更改为Y.

如果应用程序被任务管理器杀死,则日期将不再更新,并且您的AppTerminatedNormally仍然是否定的。

这样,您可以运行查询,查找LastPollDate超过10分钟且AppTerminatedNormally为N的所有行,并且您将拥有exception终止的所有会话。

你们都会在这篇文章中吐痰,但是这里……

你试图在错误的级别解决问题(即当内核杀死应用程序时在你的应用程序中运行代码)。 真正的问题是确保数据库正确反映其客户端应用程序的存在(或不存在)。

要解决此问题,请避免允许应用程序在用户交互之间处于“不一致”状态。 换句话说,不要启动无法快速提交的事务,不要将数据写入使文件处于半写或不可读状态的文件,并且不要将应用程序外部的资源保留为不一致在用户交互之外的状态。 换句话说,如果您的应用程序没有忙于响应事件处理程序,它应该准备好立即关闭。

如果您遵循上述惯例,您会发现在终止之前需要“快速清理”的情况很少。 在用户单击“确定”或“保存”等的交互之外,编写良好的应用程序应能够立即终止,而不会对其数据存储造成任何持久性损坏或损坏。

如果您必须在退出时在数据库中设置一个标志(这通常是用于检测用户是否登录的模式),请考虑以下任一选项:

  1. 定期(可能每30秒一次)在数据库中插入/更新类似时间戳的字段,以指示应用程序最近在线的时间。 其他应用程序可以检查这些时间戳以确定最近另一个应用程序在线的时间…如果该值在最近30秒内,则另一个应用程序仍然是opnline。

  2. 正如Woodhenge正确建议的那样,创建一个单独的进程(理想情况下是一个服务)来监视主应用程序的状态。 可以将Windows服务配置为在服务发生故障时自动重新启动。 然后,此监视进程将向数据库发出时间戳。

请注意,上述两个建议都解决了实际问题(检测应用程序是否正在访问数据库),而不会使数据库处于“不一致状态”(当应用程序实际死亡时,上述标志为“Y”且标志应为“N”)。

如果您的目标是Windows Vista(或更高版本),您可能会对RegisterApplicationRecoveryCallback API感兴趣…

http://msdn.microsoft.com/en-us/library/aa373345.aspx

它允许您在应用程序中指定一个回调例程,该例程将在进程即将崩溃时调用。 注意,它仅用于崩溃,如果进程被故意杀死,则不会自动调用。

你可以从C#调用这个API(我已经完成了),但请记住,当你的回调被调用时,你的应用程序已处于非常糟糕的状态,你可以对你的记忆状态作出很少的假设。 如果你想在这个例程中使用任何内存数据,我会把它放在一个非常通用的静态范围内,这样你就有可能在你的回调例程中没有“整理”它运行。

还有一些与此相关的有趣API,允许您在失败后自动重启应用程序等。

您可以做的是获取进程ID并监视进程,您可以使用HasExited属性来检查进程是否已结束。 下面是一个快速的VB代码(对不起,我现在没有VS.这是我在另一个论坛上写的)

Public Class Form1 Dim p As ProcessStartInfo Dim process As Process Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click p = New ProcessStartInfo("iexplore.exe") process = process.Start(p) End Sub Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click MsgBox(process.Id) If process.HasExited Then MsgBox("yes") Else MsgBox("no") End If End Sub End Class 

上面的代码启动Internetexplorer,按钮检查进程是否结束。 您可以使用类似的过程来获取所有正在运行的进程并使用processID。

我不认为这可以从应用程序中做到。 结束任务的目的是立即停止该过程。 这不允许执行任何清理代码。

Shoban指出了另一种实现目标的方法。 另一个应用程序或服务需要查找主进程。 当其他进程找不到主进程时,您可以在此时进行数据库处理。