[EOutOfResources尝试还原任务栏图标时发生异常

问题描述 投票:0回答:1

我在尝试执行代码以在资源管理器崩溃/重启后恢复托盘图标时,遇到EOutOfResources异常'无法删除外壳通知图标'。我的代码基于发现的旧解决方案here。尝试隐藏任务栏图标时发生异常。为什么下面的Delphi XE代码不起作用?

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ImgList, ExtCtrls;

type
  TForm1 = class(TForm)
    TrayIcon1: TTrayIcon;
    ImageListTray: TImageList;
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  protected
    procedure WndProc(var Message: TMessage); Override;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  msgTaskbarRestart : Cardinal; {custom systemwide message}  

implementation

{$R *.dfm}

//ensure systray icon recreated on explorer crash
procedure TForm1.FormCreate(Sender: TObject);
begin
  msgTaskbarRestart := RegisterWindowMessage('TaskbarCreated');
end;

procedure TForm1.WndProc(var Message: TMessage);
begin
  if (msgTaskbarRestart <> 0) and (Message.Msg = msgTaskbarRestart) then begin 
    TrayIcon1.Visible := False; {Destroy the systray icon here}//EOutOfResources exception here
    TrayIcon1.Visible := True;  {Replace the systray icon}
    Message.Result := 1;
  end;
  inherited WndProc(Message);
end;

end.
delphi-xe trayicon
1个回答
0
投票

TTrayIcon.Visible属性设置器在EOutOfResources请求失败时引发NIM_DELETE

procedure TCustomTrayIcon.SetVisible(Value: Boolean);
begin
  if FVisible <> Value then
  begin
    FVisible := Value;
    ...

    if not (csDesigning in ComponentState) then
    begin
      if FVisible then
        ...
      else if not (csLoading in ComponentState) then
      begin
        if not Refresh(NIM_DELETE) then
          raise EOutOfResources.Create(STrayIconRemoveError); // <-- HERE
      end;
      ...
    end;
  end;
end;

Refresh()只是Win32 Shell_NotifyIcon()函数的调用:

function TCustomTrayIcon.Refresh(Message: Integer): Boolean;
  ...
begin
  Result := Shell_NotifyIcon(Message, FData);
  ...
end;

[当您收到TaskbarCreated消息时,以前的图标不再出现在任务栏中,因此Shell_NotifyIcon(NIM_DELETE)返回False。创建(重新)任务栏时,您根本不应尝试删除旧图标,只需根据需要使用Shell_NotifyIcon(NIM_ADD)重新添加新图标。

[TTrayIcon有一个公共的Refresh()方法,但是使用NIM_MODIFY而不是NIM_ADD,因此在这种情况下将不起作用,或者:

procedure TCustomTrayIcon.Refresh;
begin
  if not (csDesigning in ComponentState) then
  begin
    ...
    if Visible then
      Refresh(NIM_MODIFY);
  end;
end;

但是,当您使用TaskbarCreated时,您实际上并不需要手动处理TTrayIcon消息,因为它已经为您内部处理了该消息,如果Shell_NotifyIcon(NIM_ADD),它将调用Visible=True

procedure TCustomTrayIcon.WindowProc(var Message: TMessage);
  ...
begin
  case Message.Msg of
    ...
  else
    if (Cardinal(Message.Msg) = RM_TaskBarCreated) and Visible then
      Refresh(NIM_ADD); // <-- HERE
  end;
end;

...

initialization
  ...
  TCustomTrayIcon.RM_TaskBarCreated := RegisterWindowMessage('TaskbarCreated');
end.

[如果由于某种原因不能正常工作,并且/或者您需要手动处理TaskbarCreated,那么我建议您直接调用受保护的TCustomTrayIcon.Refresh()方法,例如:

type
  TTrayIconAccess = class(TTrayIcon)
  end;

procedure TForm1.WndProc(var Message: TMessage);
begin
  if (msgTaskbarRestart <> 0) and (Message.Msg = msgTaskbarRestart) then begin 
    if TrayIcon1.Visible then begin
      // TrayIcon1.Refresh;
      TTrayIconAccess(TrayIcon1).Refresh(NIM_ADD);
    end;
    Message.Result := 1;
  end;
  inherited WndProc(Message);
end;

否则,根本不使用TTrayIcon。已知是越野车。多年来,我已经看到很多人在TTrayIcon方面有很多问题。我建议直接使用Shell_NotifyIcon()。我自己使用它从未有任何问题。

© www.soinside.com 2019 - 2024. All rights reserved.