• .NET(C#):谈谈各种结束进程的方法


    返回目录

    Process类的CloseMainWindow, Kill, Close

    Process.CloseMainWindow 是GUI程序的最友好结束方式,从名字上就可以看出来它是通过关闭程序主窗体,相当于用户点击窗体的关闭按钮或者按Alt + F4。它的本质就是向主窗体发送WM_CLOSE消息(Process.MainWindowsHandle可以返回主窗体的句柄)。这个可以在.NET Framework源代码中看出来:

            publicbool CloseMainWindow()

            {

                IntPtr mainWindowHandle =this.MainWindowHandle;

                //句柄是否为0

                if (mainWindowHandle ==IntPtr.Zero)

                {

                    returnfalse;

                }

                //GetWindowLong是否成功执行

                if ((NativeMethods.GetWindowLong(newHandleRef(this, mainWindowHandle), -16) &0x8000000) !=0)

                {

                    returnfalse;

                }

                //0x10 是 WM_CLOSE消息

                //向主窗体发送WM_CLOSE,注意是PostMessage而不是SendMessage

                NativeMethods.PostMessage(newHandleRef(this, mainWindowHandle), 0x10, IntPtr.Zero, IntPtr.Zero);

                returntrue;

            }

    CloseMainWindow方法使用PostMessage(不是SendMessage,所以消息会加在消息队列的最后)方法向主窗体发送一个WM_CLOSE消息,这样等主窗体处理完所有消息后,等遇到WM_CLOSE便开始执行退出动作。

    比如记事本接到了WM_CLOSE消息但是有未保存的文件,记事本会弹出对话框提示用户保存还是不保存还是取消退出操作。Windows Forms和WPF的窗体都会有类似操作,通过窗体的Closing事件来在WM_CLOSE消息接收后做出是否退出的决定。

    之后我们会讲到Windows Forms和WPF都有自己的友好型常规退出方式,但是其实有一个通用的GUI程序退出方式,就是利用这个CloseMainWindow方法:

                //Windows Forms和WPF都可以用

                //Windows Forms的Form.Closing事件会在之后发生

                //WPF的Windows.Closing事件也会

                Process.GetCurrentProcess().CloseMainWindow();

    接下来就是Process.Kill方法,从名字也可以看出来,直接杀掉,不给任何喘息机会呵呵。Kill方法会直接结束整个进程,不进行常规资源清理(什么finally块等……)。Kill本质调用本地API:TerminateProcess函数。

    最后一个是Process.Close方法。抱歉它根本不是用来结束进程的!这个方法名字有些误导,其实则不然。它仅仅是IDisposable的Dispose方法的具体执行,用来进行Process类的托管资源清理的!

    由 于Process类继承自Component类,后者继承IDisposable而同时又有析构函数,而通过一个继承类可改写的Dispose方法(参数 是bool disposing)来判断这个Dispose是用户调用还是GC调用。而这个Process.Close()方法正是用户调用Dispose时进行托管 资源的清理方法:

    下面Process.Dispose方法代码:

            protectedoverridevoid Dispose(bool disposing)

            {

                if (!this.disposed)

                {

                    if (disposing)

                    {

                        //用户调用,清理托管资源

                        this.Close();

                    }

                    this.disposed =true;

                    //调用Component的Dispose

                    base.Dispose(disposing);

                }

            }

    可见这个Close方法类似很多其他.NET中的类的Close,比如Stream……因此Close肯定不会结束进程,仅仅是Process类作为IDisposable接口的间接继承者的自我清理方法。

    返回目录

    Environment类的Exit和FailFast

    Environment.Exit相当于在Main函数中的return指令。不过它不会执行代码块的finally块(如果有的话),但资源清理还是要进行的。

    它是最常见的退出当前进程的方法之一。在Main函数中我们可以直接return语句便退出了程序。如果不在Main函数内,那么Environment.Exit方法就可以派上用场:

        classa

        {

            ~a()

            {

                Console.WriteLine("析构函数");

            }

        }

        classProgram

        {

            staticvoid Main()

            {

                try

                {

                    a oa =newa();

                    test();

                }

                finally

                {

                    //这段代码永远不会执行

                    Console.WriteLine("finally");

                }

            }

            staticvoid test()

            {

                Environment.Exit(0);

            }

        }

    代码将会输出:

    析构函数

    看来GC调用了oa的析构函数,但注意finally块没有运行。

    Environment.FailFast方法更速度,它甚至不需要向操作系统返回进程退出代码(ExitCode),直接结束当前进程并在应用程序事件薄中写入信息,用于程序出现致命错误需要立即停止。

        classa

        {

            ~a()

            {

                Console.WriteLine("析构函数");

            }

        }

        classProgram

        {

            staticvoid Main()

            {

                try

                {

                    a oa =newa();

                    Environment.FailFast("致命错误发生!");

                }

                finally

                {

                    //这段代码永远不会执行

                    Console.WriteLine("finally");

                }

            }

        }

    在.NET 4.0下,Environment.FailFast代码会抛出FatalExecutionEngineError,而在4.0之前会抛出 ExecutionEngineException。但都不会有任何输出(GC没有清理对象,同时finally块也没有运行)

    返回目录

    WPF的Shutdown和Windows Forms的Exit

    GUI 程序往往都有自己的消息队列和事件管理模式,因此结束一个GUI程序要远复杂与结束一个控制台程序。上述的方法中,Process.Kill和 Environment.Exit和FailFast如果用在一个GUI程序中,都会直接强制结束整个程序,而不会激发GUI窗体的一些针对应用程序结束 的事件(比如Closing事件)。而上面也讲过:Process.CloseMainWindow通过向主窗体发送一个WM_CLOSE消息可以很好的 结束一个GUI程序,不过往往更自然的方法是利用GUI框架本身提供的结束程序的方法。

    WPF中是 System.Windows.Application.Shutdown方法,它其实就是在当前线程的消息队列Dispatcher对象中加入一个正常 优先级(DispatcherPriority.Normal)的回调退出函数,等消息队列最后处理到该项时程序开始退出操作。通常这样使用:

                //或者App也可以,WPF程序默认会有一个App类继承Application类

                Application.Current.Shutdown();

    Windows Forms中是:System.Windows.Forms.Application.Exit方法。它是通过 Application.OpenFormsInternal属性先把已经打开的窗体通过正常方式都关闭(运行Form.Closing事件),最后再结 束整个应用程序进程。

    最后,通过WPF的Window.Closing或Windows Forms的Form.Closing事件都可以取消这种形式的退出操作。

    返回目录

    非托管的ExitProcess和TerminateProcess

    这 是Windows API中结束进程的非托管方法。ExitProcess结束进程更友好些,而TerminateProcess会立即强制结束进程。两者的关系有点像 Environment.Exit和FailFast,但我不确定本质上是否一样。而且TerminateProcess可以指定进程返回值,但 FailFast不可以。两个非托管API的执行都不回运行finally块。

    使用起来很简单(关键是P/Invoke,参考:http://www.pinvoke.net,很有用的)

        using System.Runtime.InteropServices;

        classProgram

        {

            [DllImport("kernel32.dll")]

            staticexternvoid ExitProcess(uint uExitCode);

            [DllImport("kernel32.dll", SetLastError =true)]

            [return: MarshalAs(UnmanagedType.Bool)]

            staticexternbool TerminateProcess(IntPtr hProcess, uint uExitCode);

            staticvoid Main()

            {

                ExitProcess(1);

                //或者

                TerminateProcess(Process.GetCurrentProcess().Handle, 1);

            }

        }

    返回目录

    手动发送WM_CLOSE,WM_DESTROY,WM_QUIT消息

    在 一个GUI程序运行环境下,我们通过得到窗体的句柄,然后便可以向该句柄发送消息,WndProc(Window Procedure)函数会处理相应的事件。其中WM_CLOSE相当于用户点击关闭按钮,使用PostMessage将WM_CLOSE发送至主窗体等 价于.NET中Process类的CloseMainWindow方法,当接收到WM_CLOSE消息时,应用程序是可以选择是否真正结束程序的,如果继 续结束程序而不取消。接着WM_DESTROY消息会发送,这个消息代表着窗体开始真正关闭,此时可以进行一些资源的清理。最后当前线程接收到 WM_QUIT消息,线程的消息循环会被终止。

    因此向窗体发送这3个消息,只有WM_CLOSE会引发Closing事件,属于正常窗体退出逻辑,其他两个中消息会直接强行关闭窗体。

    注意WM_QUIT消息只能用PostMessage将其送至消息队列尾部,使用SendMessage立即发送在WPF应用程序上运行后程序没有任何反应。

    下面是一个WPF程序发送下列消息,(并没有贴XAML,你一定知道怎样加3个按钮然后把Click事件和窗体的Closing事件绑在代码上吧)

    using System;

    using System.Collections.Generic;

    using System.Linq;

    using System.Text;

    using System.Windows;

    using System.Windows.Controls;

    using System.Windows.Data;

    using System.Windows.Documents;

    using System.Windows.Input;

    using System.Windows.Media;

    using System.Windows.Media.Imaging;

    using System.Windows.Navigation;

    using System.Windows.Shapes;

    //外加命名空间

    using System.Diagnostics;

    using System.Runtime.InteropServices;

    namespace Mgen.TEX

    {

        publicpartialclassMainWindow : Window

        {

            public MainWindow()

            {

                InitializeComponent();

            }

            //Windows消息值

            constuint WM_CLOSE =0x10;

            constuint WM_DESTROY =0x02;

            constuint WM_QUIT =0x12;

            //SendMessage和PostMessage的P/Invoke

            [DllImport("user32.dll", CharSet =CharSet.Auto)]

            staticexternIntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

            [return: MarshalAs(UnmanagedType.Bool)]

            [DllImport("user32.dll", SetLastError =true)]

            staticexternbool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

            //窗体的Closing事件,判断Closing是否被运行

            privatevoid Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)

            {

                MessageBox.Show("Closing事件!");

            }

            //发送三种消息

            privatevoid WM_CLOSE_Click(object sender, RoutedEventArgs e)

            {

                //也可以用PostMessage

                SendMessage(Process.GetCurrentProcess().MainWindowHandle, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);

            }

            privatevoid WM_DESTROY_Click(object sender, RoutedEventArgs e)

            {

                //也可以用PostMessage

                SendMessage(Process.GetCurrentProcess().MainWindowHandle, WM_DESTROY, IntPtr.Zero, IntPtr.Zero);

            }

            privatevoid WM_QUIT_Click(object sender, RoutedEventArgs e)

            {

                //只能使用PostMessage去将WM_QUIT送至消息队列尾部

                PostMessage(Process.GetCurrentProcess().MainWindowHandle, WM_QUIT, IntPtr.Zero, IntPtr.Zero);

            }

        }

    }

    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    OneSQL安装
    Dropbox可伸缩性设计最佳实践分享
    软件开发实践的24条军规
    最精彩的英语学习经验总结:俺的英语之路
    Facebook和Google如何激发工程师的创造力
    十种更好的表达“你的代码写的很烂”的方法
    一次java程序的重构
    漂亮代码
    一段代码引发的思考
    最难忘的Bug调试经历
  • 原文地址:https://www.cnblogs.com/garfield211/p/2299221.html
Copyright © 2020-2023  润新知