任務(wù)欄(Taskbar)是微軟公司在Windows 95中引入的一種特殊的桌面工具條,它為用戶快速訪問計算機資源提供了極大的方便,而狀態(tài)欄(以下稱通知欄)無疑是任務(wù)欄上較為特殊的一個窗口。編程人員可以調(diào)用API函數(shù)Shell_NotifyIcon向通知欄發(fā)送消息來添加、刪除或修改圖標(biāo),當(dāng)在圖標(biāo)上發(fā)生鼠標(biāo)或鍵盤事件時,系統(tǒng)會向應(yīng)用程序發(fā)送編程時預(yù)先定義的消息,通知欄處理回調(diào)函數(shù)就會被自動調(diào)用以做出相應(yīng)的處理。實現(xiàn)上述功能的相關(guān)文章俯仰即拾,此處不再贅述。本文將討論兩個較為深入的問題及其在Delphi中的實現(xiàn)方法。
1、Windows發(fā)生錯誤導(dǎo)致外殼Explorer.exe重啟時通知欄圖標(biāo)的自動恢復(fù)
2、將自動恢復(fù)功能封裝在控件中以便其它程序中調(diào)用。
關(guān)鍵詞:通知欄、窗口過程
1 外殼Explorer重啟時通知欄圖標(biāo)的自動恢復(fù)
相信很多Windows用戶都碰到過這種情況:運行某個程序時出現(xiàn)意外錯誤,導(dǎo)致外殼程序Explorer.exe崩潰而發(fā)生重啟(即Explorer.exe被關(guān)閉后重新運行),任務(wù)欄也在消失后重新生成,但應(yīng)用程序在通知欄添加的圖標(biāo)消失了,雖然這些程序仍在運行,但再也無法通過通知欄圖標(biāo)與用戶交互。為避免這種情況出現(xiàn),Windows提供了相應(yīng)的機制。
在安裝了Internet Explorer 4.0及以上版本的Windows操作系統(tǒng)中,當(dāng)任務(wù)欄建立后,外殼會向所有頂層的應(yīng)用程序發(fā)出通知消息,該消息是外殼以字符串"TaskbarCreated"為參數(shù)向系統(tǒng)注冊獲得的,應(yīng)用程序窗口接收到該消息后就應(yīng)該重新添加的通知欄圖標(biāo)。
在Delphi中實現(xiàn)過程如下:
1). 定義一個整型變量MsgTaskbarRestart,用以保存任務(wù)欄重建的消息。
2). 在主程序的initialization部分或者是在OnCreate事件中以"TaskbarCreated"為參數(shù)向系統(tǒng)注冊消息(也即是詢問"TaskbarCreated"是哪條消息,因為以相同的參數(shù)注冊會得到相同的消息,而"TaskbarCreated"在Windows啟動的時候就已經(jīng)被外殼注冊)。
initialization
MsgTaskbarRestart := RegisterWindowMessage('TaskbarCreated');
3). 重載主窗口的消息處理過程,攔截任務(wù)欄重建消息,進(jìn)行重新添加圖標(biāo)的操作。
PRocedure TMainForm.WndProc(var Message: TMessage);
begin
……
if Message.Msg = MsgTaskbarRestart then
begin
TrayIcon.Active := False; //刪除通知欄圖標(biāo)
TrayIcon.Active := True; //添加通知欄圖標(biāo)
end;
……
inherited WndProc(Message);
end; //end of WndProc
2 自動恢復(fù)功能的封裝
由于外殼只向所有頂層的應(yīng)用程序發(fā)送通知,這為封裝自動恢復(fù)功能帶來了一定的困難。因為通知欄圖標(biāo)的回調(diào)函數(shù)只能接收WM_XBUTTONDOWN、WM_XBUTTONUP等有限的幾個消息,并不能接收所有的窗口消息。本節(jié)介紹的方法將使得在控件中能夠接收窗口消息,從而實現(xiàn)自動恢復(fù)功能的封裝。
解決問題的關(guān)鍵是SetWindowLong函數(shù),向它傳入GWL_WNDPROC參數(shù),可以改變一個窗口的窗口過程。只需在創(chuàng)建控件時將應(yīng)用程序窗口的窗口過程指針保存起來,并指向為控件中的某個新的窗口處理過程,在控件中就能夠響應(yīng)所有的窗口消息了(包括任務(wù)欄重建的消息);當(dāng)控件銷毀的時候再將保存的原始窗口過程指針恢復(fù)即可。實現(xiàn)代碼如下(其中"……"的地方略去容易實現(xiàn)的添加、刪除通知欄圖標(biāo)等函數(shù)及過程):
TEoCSysTray = class(TComponent)
Private
……
FActive: boolean;
FParentWindow: TWinControl; //父窗口
FNewWndProc: Pointer; //新的父窗口過程指針
FPrevWndProc: Pointer; //原先的父窗口過程指針
FTaskBarCreated: TNotifyEvent; //任務(wù)欄重建事件
……
procedure SetActive(Value: boolean); //設(shè)置控件是否起作用
procedure HookParentForm; //替換父窗口的窗口過程
procedure UnHookParentForm; //還原父窗口的窗口過程
procedure HookWndProc(var AMsg: TMessage); //新的父窗口過程
protected
procedure DoTaskBarCreated; dynamic; //觸發(fā)任務(wù)欄重建事件
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); //用以訪問位于父窗口保護域的默認(rèn)窗口處理過程
var
MsgTaskbarCreated : Integer; //由系統(tǒng)注冊的任務(wù)欄重建消息
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 //控件未處于設(shè)計狀態(tài)
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 //接收到任務(wù)欄重建消息
DoTaskBarCreated; //觸發(fā)任務(wù)欄重建事件
if Assigned(FPrevWndProc) then //調(diào)用原窗口的窗口過程
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
…… //在這里重新添加通知欄圖標(biāo)
if Assigned(FTaskBarCreated) then
FTaskBarCreated(Self);
end; //end of procedure DoTaskBarCreated
initialization
//注冊詢問任務(wù)欄重建的消息
MsgTaskbarCreated := RegisterWindowMessage('TaskbarCreated');
end.
新聞熱點
疑難解答