最近做的项目中需要系统关机,因为我在FormCloseQuery中总是把CanClose设为False,不关机的话直接导致系统中的关机、重启、注销都失效了!导致用户不能关机!(提示用户说要关机的话直接按电源^^)这样肯定是不行的!

要完成这个功能只需要到WM_QUERYENDSESSION消息就万事Ok!

  Windows在关机的时候会想(向)所有顶层窗口广播一个消息WM_QUERYENDSESSION,其lParam参数可以区分是关机还是注销用户(注销用 户时lParam是ENDSESSION_LOGOFF)。然后Windows会等到所有的应用程序都对这个消息返回TRUE才会关机,因此,只要我们的 应用程序对这个消息的处理返回FALSE,Windows就不会关机了。

  这个消息不能直接让应用程序退出,这个消息主要是询问应用程序是否已经作好了退出的准备,当所有的应用程序都对这个消息返回TRUE,系统回(会)注销或关机。如果想退出程序,请使用WM_CLOSE消息!

关键在于怎么在Delphi下WM_QUERYENDSESSION消息呢?Delphi也是很强悍的,当然不用直接去SetWindowLong了。

  首先介绍一下Delphi中消息的几种做法

第一种:自定义处理单条消息

1. unit Unit2;
2.
3. interface
4.
5. uses
6. Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
7. Dialogs;
8.
9. type
10. class(TForm)
11. procedure FormCreate(Sender: TObject);
12. procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
13. private
14. { Private declarations }
15. //直接用 TWMQueryEndSession
16. procedure EndMsg(var nMsg: TWMQueryEndSession); Message WM_QUERYENDSESSION;
17. //也可以用这种
18. //procedure EndMsg(var nMsg: TMessage); Message WM_QUERYENDSESSION;
19. public
20. { Public declarations }
21. end;
22.
23. var
24. Form2: TForm2;
25.
26. implementation
27.
28. {$R *.dfm}
29.
30. //收到WM_QUERYENDSESSION消息后就会触发这个过程
31. procedure TForm2.EndMsg(var nMsg: TWMQueryEndSession);
32. begin
33. //0 可以取消关机操作
34. .Result := 1;
35. '注销、重启、关机');
36. end;
37.
38. end.


第二种:利用Application.OnMessage处理消息




1. unit Unit2;
2.
3. interface
4.
5. uses
6. Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
7. Dialogs;
8.
9. type
10. class(TForm)
11. procedure FormCreate(Sender: TObject);
12. private
13. { Private declarations }
14.
15. public
16. { Public declarations }
17. procedure OnMsg(var nMsg: tagMSG; var nHandled: Boolean);
18. end;
19.
20. var
21. Form2: TForm2;
22.
23. implementation
24.
25. {$R *.dfm}
26.
27. //消息处理过程
28. procedure TForm2.OnMsg(var nMsg: tagMSG; var nHandled: Boolean);
29. begin
30. //处理……
31. //这里会收到各种消息……经测试无法收到WM_QUERYENDSESSION消息
32. end;
33.
34. procedure TForm2.FormCreate(Sender: TObject);
35. begin
36. .OnMessage := OnMsg;
37. end;
38.
39. end.


第三种:自己处理窗口函数,个人感觉这是最强大的,可以一切发往窗口的消息!




1. unit Unit2;
2.
3. interface
4.
5. uses
6. Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
7. Dialogs;
8.
9. type
10. class(TForm)
11. procedure FormCreate(Sender: TObject);
12. private
13. { Private declarations }
14.
15. public
16. { Public declarations }
17. procedure WndProc(var nMsg: TMessage); override;
18. end;
19.
20. var
21. Form2: TForm2;
22.
23. implementation
24.
25. {$R *.dfm}
26.
27. procedure TForm2.WndProc(var nMsg: TMessage);
28. begin
29.
30. //这里能收到发往窗口的所有消息
31.
32. inherited; // 注意这句不能少
33.
34. if nMsg.Msg = WM_QUERYENDSESSION then
35. begin
36. if nMsg.LParam = 0 then
37. '关机或重启')
38. else
39. '注销');
40. end;
41.
42. end;
43.
44. end.


经过再次测试,只有Application.OnMessage不能WM_QUERYENDSESSION消息!还有,有时候Delphi的一些控件也会导致不能正确。