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()
};
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()
::SendMessage(hWndTarget, WM_MY_MESSAGE, wParam, lParam);
// 或 MFC
pWnd->SendMessage(WM_MY_MESSAGE, wParam, lParam);
特性:阻塞呼叫執行緒直到處理完成,返回 LRESULT。
::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;
}
WM_USER + 100 以上,或 RegisterWindowMessage 取得全域唯一 IDWPARAM/LPARAM 為 64 位,指標傳遞安全;32 位元下需注意ON_MESSAGE 巨集拼寫、類別繼承鏈、訊息對應表是否在正確類別// 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);
}