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

Windows CE 使用 SetWindowLong 截獲與轉移視窗訊息處理函數完整教學

     編輯:.NET 實例教學

核心概念

Windows CE 提供 SetWindowLong API,允許將視窗的原始訊息處理函數(Window Procedure)替換為自訂函數,實現「視窗子類化」。這對於攔截特定按鍵、修改控件行為等場景極為實用。

關鍵 API 對照

API用途
SetWindowLong(hWnd, GWL_WNDPROC, NewProc)設定新視窗程序,返回舊程序位址
GetWindowLong(hWnd, GWL_WNDPROC)取得目前視窗程序位址
CallWindowProc(PrevProc, hWnd, Msg, wParam, lParam)呼叫舊視窗程序,維持預設行為

完整實作範例:攔截 Edit 控件的 ESC 鍵退出程式

1. 類別宣告

class CMainWnd : public CWndBase {
protected:
    static LRESULT CALLBACK CtrlProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
private:
    HWND m_hEdit;
    WNDPROC m_pOldEditProc;
};

2. 建立 Edit 控件並子類化

BOOL CMainWnd::Create(HINSTANCE hInst, HWND hParent, ...) {
    // ... 基礎建立略 ...

    // 建立 Edit 控件
    m_hEdit = CreateWindowEx(WS_EX_TOPMOST, TEXT("EDIT"), TEXT(""),
        ES_LEFT | WS_TABSTOP | WS_VISIBLE | WS_CHILD | WS_BORDER,
        cx/3, cy/3, cx/3, cy/3, m_hWnd, (HMENU)IDC_EDIT_WORD, m_hInst, NULL);

    // 將 this 指標存入 GWL_USERDATA,供靜態回呼函數取得物件實例
    SetWindowLong(m_hEdit, GWL_USERDATA, (DWORD)this);

    // 保存原視窗程序,設定新程序
    m_pOldEditProc = (WNDPROC)GetWindowLong(m_hEdit, GWL_WNDPROC);
    SetWindowLong(m_hEdit, GWL_WNDPROC, (DWORD)CtrlProc);

    return TRUE;
}

3. 自訂視窗程序實作

LRESULT CALLBACK CMainWnd::CtrlProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    // 取得儲存的物件指標
    CMainWnd* pThis = (CMainWnd*)GetWindowLong(hWnd, GWL_USERDATA);
    if (!pThis || pThis->m_hEdit != hWnd)
        return DefWindowProc(hWnd, uMsg, wParam, lParam);

    switch (uMsg) {
    case WM_CHAR:
        if ((TCHAR)wParam == VK_ESCAPE) {
            PostQuitMessage(0); // 按 ESC 退出程式
            return 0;
        }
        break;
    }

    // 其他訊息交給原視窗程序處理
    return CallWindowProc(pThis->m_pOldEditProc, hWnd, uMsg, wParam, lParam);
}

進階技巧:一步到位獲取舊程序並設定新程序

// SetWindowLong 返回值即為舊程序位址
m_pOldEditProc = (WNDPROC)SetWindowLong(m_hEdit, GWL_WNDPROC, (DWORD)CtrlProc);

風險警示:系統崩潰案例

若將視窗程序指向已釋放的記憶體(如 DLL 卸載後、物件銷毀後),系統會立即崩潰。切勿在全域視窗(如前景視窗)上嘗試此操作:

// 危險示範:會導致系統當機
LRESULT MyProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
int WINAPI WinMain(...) {
    HWND hWnd = GetForegroundWindow();
    SetWindowLong(hWnd, GWL_WNDPROC, (DWORD)MyProc); // 系統崩潰!
    return 0;
}

最佳實踐總結

  1. 永遠保存原視窗程序指標,並在自訂程序中透過 CallWindowProc 轉發未處理訊息。
  2. 利用 GWL_USERDATA 傳遞物件實例,解決靜態回呼函數無法存取成員變數的問題。
  3. 在物件銷毀前(如 WM_DESTROY)務必呼叫 SetWindowLong 恢復原視窗程序,防止野指標。
  4. 僅子類化自建控件,避免修改系統關鍵視窗。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved