C# 处理 Windows 应用程序的结束进程
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/838261/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Handling end process of a windows app
提问by SO User
Is it possible to capture the task manager end process of a windows application within the same windows application itself? I am using a C# 2.0 win app and I would like to do some database processing (change a flag from 'Y' to 'N' in the DB) when an end process happens.
是否可以在同一 Windows 应用程序本身中捕获 Windows 应用程序的任务管理器结束进程?我正在使用 C# 2.0 win 应用程序,我想在结束进程发生时进行一些数据库处理(将 DB 中的标志从“Y”更改为“N”)。
采纳答案by Foredecker
No, it is not possible to hook the operating system's decision to end a process. Note, this is not done by task manger, ending a process is the responsibility of the kernel.
不,不可能挂钩操作系统结束进程的决定。请注意,这不是由任务管理器完成的,结束进程是内核的责任。
You will need to do two things here:
您需要在这里做两件事:
- Connect event handlers to the normal user interface messages that tell a application to exit. Use these events to persist data, free resources, and otherwise exit cleanly.
- Handle exceptions as appropriate to catch errors and clean up and save data if possible.
- 将事件处理程序连接到告诉应用程序退出的普通用户界面消息。使用这些事件来持久化数据、释放资源,并以其他方式干净地退出。
- 酌情处理异常以捕获错误并在可能的情况下清理和保存数据。
Here are a three links to Raymond's blog explaining why you cannot do what you are asking.
下面是 Raymond 博客的三个链接,解释了为什么你不能做你所要求的。
- Why can't you trap TerminateProcess?
- Why do some process stay in Task Manager after they've been killed?
- The arms race between programs and users
Also, I addressed a similar StackOverflow question here.
另外,我在这里解决了一个类似的 StackOverflow 问题。
回答by Shoban
What you can do is get the Process ID and monitor the process and you can use HasExited property to check whether the process has end or not. Below is a quick VB code (Excuse me I dont have VS now. This was written by me in another forum)
您可以做的是获取进程 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
Above code starts Internetexplorer and the button checks whether the process has end or not. You can use a similar process to get all running process and use the processID.
上面的代码启动 Internetexplorer,按钮检查进程是否结束。您可以使用类似的进程来获取所有正在运行的进程并使用 processID。
回答by jhamm
I do not think this is possible to do from within the application. The purpose of End Task is to immediately stop the process. This does not allow any clean up code to execute.
我认为这不可能在应用程序中完成。End Task 的目的是立即停止进程。这不允许执行任何清理代码。
Shoban pointed out another way to accomplish your goal. Another application or service would need to look for the main process. When the other process cannot find the main process, you could do the database processing at that point.
Shoban 指出了另一种实现目标的方法。另一个应用程序或服务需要寻找主进程。当其他进程找不到主进程时,您可以在此时进行数据库处理。
回答by Bork Blatt
How about a slightly different approach:
稍微不同的方法怎么样:
Have your application update a date time field e.g. LastPollDate every so often while it is running, and have a separate field e.g. "AppTerminatedNormally" which you set to N, and change to Y if you get a form close event.
让您的应用程序在运行时每隔一段时间更新一个日期时间字段,例如 LastPollDate,并有一个单独的字段,例如您设置为 N 的“AppTerminatedNormally”,如果您收到表单关闭事件,则更改为 Y。
If the app is killed via Task Manager, the date will not be updated any more, and your AppTerminatedNormally will still be no.
如果应用程序通过任务管理器被杀死,日期将不再更新,并且您的 AppTerminatedNormally 仍然是 no。
This way you could run a query that finds all rows where LastPollDate is older than 10 minutes and AppTerminatedNormally is N, and you would have all the sessions that were abnormally terminated.
通过这种方式,您可以运行查询来查找 LastPollDate 早于 10 分钟且 AppTerminatedNormally 为 N 的所有行,并且您将拥有所有异常终止的会话。
回答by Mark
You're all gonna spit at this post, but here goes...
你们都会吐槽这篇文章,但这里...
You're trying to solve the problem at the wrong level (i.e. running code in your app when the kernal is killing the app). The real problem is about ensuring that the database correctly reflect the presence (or absence) of it's client application/s.
您试图在错误的级别解决问题(即,当内核杀死应用程序时,在您的应用程序中运行代码)。真正的问题是确保数据库正确反映其客户端应用程序的存在(或不存在)。
To solve this, avoid allowing applications to be in an "incongruent state" between user interactions. In other words, don't start transactions that you can't commit quickly, don't write data to files that leaves the file in a half-written or unreadable state, and don't hold resources in external to your application an incongruent state outside of user interactions. Put differently, if your app isn't busy responding to an event handler, it should be ready to close immediately.
要解决此问题,请避免让应用程序在用户交互之间处于“不一致状态”。换句话说,不要启动您不能快速提交的事务,不要将数据写入文件,从而使文件处于半写入或不可读状态,并且不要在应用程序外部持有不一致的资源用户交互之外的状态。换句话说,如果您的应用程序不忙于响应事件处理程序,它应该准备好立即关闭。
If you follow the above practise, you'll find very few scenarios where you need to "quickly clean up" before terminating. Outside of interactions where a user clicks "OK" or "Save", etc. a well written application should be able to survive immediate termination without any lasting damage or corruption of it's data stores.
如果你按照上面的做法,你会发现很少有需要在终止前“快速清理”的场景。在用户单击“确定”或“保存”等交互之外,编写良好的应用程序应该能够在立即终止后继续存在,而不会对其数据存储造成任何持久损坏或损坏。
If you absolutely have to set a flag in the database upon exit (which sounds typical of a pattern used to detect whether a user is logged in or not), then consider either of the following alternatives:
如果您绝对必须在退出时在数据库中设置一个标志(这听起来是用于检测用户是否登录的典型模式),请考虑以下任一替代方案:
Periodically (perhaps once every 30 seconds) insert/update a timestamp-like field in the database, to indicate how recently an application was online. Other applications can inspect these timestamps to determine how recently another application was online... if the value is within the last 30 seconds, the other app is still opnline.
As Woodhenge rightly suggested, create a seperate process (ideally a service) to monitor the status of the main application. Windows services can be configured to automatically restart in the event of a failure of the service. This monitoring process will then issue timestamps to the database.
定期(可能每 30 秒一次)在数据库中插入/更新类似时间戳的字段,以指示应用程序最近多久在线。其他应用程序可以检查这些时间戳以确定另一个应用程序最近多久在线……如果该值在过去 30 秒内,则另一个应用程序仍然处于在线状态。
正如 Woodhenge 正确建议的那样,创建一个单独的进程(最好是一个服务)来监视主应用程序的状态。Windows 服务可以配置为在服务发生故障时自动重新启动。然后,此监视过程将向数据库发出时间戳。
Notice that both of the above suggestions solve the real problem (detecting whether applications are accessing the database) without ever leaving the database in an "incongruent state" (the aforementioned flag is "Y" when the application is actualy dead and the flag should be "N").
请注意,上述两个建议都解决了真正的问题(检测应用程序是否正在访问数据库),而不会使数据库处于“不一致状态”(当应用程序实际上已死时,上述标志为“Y”,标志应该是“N”)。
回答by Martin
If you're targeting Windows Vista (or above) you might be interested in the RegisterApplicationRecoveryCallback API...
如果您的目标是 Windows Vista(或更高版本),您可能会对 RegisterApplicationRecoveryCallback API 感兴趣...
http://msdn.microsoft.com/en-us/library/aa373345.aspx
http://msdn.microsoft.com/en-us/library/aa373345.aspx
It allows you to specify a callback routine in your app that will be invoked when the process is about to crash. N.B. it is only for crashes, and won't automatically be called if the process is killed deliberately.
它允许您在应用程序中指定一个回调例程,该例程将在进程即将崩溃时调用。注意它仅用于崩溃,如果进程被故意杀死,则不会自动调用。
You can p/invoke to this API from C# (I have done it), but bear in mind that when your callback is invoked your app is already in a very bad state, and you can make very few assumptions about the state of your memory. If you have any in-memory data that you want to use in this routine, I would put it in a static at a very general scope so that you have the best possible chance of it not having been "tidied up" when your callback routine runs.
你可以从 C# 调用这个 API(我已经做到了),但请记住,当你的回调被调用时,你的应用程序已经处于非常糟糕的状态,你可以对你的内存状态做出很少的假设. 如果您有任何要在此例程中使用的内存中数据,我会将它放在一个非常通用的范围内的静态数据中,以便您在回调例程中最有可能不会“整理”它运行。
There are some other interesting APIs, related to this one, that allow you automatically restart your app after a failure, etc.
还有一些其他有趣的 API,与此相关,允许您在失败后自动重新启动应用程序等。