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

Windows API 深度解析:PostMessage vs SendMessage 異同與實戰陷阱

     編輯:關於C語言

核心區別一覽

特性PostMessageSendMessage
模式**異步** (非阻塞)**同步** (阻塞)
返回時機消息入隊列**立即返回**目標窗口進程**處理完畢**後返回
消息流向投遞到目標線程**消息隊列**直接調用目標窗口**窗口過程**
返回值BOOL (是否成功入隊)LRESULT (目標窗口處理結果)
跨線程行為線程繼續執行,目標線程稍後處理發送線程**阻塞**,直到目標線程處理完 (需目標線程有消息泵)
典型用途通知、觸發不關心結果的操作、避免死鎖需要立即獲得結果、狀態同步、UI 強制刷新

函數原型詳解

// PostMessage
BOOL PostMessage(
    HWND hWnd,      // 目標窗口句柄,HWND_BROADCAST=廣播
    UINT Msg,       // 消息 ID
    WPARAM wParam,  // 參數 1
    LPARAM lParam   // 參數 2
);

// SendMessage
LRESULT SendMessage(
    HWND hWnd,
    UINT Msg,
    WPARAM wParam,
    LPARAM lParam
);

關鍵參數 hWnd 特殊值

  • HWND_BROADCAST (0xFFFF):發送/投遞給**所有頂層窗口** (含不可見、非擁有、彈出式),**不含子窗口**。
  • NULL:PostMessage 相當於 PostThreadMessage(GetCurrentThreadId(), ...);SendMessage 不常用 NULL。

實戰代碼示例

1. 關閉指定窗口 (如 QQ)

#include 
int main() {
    HWND hWnd = FindWindow(NULL, L"QQ2010"); // 類名/標題
    if (hWnd) SendMessage(hWnd, WM_CLOSE, 0, 0);
    return 0;
}

2. 控制 ComboBox 下拉列表

// 拉下列表
SendMessage(hCombo, CB_SHOWDROPDOWN, TRUE, 0);
// 收起列表
SendMessage(hCombo, CB_SHOWDROPDOWN, FALSE, 0);

3. 實現剪切/複製/粘貼

SendMessage(hEdit, WM_COPY,  0, 0);
SendMessage(hEdit, WM_CUT,   0, 0);
SendMessage(hEdit, WM_PASTE, 0, 0);

4. 進程間傳遞數據 (WM_COPYDATA)

// 發送端
std::string str = "VC知識庫";
COPYDATASTRUCT cds = { 0, (DWORD)str.size()+1, (PVOID)str.c_str() };
SendMessage(hTarget, WM_COPYDATA, (WPARAM)hSource, (LPARAM)&cds);

// 接收端 (窗口過程)
case WM_COPYDATA: {
    COPYDATASTRUCT *p = (COPYDATASTRUCT*)lParam;
    std::string recv((char*)p->lpData, p->cbData);
    // 處理數據...
    return TRUE;
}

注意: SendMessage 配合 WM_COPYDATA 可確保接收端讀完前發送端不釋放緩衝區。

常見陷阱與避坑指南

  1. 死鎖風險:UI 線程 SendMessage 給自身或同進程其他 UI 線程,若目標線程正等待發送線程的鎖 → **死鎖**。
    → 解決:改用 PostMessage 或 SendMessageTimeout。
  2. 指針參數失效:PostMessage`/`SendNotifyMessage` 發送**系統保留消息 (WM_USER 以下)**時,**不能**在 wParam/lParam 傳遞指針 (內存可能在目標處理前被釋放)。
    → 解決:自定義消息 >= WM_USER,或用 WM_COPYDATA、共享內存。
  3. 跨進程指針無效:進程地址空間隔離,**絕不可**跨進程傳遞裸指針。必須用 WM_COPYDATA、管道、共享內存、RPC 等。
  4. 消息泵缺失:SendMessage 要求目標線程運行消息循環 (GetMessage/DispatchMessage);純工作線程無消息泵會導致發送線程永久阻塞。
    → 解決:工作線程用 PostThreadMessage + 自定義消息處理,或建立隱藏窗口跑消息泵。
  5. 廣播風暴:HWND_BROADCAST` 配合 `SendMessage` 會**依次阻塞等待每個頂層窗口處理**,極易導致系統假死。
    → 解決:廣播**只用** PostMessage,並註冊唯一消息 RegisterWindowMessage。

HTML5 postMessage 補充

瀏覽器端 window.postMessage(data, targetOrigin) 實現**跨文檔/跨域通信** (Cross-Document Messaging),屬異步,類似 PostMessage。監聽:

window.addEventListener('message', e => {
    if (e.origin !== 'https://trusted.com') return; // 安全校驗
    console.log('收到:', e.data);
});

支持 IE8+, Firefox 3+, Chrome 3+, Safari 4+, Opera 9+。

最佳實踐建議

  1. **默認優先 PostMessage** — 非阻塞、解耦、避免死鎖。
  2. **必須同步獲取結果時** 才用 SendMessage,並考慮 SendMessageTimeout 設置超時防卡死。
  3. **自定義消息** 一律用 RegisterWindowMessage 獲取全局唯一 ID,避免衝突。
  4. **跨進程數據傳輸** 統一用 WM_COPYDATA (小數據) 或 共享內存/命名管道 (大數據)。
  5. **調試技巧**:用 Spy++ (VS 自帶) 監控消息流向,確認消息是否到達、參數是否正確。

總結

PostMessage = **發並忘** (異步);SendMessage = **發並等** (同步)。理解二者在**線程模型、消息隊列、阻塞語義**上的本質差異,是 Windows GUI 程序穩健、高效的基石。

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