WEB开发网
开发学院软件开发Delphi 具有自动恢复功能的通知栏图标控件 阅读

具有自动恢复功能的通知栏图标控件

 2006-02-04 13:36:48 来源:WEB开发网   
核心提示: 任务栏(Taskbar)是微软公司在Windows 95中引入的一种特殊的桌面工具条,它为用户快速访问计算机资源提供了极大的方便,具有自动恢复功能的通知栏图标控件,而状态栏(以下称通知栏)无疑是任务栏上较为特殊的一个窗口,编程人员可以调用API函数Shell_NotifyIcon向通知栏发送消息来添加、删除或修改图标
 

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

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


1 外壳Explorer重启时通知栏图标的自动恢复
相信很多Windows用户都碰到过这种情况:运行某个程序时出现意外错误,导致外壳程序Explorer.exe崩溃而发生重启(即Explorer.exe被关闭后重新运行),任务栏也在消失后重新生成,但应用程序在通知栏添加的图标消失了,虽然这些程序仍在运行,但再也无法通过通知栏图标与用户交互。为避免这种情况出现,Windows提供了相应的机制。
在安装了Internet Explorer 4.0及以上版本的Windows操作系统中,当任务栏建立后,外壳会向所有顶层的应用程序发出通知消息,该消息是外壳以字符串"TaskbarCreated"为参数向系统注册获得的,应用程序窗口接收到该消息后就应该重新添加的通知栏图标。
在Delphi中实现过程如下:
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

2 自动恢复功能的封装
由于外壳只向所有顶层的应用程序发送通知,这为封装自动恢复功能带来了一定的困难。因为通知栏图标的回调函数只能接收WM_XBUTTONDOWN、WM_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.


Tags:具有 自动 恢复

编辑录入:爽爽 [复制链接] [打 印]
赞助商链接