我正在开发一个.NET控制台应用程序,需要在退出时清理资源。我遇到的问题是,如果通过控制台窗口[X],通过任务管理器/进程资源管理器或使用WM_CLOSE以编程方式关闭cmd父级,则不会收到任何通知。我无法处理来自Task Mgr的 Kill Process 。或ProcExp。父控制台的WM_CLOSE是此应用程序在完成处理之前关闭的最可能方式。
以下是我到目前为止尝试注册的事件:
AppDomain.CurrentDomain.ProcessExit += CurrentDomainProcessExit;
AppDomain.CurrentDomain.UnhandledException += CurrentDomainUnhandledException;
Console.CancelKeyPress += ConsoleCancelKeyPress;
Application.ApplicationExit += ApplicationApplicationExit;
Process parentProcess = ProcessInfo.GetParentProcess(
Process.GetCurrentProcess());
parentProcess.Disposed += ParentDisposed;
parentProcess.Exited += ParentExited;
Process grandParentProcess = ProcessInfo.GetParentProcess(parentProcess);
grandParentProcess.Disposed += GrandParentDisposed;
grandParentProcess.Exited += GrandParentExited;
当我从控制台发送CTRL + C或应用程序不间断完成时,这些事件会正常触发。但是,当父应用程序(cmd控制台)关闭时,它们都不会触发。 (父母/祖父母的过程不是CLR所以我不确定我会收到那些Disposed / Exited事件。那些只是在黑暗中拍摄。)我看了一些pInvoke的东西,但我宁愿不下去如果.NET是一个选项,那条道路。
在这些情况下,有没有办法检测和处理关机?我对任何.NET,pInvoke / Win32 / C / C ++解决方案都持开放态度。 (基本上任何方式都可以在Windows平台上完成。)
感谢。
P.S。我仍在使用.NET 2.0,所以我不能使用.NET 3.0 +中引入的任何东西
答案 0 :(得分:2)
你最好的选择是使用P / Invoke。 Windows API函数SetConsoleCtrlHandler()
可能会达到预期效果。
以下示例代码来自here(MSDN上提供的类似代码,here)。
class Program
{
[DllImport("Kernel32")]
public static extern bool SetConsoleCtrlHandler(HandlerRoutine Handler, bool Add);
// A delegate type to be used as the handler routine
// for SetConsoleCtrlHandler.
public delegate bool HandlerRoutine(CtrlTypes CtrlType);
// An enumerated type for the control messages
// sent to the handler routine.
public enum CtrlTypes
{
CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT,
CTRL_CLOSE_EVENT,
CTRL_LOGOFF_EVENT = 5,
CTRL_SHUTDOWN_EVENT
}
private static bool ConsoleCtrlCheck(CtrlTypes ctrlType)
{
// Put your own handler here
return true;
}
static void Main(string[] args)
{
SetConsoleCtrlHandler(new HandlerRoutine(ConsoleCtrlCheck), true);
}
}
答案 1 :(得分:2)
每当您在Exited
上设置EnableRaisingEvents = true
时,Process
事件都会触发,无论该进程是.Net,本机还是其他任何内容。如果您实际上在没有Process
事件触发的情况下看到Exited
退出,您是否可以发布小型复制方案?
此外,Disposed
事件对您的方案毫无用处。它仅在您在进程中的Dispose()
对象实例上调用Process
时触发,并且与对象引用的OS进程中发生的任何事情无关。
答案 2 :(得分:0)
您的部分问题可能是示例中的ProcessInfo
类是System.Web
命名空间的一部分。它(引用MSDN)“返回有关在ASP.Net Process Model下运行的ASP.Net工作进程的信息。”这似乎不太可能返回任何对您的命令行应用程序非常有用的东西。
您需要通过System.Management命名空间使用WMI。下面的示例应该可以解决这个问题,并为当前进程的直接父级获取System.Diagnostics.Process对象。此示例使用Process.WaitForExit()
方法,但连接事件处理程序也应该有效。
但是,您应该注意,特别是在您谈论控制台应用程序时,直接父进程不必然是实际产生当前进程的进程。如果进程A直接生成进程控制台应用程序B,ProcessStartInfo
指定UseShellExecute=false
,则A将是B的直接父进程。但是,如果进程A生成进程B(控制台应用程序){{1} }指定ProcessStartInfo
,然后进程A将不作为进程B的直接父进程:A和B之间将有一个中间进程(UseShellExecute=true
)。如果你'涉及* .cmd批处理文件或PowerShell代码......可能会更复杂。
因此,您可能需要进一步运行流程树以找到感兴趣的父级。
此外,由于您没有生成父进程,因此在完成后您将无法访问父进程的条件(退出)代码。尝试访问父进程的ExitCode属性会引发无效的操作异常。
cmd.exe