你好,欢迎来到电脑编程技巧与维护杂志社! 杂志社简介广告服务读者反馈编程社区  
合订本订阅
 
 
您的位置:杂志经典 / 网络与通信
具有自动恢复功能的通知栏图标控件
 

关键词:通知栏、窗口过程

 

任务栏(Taskbar)是微软公司在Windows 95中引入的一种特殊的桌面工具条,它为用户快速访问计算机资源提供了极大的方便,而状态栏(以下称通知栏)无疑是任务栏上较为特殊的一个。编程人员可以调用API函数Shell_NotifyIcon向通知栏发送消息来添加、删除或修改图标,当在图标上发生鼠标或键盘事件时,系统会向应用程序发送编程时预先定义的消息,通知栏处理回调函数就会被自动调用以做出相应的处理。实现上述功能的相关文章俯仰即拾,此处不再赘述。本文将讨论两个较为深入的问题及其在Delphi中的实现方法。

l         Windows发生错误导致外壳Explorer.exe重启时通知栏图标的自动恢复

l         将自动恢复功能封装在控件中以便其它程序中调用用。

一、外壳Explorer重启时通知栏图标的自动恢复

相信很多Windows用户都碰到过这种情况:运行某个程序时出现意外错误,导致外壳程序Explorer.exe崩溃而发生重启(即Explorer.exe被关闭后重新运行),任务栏也在消失后重新生成。此种情况造成的后果之一是:通知栏中除Windows自己添加的图标之外的所有图标都被删除,虽然这些被删除的图标所属的应用程序仍在运行,但再也无法通过通知栏图标与用户交互。为避免这种情况出现,Windows提供了相应的机制。

首先应强调的是,图标丢失是不可避免的。用户所看到的外壳重启后Windows自己添加的图标没有被删除其实只是假象。这些图标同样也已经被删除,只不过是它们重新被添加了一次。显然在这种情况下应用程序应该是接收到了某个消息,知道任务栏已经被重新建立,才重新向通知栏添加图标,从而避免了程序仍在运行而无法与用户交互的情况。

事实上在安装了Internet Explorer 4.0及以上版本的Windows操作系统中,每次任务栏重建后,外壳都会向所有顶层的应用程序发出通知,其过程如下:任务栏建立后,它以字符串“TaskbarCreated”为参数向系统注册获得一个消息,并将这个消息广播给所有的顶层窗口,应用程序窗口接收到该消息后就应该重新添加的通知栏图标。

好,原理已经分析清楚,代码编写就很容易了,实现过程如下:

1.一个整型变量MsgTaskbarRestart,它将保存我们要接收的任务栏重建的消息。

2.程序的initialization部分或者是在OnCreate事件中以“TaskbarCreated”为参数向系统注册消息(也即是询问“TaskbarCreated”是哪条消息,因为以相同的参数注册会得到相同的消息,而“TaskbarCreated”在Windows启动的时候就已经被外壳注册)。

initialization

  MsgTaskbarRestart := RegisterWindowMessage(‘TaskbarCreated’);

3.主窗口的消息处理过程,拦截任务栏重建消息,进行重新添加图标的操作。

procedure TMainForm.WndProc(var Message: TMessage);

begin

  ……

  if Message.Msg = MsgTaskbarRestart then

  begin

    TrayIcon.Active := False;   //删除通知栏图标

    TrayIcon.Active := True;    //添加通知栏图标

  end;

  ……

  inherited WndProc(Message);

end; //end of WndProc

二、自动恢复功能的封装

将通知栏图标的自动恢复功能封装为控件供其它程序调用是很有实用价值的。但遗憾的是通知栏图标的回调函数只能接收WM_XBUTTONDOWNWM_XBUTTONUP等有限的几个消息,并不能接收所有的窗口消息。本节将介绍一种方法,使得在控件中能够接收窗口消息,从而实现自动恢复功能的封装。

我们知道,SetWindowLong函数可以改变一个窗口的属性,包括其窗口过程(使用GWL_WNDPROC参数),因此,在创建控件时只需在适当的地方将应用程序窗口的窗口过程保存起来,并替换为控件中的某个新的窗口处理过程,在控件中就能够响应所有的窗口消息了(包括任务栏重建的消息);当控件销毁的时候再将保存的原始窗口过程恢复即可。实现代码如下(其中“……”的地方略去容易实现的添加、删除通知栏图标等函数及过程):

  TEoCSysTray = class(TComponent)

  Private

    ……

    FActive: boolean;

    FParentWindow: TWinControl;     //父窗口

    FNewWndProc: Pointer;     //新的父窗口过程指针

    FPrevWndProc: Pointer;    //原先的父窗口过程指针

    FTaskBarCreated: TNotifyEvent;  //任务栏重建事件

    ……

    procedure SetActive(Value: boolean);    //设置控件是否起作用

    procedure HookParentForm;               //替换父窗口的窗口过程

    procedure UnHookParentForm;             //还原父窗口的窗口过程

    procedure HookWndProc(var AMsg: TMessage);  //新的父窗口过程

  protected

    procedure DoTaskBarCreated; dynamic;        //触发任务栏重建事件

  public

    constructor Create(AOwner: TComponent); override;

    destructor Destroy; override;

    property Active: boolean read FActive write SetActive;

    property OnTaskBarCreated: TNotifyEvent read FTaskBarCreated

      write FTaskBarCreated;

implementation

type

  THack = class(TWinControl);   //用以访问位于父窗口保护域的默认窗口处理过程

var

  MsgTaskbarCreated  : Integer;             //由系统注册的任务栏重建消息

constructor TEoCSysTray.Create(AOwner: TComponent);

begin

  inherited Create(AOwner);

  ……

  FActive := false;

  FNewWndProc := MakeObjectInstance(HookWndProc);//建立新的窗口过程指针

  FPrevWndProc := nil;

  if (AOwner <> nil) and (AOwner is TForm) then     //获得父窗口

    FParentWindow := TWinControl(AOwner)

  else

    FParentWindow := Application.MainForm;

  ……

end;//end of Contructor

destructor TEoCSysTray.Destroy;

begin

  ……

  FDestroying := True;

  FParentWindow := nil;

  FreeObjectInstance(FNewWndProc);

  FNewWndProc := nil;

  ……

  inherited Destroy;

end; //end of destructor

procedure TEoCSysTray.SetActive(Value: boolean);

begin

  if Value <> FActive then

  begin

    FActive := Value;

    if not (csDesigning in ComponentState) then //控件未处于设计状态

      case Value of

        True:

          begin

            ……

            HookParentForm;   //替换父窗口的窗口过程

            ……

          end;

        False:

          begin

            ……

            UnHookParentForm;  //还原父窗口的窗口过程

            ……

          end;

      end;

  end;

end; //end of procedure SetActive

procedure TEoCSysTray.HookParentForm;       //替换父窗口的窗口过程

var

  P                                     : Pointer;

begin

  if Assigned(FParentWindow) and

    not ((csDesigning in FParentWindow.ComponentState) or

    (csDestroying in FParentWindow.ComponentState) or FDestroying) then

  begin

    FParentWindow.HandleNeeded;

    P := Pointer(GetWindowLong(FParentWindow.Handle, GWL_WNDPROC));

    if (P <> FNewWndProc) then

    begin

      FPrevWndProc := P;

      SetWindowLong(FParentWindow.Handle,

        GWL_WNDPROC, LongInt(FNewWndProc));       //替换父窗口的窗口过程

    end;

  end;

end; //end of procedure HookParentForm

procedure TEoCSysTray.UnHookParentForm;         //还原父窗口的窗口过程

begin

  if Assigned(FParentWindow) then

  begin

    if Assigned(FPrevWndProc) and FParentWindow.HandleAllocated and

      (Pointer(GetWindowLong(FParentWindow.Handle, GWL_WNDPROC)) =

FNewWndProc) then

      SetWindowLong(FParentWindow.Handle,

        GWL_WNDPROC, LongInt(FPrevWndProc));      //还原父窗口的窗口过程

  end;

  FPrevWndProc := nil;

end; //end of procedure UnHookParentForm

procedure TEoCSysTray.HookWndProc(var AMsg: TMessage);

begin

  if Assigned(FParentWindow) then

  begin

    with AMsg do

    begin

      if Msg = MsgTaskbarCreated then           //接收到任务栏重建消息

        DoTaskBarCreated;                       //触发任务栏重建事件

      if Assigned(FPrevWndProc) then            //调用原窗口的窗口过程

        Result := CallWindowProc(FPrevWndProc, FParentWindow.Handle,

          Msg, WParam, LParam)

      else

        Result := CallWindowProc(THack(FParentWindow).DefWndProc,

          FParentWindow.Handle, Msg, WParam, LParam);

      if Msg = WM_DESTROY then           //窗口正被销毁

        UnHookParentForm;                //还原父窗口的窗口过程

    end;

  end;

end; //end of procedure HookWndProc

procedure TEoCSysTray.DoTaskBarCreated;

begin

  ……                                   //可以在这里重新添加通知栏图标

  if Assigned(FTaskBarCreated) then

    FTaskBarCreated(Self);

end; //end of procedure DoTaskBarCreated

initialization

  //注册询问任务栏重建的消息

  MsgTaskbarCreated := RegisterWindowMessage('TaskbarCreated');

end.
  推荐精品文章

·2024年2月目录 
·2024年1月目录
·2023年12月目录
·2023年11月目录
·2023年10月目录
·2023年9月目录 
·2023年8月目录 
·2023年7月目录
·2023年6月目录 
·2023年5月目录
·2023年4月目录 
·2023年3月目录 
·2023年2月目录 
·2023年1月目录 

  联系方式
TEL:010-82561037
Fax: 010-82561614
QQ: 100164630
Mail:gaojian@comprg.com.cn

  友情链接
 
Copyright 2001-2010, www.comprg.com.cn, All Rights Reserved
京ICP备14022230号-1,电话/传真:010-82561037 82561614 ,Mail:gaojian@comprg.com.cn
地址:北京市海淀区远大路20号宝蓝大厦E座704,邮编:100089