程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> 具有自動恢復功能的通知欄圖標控件

具有自動恢復功能的通知欄圖標控件

編輯:Delphi

  任務欄(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');

  

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved