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

MFC 自訂訊息完整實作指南:從定義到訊息對應的四步驟標準流程

     編輯:關於VC++

前言:為什麼需要自訂訊息?

ClassWizard 不支援直接新增使用者訊息 (WM_USER 以上),需手動編輯原始碼。常用於:執行緒完成通知主視窗、模組間解耦通訊、非標準事件傳遞。本文以 WM_MY_MESSAGE 為例,示範標準四步驟流程。

四步驟實作流程

第一步:定義訊息常數

在共用標頭檔 (如 `Resource.h` 或 `CustomMessages.h`) 定義:

// Microsoft 建議自訂訊息從 WM_USER + 100 起始,避免與系統/控件訊息衝突
#define WM_MY_MESSAGE (WM_USER + 100)

第二步:實作訊息處理函式

在目標視窗類別 (如 `CMainFrame`) 宣告與實作:

// .h 宣告
protected:
    afx_msg LRESULT OnMyMessage(WPARAM wParam, LPARAM lParam);

// .cpp 實作
LRESULT CMainFrame::OnMyMessage(WPARAM wParam, LPARAM lParam) {
    // TODO: 處理自訂訊息
    // wParam/lParam 可傳遞指標/整數/結構體指標
    // 若傳遞指標,需確保記憶體生命週期 (建議用 PostMessage + new, 接收端 delete)
    return 0;
}

第三步:類別宣告區加入訊息對應宣告

class CMainFrame : public CMDIFrameWnd {
    // ...
protected:
    //{{AFX_MSG(CMainFrame)
    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
    afx_msg void OnTimer(UINT nIDEvent);
    afx_msg LRESULT OnMyMessage(WPARAM wParam, LPARAM lParam); // 手動加入
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()
};

第四步:訊息對應表加入 ON_MESSAGE 巨集

BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd)
    //{{AFX_MSG_MAP(CMainFrame)
    ON_WM_CREATE()
    ON_WM_TIMER()
    ON_MESSAGE(WM_MY_MESSAGE, OnMyMessage) // 手動加入
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

發送訊息的兩種方式

1. 同步傳送 (SendMessage) — 立即執行並等待返回

::SendMessage(hWndTarget, WM_MY_MESSAGE, wParam, lParam);
// 或 MFC
pWnd->SendMessage(WM_MY_MESSAGE, wParam, lParam);

特性:阻塞呼叫執行緒直到處理完成,返回 LRESULT。

2. 非同步張貼 (PostMessage) — 放入訊息佇列立即返回

::PostMessage(hWndTarget, WM_MY_MESSAGE, wParam, lParam);
// 或 MFC
pWnd->PostMessage(WM_MY_MESSAGE, wParam, lParam);

特性:不阻塞,訊息稍後處理,無返回值。
適用:跨執行緒通知 (Worker Thread → UI Thread)、避免死結。

進階:跨執行緒傳遞指標資料的安全模式

// 發送端 (Worker Thread)
struct MyData { int id; CString info; };
MyData* pData = new MyData{ 1, _T("Task Done") };
::PostMessage(hMainWnd, WM_MY_MESSAGE, 0, (LPARAM)pData);

// 接收端 (UI Thread - OnMyMessage)
LRESULT CMainFrame::OnMyMessage(WPARAM, LPARAM lParam) {
    MyData* pData = (MyData*)lParam;
    if (pData) {
        // 使用 pData->info ...
        delete pData; // 必須釋放!
    }
    return 0;
}

常見錯誤與注意事項

  1. 訊息 ID 衝突:嚴格使用 WM_USER + 100 以上,或 RegisterWindowMessage 取得全域唯一 ID
  2. 參數截斷:64 位元下 WPARAM/LPARAM 為 64 位,指標傳遞安全;32 位元下需注意
  3. 訊息處理未進入:檢查 ON_MESSAGE 巨集拼寫、類別繼承鏈、訊息對應表是否在正確類別
  4. ClassWizard 衝突:手動加入後 ClassWizard 可能顯示錯誤,但編譯無誤即可;或用 Visual Studio 2008+ 內建支援

實戰範例:多執行緒完成通知主視窗

// Worker Thread
UINT WorkerThread(LPVOID pParam) {
    CMainFrame* pMain = (CMainFrame*)pParam;
    // ... 長時間運算 ...
    ::PostMessage(pMain->GetSafeHwnd(), WM_MY_MESSAGE, 0, (LPARAM)new CString(_T("Done")));
    return 0;
}

// MainFrame - 按鈕啟動執行緒
void CMainFrame::OnStartTask() {
    AfxBeginThread(WorkerThread, this);
}
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved