首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

深入 VCL 理解 BCB 訊息機制 (三):Application 層 Hook 與 HookMainWindow

     編輯:關於C++

TApplication 在訊息處理中的核心地位

BCB 程式啟動流程:


WinMain → Application->Initialize() → Application->CreateForm() → Application->Run()

Application->Run() 進入訊息迴圈,核心邏輯在 TApplication.ProcessMessage:


// 簡化自 VCL 原始碼 (forms.pas)
function TApplication.ProcessMessage(var Msg: TMsg): Boolean;
begin
    Result := False;
    if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then begin
        Result := True;
        if Msg.Message <> WM_QUIT then begin
            Handled := False;
            // 1. OnMessage 事件 (最早攔截點,僅限佇列訊息)
            if Assigned(FOnMessage) then FOnMessage(Msg, Handled);
            // 2. 內部處理 (Hint, MDI, 鍵盤, 對話框)
            if not Handled and not IsHintMsg(Msg) and not IsMDIMsg(Msg)
               and not IsKeyMsg(Msg) and not IsDlgMsg(Msg) then begin
                TranslateMessage(Msg);
                DispatchMessage(Msg);  // 分派至視窗程序
            end;
        end else
            FTerminate := True;
    end;
end;

三種攔截層級比較

攔截點方法可攔截 SendMessage時機備註
1. Application.OnMessage指派事件處理器否 (僅 PeekMessage 取得的佇列訊息)最早 (訊息迴圈中)簡單、安全
2. Form/Control.WndProc覆寫虛擬函數是中等 (視窗程序中)標準做法
3. Application.HookMainWindow掛鉤自訂函數是最早 (TApplication.WndProc 開頭)功能最強、需手動移除

Application.OnMessage 範例


void __fastcall TForm1::MyOnMessage(tagMSG &Msg, bool &Handled) {
    if (Msg.message == WM_KEYDOWN) {
        MessageDlg("You Pressed Key!", mtWarning, TMsgDlgButtons() << mbOK, 0);
        Handled = true;  // 標記已處理,不再分派
    }
}

void __fastcall TForm1::FormCreate(TObject *Sender) {
    Application->OnMessage = MyOnMessage;
}

HookMainWindow 進階用法

原理

TApplication.WndProc 開頭遍歷 FWindowHooks 清單呼叫掛鉤函數:


for I := 0 to FWindowHooks.Count - 1 do
    if TWindowHook(FWindowHooks[I])(Message) then Exit;  // 返回 true 表示已處理

完整範例:攔截 SendMessage 發送的 WM_CLOSE


bool __fastcall TForm1::AppHookFunc(TMessage &Message) {
    bool Handled = false;
    if (Message.Msg == WM_CLOSE) {
        if (MessageDlg("Really Close??", mtWarning,
            TMsgDlgButtons() << mbYes << mbNo, 0) == mrNo) {
            Handled = true;  // 取消關閉
        }
    }
    return Handled;
}

void __fastcall TForm1::FormCreate(TObject *Sender) {
    Application->HookMainWindow(AppHookFunc);
}

void __fastcall TForm1::FormDestroy(TObject *Sender) {
    Application->UnhookMainWindow(AppHookFunc);  // 必須配對移除!
}

void __fastcall TForm1::Button1Click(TObject *Sender) {
    // 測試:直接發送 WM_CLOSE 給 Application 視窗
    SendMessage(Application->Handle, WM_CLOSE, 0, 0);
}

完整訊息流向圖

[硬體/系統] → 系統訊息佇列
    ↓ PeekMessage (ProcessMessage)
[Application.OnMessage] → 內部處理 (Hint/MDI/Key/Dialog)
    ↓ DispatchMessage
[TApplication.WndProc] → [HookMainWindow 掛鉤函數] → 預設處理
    ↓ DispatchMessage 找到目標視窗
[目標視窗 MainWndProc] → [WndProc] → [Dispatch] → [DefaultHandler]

最佳實踐建議

  1. 優先使用 OnMessage:簡單、自動清理、不影響 SendMessage
  2. 需攔截 SendMessage 時:使用 HookMainWindow,並在 FormDestroy 呼叫 UnhookMainWindow
  3. 控制項層級:覆寫 WndProc 或使用訊息對應巨集
  4. 避免在 Hook 中耗時操作:會阻塞所有訊息處理
  5. 64 位元相容:Hook 函數簽名需符合 bool __fastcall (TMessage&)

系列總結 (三篇合併觀點)

BCB 訊息處理提供從應用層到控制項層的完整攔截能力,開發者可根據需求選擇最適合的切入點。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved