Delphi - 防止托盘应用程序中的窗口关闭

时间:2016-09-05 09:01:20

标签: delphi delphi-xe3

我正在尝试为我们的系统制作一个小工具,以防止在我们的软件运行时窗口关闭。为了使其独立,我创建了一个单独的应用程序,以防止使用this信息关闭。

但是,当应用程序最小化到托盘时,Windows只会将其杀死并正常关闭。如果表单可见(也就是我从Form.OnCreate事件中注释Application.Minimize调用),它确实可以防止关闭。

如何将应用程序隐藏在托盘中,我可以如何实现MainWindow挂钩以保持活动,或者可以通过其他方式阻止系统关闭?

感谢。

当前代码:

unit Main;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, TlHelp32, dateutils, Vcl.AppEvnts, Vcl.ExtCtrls;

type
  TForm1 = class(TForm)
    TrayIcon1: TTrayIcon;
    ApplicationEvents1: TApplicationEvents;
    procedure FormCreate(Sender: TObject);
    function HookEndSession(var Message: TMessage): Boolean;
    procedure WMQueryEndSession(var Msg : TWMQueryEndSession) ;
         message WM_QueryEndSession;
    procedure ApplicationEvents1Minimize(Sender: TObject);
    procedure ApplicationEvents1Restore(Sender: TObject);
    procedure TrayIcon1DblClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var Form1: TForm1;
var Mutex : THandle;

implementation

{$R *.dfm}



procedure TForm1.WMQueryEndSession(var Msg: TWMQueryEndSession);
begin
  Msg.Result := 0;
end;

function TForm1.HookEndSession(var Message: TMessage): Boolean;
begin
  result := false;
  if Message.Msg = WM_ENDSESSION then begin
    Message.Result := 0;
    result := true;
  end;
end;


procedure TForm1.TrayIcon1DblClick(Sender: TObject);
begin
  WindowState := wsNormal;
  Application.Terminate;
end;

procedure TForm1.ApplicationEvents1Minimize(Sender: TObject);
begin
  Hide();
  WindowState := wsMinimized;
  TrayIcon1.Visible := True;
end;

procedure TForm1.ApplicationEvents1Restore(Sender: TObject);
begin
  Application.Minimize;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Mutex := CreateMutex(nil, True, 'preventWinShutdown');
  if (Mutex = 0) OR (GetLastError = ERROR_ALREADY_EXISTS) then
  Application.Terminate;

  Application.HookMainWindow(HookEndSession);

  TrayIcon1.Hint := 'Windows Shutdown prevented.';
  //Application.Minimize;
end;


end.

2 个答案:

答案 0 :(得分:4)

我手头没有XE3,但在XE7中,TCustomTrayIcon.WindowProc通过返回1(相当于TRUE)显式处理WM_QUERYENDSESSION,向Windows发出信号以进行注销/关机/重启序列。 你可以覆盖它:

type
  TTrayIcon = class(Vcl.ExtCtrls.TTrayIcon)
  protected
    procedure WindowProc(var Message: TMessage); override;
  end;

procedure TTrayIcon.WindowProc(var Message: TMessage);
begin
  case Message.Msg of
    WM_QUERYENDSESSION:
      Message.Result := 0;
    else
      inherited WindowProc(Message);
  end;
end;

要获得更完整的解决方案,您可以在一个单独的单元中创建一个新类,继承自TCustomTrayIcon并重新发布您希望公开的属性和事件,如TTrayIcon,安装组件在IDE中,并在任何项目中使用它。

以上示例只是一个带有插入类的快速示例,您只需在TForm1之前声明并立即使用,仅在此单元中使用。

答案 1 :(得分:3)

Ondrej说的只是解决方案的一半。

在Vista及更高版本中,您必须使用ShutdownBlockReasonCreate()。由于以下警告,这在您的情况下尤为重要:

Application Shutdown Changes in Windows Vista

  

必须阻止关闭的应用程序应使用新的关闭原因API

     

在Windows XP中,Microsoft建议如果某个应用程序需要阻止关闭,它应该显示自己的UI,解释为什么需要这样做,这样用户在关闭失败时就不那么沮丧了。如前所述,通过显示新的UI,列出应用程序为阻止关闭提供的所有原因,Windows Vista将在关机失败时减少用户的挫败感。

     

在Windows Vista中,如果您的应用程序必须阻止关闭,除了返回FALSE或不响应WM_QUERYENDSESSION之外,它还应该利用这个新UI来使用简单的API为Windows提供一个原因字符串来解释它为什么阻止关机。这个API很简单:

     

BOOL ShutdownBlockReasonCreate(HWND hWnd, LPCWSTR pwszReason);

     

BOOL ShutdownBlockReasonDestroy(HWND hWnd);

     

BOOL ShutdownBlockReasonQuery(HWND hWnd, LPWSTR pwszBuff, DWORD *pcchBuff);

     

本主题后面将详细介绍此API的使用,以及各个ShutdownBlockReason()函数的MSDN文档。

     

再次注意,此API不会替换为WM_QUERYENDSESSION返回FALSE(或延迟响应)以阻止关闭的需要。除了使用API​​之外,应用程序还需要执行此操作。返回TRUE到WM_QUERYENDSESSION的应用程序将在关闭时关闭,无论他们是否使用了API

     

另请注意,如果您的应用程序没有可见的顶级窗口,则必须使用此API才能成功阻止关闭。如果这些应用程序在不使用API​​ 的情况下阻止关闭,它们将自动终止。