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 WndProc2 自动恢复功能的封装
由于外壳只向所有顶层的应用程序发送通知,这为封装自动恢复功能带来了一定的困难。因为通知栏图标的回调函数只能接收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 Contructordestructor TEoCSysTray.Destroy;
begin …… FDestroying := True; FParentWindow := nil; FreeObjectInstance(FNewWndProc); FNewWndProc := nil; …… inherited Destroy;end; //end of destructorprocedure 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 SetActiveprocedure 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 HookParentFormprocedure 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 UnHookParentFormprocedure 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 HookWndProcprocedure TEoCSysTray.DoTaskBarCreated;
begin …… //在这里重新添加通知栏图标 if Assigned(FTaskBarCreated) then FTaskBarCreated(Self);end; //end of procedure DoTaskBarCreatedinitialization
//注册询问任务栏重建的消息 MsgTaskbarCreated := RegisterWindowMessage('TaskbarCreated');end.