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

攔截訊息框文字:用 CBT Hook 擷取與修改 MessageBox 內容

     編輯:關於.NET

目標:把彈出的 MessageBox 文字取出來,甚至修改它

核心技術:Windows CBT Hook (WH_CBT)

利用 SetWindowsHookEx 安裝 WH_CBT (代碼 5) 掛鉤,在視窗建立前夕(HCBT_CREATEWND)攔截 MessageBox,讀取標題與內容,甚至修改按鈕文字。

完整實作程式碼 (C# WinForms)

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Reflection;
using System.Xml;
using System.Runtime.InteropServices;

namespace WindowsApplication26
{
    public partial class Form1 : Form
    {
        public class HookMsg
        {
            #region Win32 API 匯入
            private const int WH_CBT = 0x5;
            private const int IDC_OK = 0x1;
            private const int IDC_Text = 0xFFFF;

            [DllImport("user32.dll")]
            protected static extern IntPtr SetWindowsHookEx(int code, HookProc func, IntPtr hInstance, int threadID);

            [DllImport("user32.dll")]
            protected static extern int UnhookWindowsHookEx(IntPtr hhook);

            [DllImport("user32.dll")]
            protected static extern int CallNextHookEx(IntPtr hhook, int code, IntPtr wParam, IntPtr lParam);

            [DllImport("user32.dll")]
            protected static extern int GetWindowText(IntPtr hwnd, [Out] StringBuilder lpString, int nMaxCount);

            [DllImport("user32.dll")]
            protected static extern int GetClassName(IntPtr hwnd, [Out] StringBuilder lpString, int nMaxCount);

            [DllImport("user32.dll")]
            protected static extern int SetWindowText(IntPtr hWnd, string lpString);

            [DllImport("user32.dll")]
            protected static extern IntPtr GetDlgItem(IntPtr hwnd, int id);

            [DllImport("User32")]
            protected static extern int SetDlgItemText(IntPtr hDlg, int nIDDlgItem, string lpString);

            [DllImport("user32.dll")]
            protected static extern int GetDlgItemText(IntPtr hDlg, int nIDDlgItem, [Out] StringBuilder lpString, int nMaxCount);

            [DllImport("user32.dll", EntryPoint = "MessageBox")]
            protected static extern int _MessageBox(IntPtr hwnd, string text, string caption, int options);

            [DllImport("user32.dll")]
            protected static extern IntPtr GetActiveWindow();

            [DllImport("user32.dll")]
            protected static extern void DestroyWindow(IntPtr hwnd);
            #endregion

            private static IntPtr m_hhook;
            public delegate int HookProc(int code, IntPtr wParam, IntPtr lParam);
            protected static HookProc m_filterFunc;

            static HookMsg()
            {
                if (m_filterFunc == null) m_filterFunc = new HookProc(CoreHookProc);
            }

            public static void Install()
            {
                m_hhook = SetWindowsHookEx(WH_CBT, m_filterFunc, IntPtr.Zero, AppDomain.GetCurrentThreadId());
            }

            public static void Uninstall()
            {
                UnhookWindowsHookEx(m_hhook);
            }

            // 回呼處理
            protected static int CoreHookProc(int code, IntPtr wParam, IntPtr lParam)
            {
                if (code == 5) // HCBT_CREATEWND
                {
                    StringBuilder sb = new StringBuilder(255);

                    // 取得標題
                    GetWindowText(wParam, sb, 255);
                    string strTitle = "jinjazz看到了:" + sb.ToString();

                    // 取得訊息文字
                    GetDlgItemText(wParam, IDC_Text, sb, 255);
                    string strText = "jinjazz看到了:" + sb.ToString();

                    // 取得按鈕樣式組合
                    int style = 0;
                    for (int i = 0; i <= 5; i++)
                    {
                        if (GetDlgItem(wParam, i) != IntPtr.Zero) style += i;
                    }

                    // 修改文字與按鈕
                    SetDlgItemText(wParam, IDC_Text, strText);
                    SetWindowText(wParam, strTitle);
                    SetWindowText(GetDlgItem(wParam, (int)DialogResult.No), "jinjazzOK");
                    SetWindowText(GetDlgItem(wParam, (int)DialogResult.Cancel), "jinjazzCancel");
                    SetWindowText(GetDlgItem(wParam, (int)DialogResult.Abort), "jinjazzAbort");
                    SetWindowText(GetDlgItem(wParam, (int)DialogResult.Ignore), "jinjazzIgnore");
                    SetWindowText(GetDlgItem(wParam, (int)DialogResult.None), "jinjazzNone");
                    SetWindowText(GetDlgItem(wParam, (int)DialogResult.OK), "jinjazzOK");
                    SetWindowText(GetDlgItem(wParam, (int)DialogResult.Retry), "jinjazzRetry");
                    SetWindowText(GetDlgItem(wParam, (int)DialogResult.Yes), "jinjazzYes");
                }
                // return CallNextHookEx(m_hhook, code, wParam, lParam);
                return 0;
            }
        }

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e) { }

        private void button1_Click(object sender, EventArgs e)
        {
            MessageBox.Show(this, "確定按鈕 ", "標題");
        }

        private void button2_Click(object sender, EventArgs e)
        {
            MessageBox.Show(this, "確定按鈕 ", "標題", MessageBoxButtons.YesNoCancel);
        }

        private void button3_Click(object sender, EventArgs e)
        {
            MessageBox.Show(this, "確定按鈕 ", "標題", MessageBoxButtons.AbortRetryIgnore);
        }

        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            if (this.checkBox1.Checked) HookMsg.Install();
            else HookMsg.Uninstall();
        }
    }
}

關鍵點解析

技術點說明
WH_CBT = 5Computer-Based Training 掛鉤,可在視窗建立/銷毀/啟用/停用等時機攔截。
HCBT_CREATEWND (code==5)視窗即將建立時觸發,此時可透過 wParam 取得視窗句柄。
IDC_Text = 0xFFFFMessageBox 內容文字的控制項 ID(經驗值)。
GetDlgItemText / SetDlgItemText讀寫對話方塊內指定控制項文字。
按鈕 ID 對應OK=1, Cancel=2, Abort=3, Retry=4, Ignore=5, Yes=6, No=7;透過 GetDlgItem 取得句柄後修改文字。
執行緒範圍AppDomain.GetCurrentThreadId() 限定當前執行緒,避免影響其他執行緒。

實務應用場景

  • 自動化測試:驗證 MessageBox 顯示內容是否符合預期。
  • 多語系驗證:攔截並記錄不同語系下的訊息框文字。
  • 安全稽核:記錄應用程式彈出的所有警告/錯誤訊息。
  • UI 客製化:在不修改原始碼前提下,動態替換按鈕文字或訊息內容。

注意事項

  1. Hook 必須在同一執行緒安裝/移除,否則會失效或導致不穩定。
  2. 修改系統視窗文字屬於進階用法,需謹慎測試相容性。
  3. .NET Core / .NET 5+ 於非 Windows 平台無法使用此 P/Invoke 方式。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved