Process類的CloseMainWindow, Kill, Close:
1.Process.CloseMainWindow是GUI程序的最友好結束方式,從名字上就可以看出來它是通過關閉程序主窗體,相當於用戶點擊窗體的關閉按鈕或者按Alt + F4。它的本質就是向主窗體發送WM_CLOSE消息(Process.MainWindowsHandle可以返回主窗體的句柄)。這個可以在.NET Framework源代碼中看出來
public bool CloseMainWindow()
{
IntPtr mainWindowHandle = this.MainWindowHandle;
//句柄是否為0
if (mainWindowHandle == IntPtr.Zero)
{
return false;
}
//GetWindowLong是否成功執行
if ((NativeMethods.GetWindowLong(new HandleRef(this, mainWindowHandle), -16) &0x8000000) != 0)
{
return false;
}
//0x10 是 WM_CLOSE消息
//向主窗體發送WM_CLOSE,注意是PostMessage而不是SendMessage
NativeMethods.PostMessage(new HandleRef(this, mainWindowHandle), 0x10, IntPtr.Zero,IntPtr.Zero);
return true;
}
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();
2.接下來就是Process.Kill方法,從名字也可以看出來,直接殺掉,不給任何喘息機會呵呵。Kill方法會直接結束整個進程,不進行常規資源清理(什么finally塊等……)。Kill本質調用本地API:TerminateProcess函數。
3.最后一個是Process.Close方法。抱歉它根本不是用來結束進程的!這個方法名字有些誤導,其實則不然。它僅僅是IDisposable的Dispose方法的具體執行,用來進行Process類的托管資源清理的!
由於Process類繼承自Component類,后者繼承IDisposable而同時又有析構函數,而通過一個繼承類可改寫的Dispose方法(參數是bool disposing)來判斷這個Dispose是用戶調用還是GC調用。而這個Process.Close()方法正是用戶調用Dispose時進行托管資源的清理方法:
下面Process.Dispose方法代碼:
protected override void 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
1. Environment.Exit相當於在Main函數中的return指令。不過它不會執行代碼塊的finally塊(如果有的話),但資源清理還是要進行的。
它是最常見的退出當前進程的方法之一。在Main函數中我們可以直接return語句便退出了程序。如果不在Main函數內,那么Environment.Exit方法就可以派上用場:
class a
{
~a()
{
Console.WriteLine("析構函數");
}
}
class Program
{
static void Main()
{
try
{
a oa = new a();
test();
}
finally
{
//這段代碼永遠不會執行
Console.WriteLine("finally");
}
}
static void test()
{
Environment.Exit(0);
}
}
代碼將會輸出:
析構函數
看來GC調用了oa的析構函數,但注意finally塊沒有運行。
2.Environment.FailFast方法更速度,它甚至不需要向操作系統返回進程退出代碼(ExitCode),直接結束當前進程並在應用程序事件薄中寫入信息,用於程序出現致命錯誤需要立即停止。
class a
{
~a()
{
Console.WriteLine("析構函數");
}
}
class Program
{
static void Main()
{
try
{
a oa = new a();
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;
class Program
{
[DllImport("kernel32.dll")]
static extern void ExitProcess(uint uExitCode);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool TerminateProcess(IntPtr hProcess, uint uExitCode);
static void 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事件綁在代碼上吧)(一下部分不了解,因為不知道WPF和XAML)
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
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
//Windows消息值
const uint WM_CLOSE = 0x10;
const uint WM_DESTROY = 0x02;
const uint WM_QUIT = 0x12;
//SendMessage和PostMessage的P/Invoke
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtrlParam);
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", SetLastError = true)]
static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
//窗體的Closing事件,判斷Closing是否被運行
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
MessageBox.Show("Closing事件!");
}
//發送三種消息
private void WM_CLOSE_Click(object sender, RoutedEventArgs e)
{
//也可以用PostMessage
SendMessage(Process.GetCurrentProcess().MainWindowHandle, WM_CLOSE, IntPtr.Zero,IntPtr.Zero);
}
private void WM_DESTROY_Click(object sender, RoutedEventArgs e)
{
//也可以用PostMessage
SendMessage(Process.GetCurrentProcess().MainWindowHandle, WM_DESTROY,IntPtr.Zero, IntPtr.Zero);
}
private void WM_QUIT_Click(object sender, RoutedEventArgs e)
{
//只能使用PostMessage去將WM_QUIT送至消息隊列尾部
PostMessage(Process.GetCurrentProcess().MainWindowHandle, WM_QUIT, IntPtr.Zero,IntPtr.Zero);
}
}
}