日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

Windows消息机制学习笔记(三)—— 消息的接收与分发

發布時間:2025/3/21 windows 16 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Windows消息机制学习笔记(三)—— 消息的接收与分发 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Windows消息機制學習筆記(三)—— 消息的接收與分發

    • 要點回顧
    • 消息循環
    • 消息隊列
    • 消息的接收
      • GetMessage
      • 實驗1:理解GetMessage
        • 第一步:編譯并運行程序A
        • 第二步:編譯并運行程序B
      • 同步與異步
      • 實驗2:理解同步與異步
        • 第一步:編譯并運行實驗1中的程序A
        • 第二步:編譯并運行實驗1中的程序B
        • 第三步:點擊確定按鈕
        • 第四步:修改程序B的代碼,再次運行
    • 消息的分發
      • DispatchMessage
      • 實驗3:理解DispatchMessage
        • 第一步:編譯并運行以下代碼
        • 第二步:操作窗口
        • 第三步:去掉DispatchMessage注釋,重新運行
        • 第四步:再次操作窗口
      • TranslateMessage
      • 實驗4:理解TranslateMessage
        • 第一步:編譯并運行以下代碼
        • 第二步:敲擊不同按鍵
        • 第三步:修改代碼并重新運行程序
        • 第四步:敲擊不同按鍵
    • 總結

要點回顧

1)一個GUI線程包含一個消息隊列

普通線程 ↓ GUI線程 ↓ THEWAD.W32THREAD ↓ THREADINFO ↓ 消息隊列

2)一個線程可以包含多個窗口,所有窗口共享一個消息隊列

_WINDOW_OBJECT //0環創建 ↓ PTHREADINFO pti //所屬線程 ↓ WNDPROC lpfnWndProc //窗口過程(窗口回調函數)

消息循環

MSG msg; while(GetMessage(&msg, NULL, 0, 0)) //從消息隊列中取出消息 {TranslateMessage(&msg); //加工消息DispatchMessage(&msg); //分發消息 }

思考:使用SendMessage與PostMessage發送的消息位于同一組隊列中嗎?
答案:不同類型的消息被置于不同隊列。

消息隊列

描述:消息隊列共有七組,用于存放不同類型的消息。
例如

1)SentMessagesListHead //接收SendMessage發來的消息 2)PostedMessagesListHead //接收PostMessage發來的消息 3)HardwareMessagesListHead //接收鍵盤、鼠標等硬件設備的消息 ...

完整隊列

//ReactOS v3.12 typedef struct _USER_MESSAGE_QUEUE {.../* Owner of the message queue */struct _ETHREAD *Thread;/* Queue of messages sent to the queue. */LIST_ENTRY SentMessagesListHead;/* Queue of messages posted to the queue. */LIST_ENTRY PostedMessagesListHead;/* Queue of sent-message notifies for the queue. */LIST_ENTRY NotifyMessagesListHead;/* Queue for hardware messages for the queue. */LIST_ENTRY HardwareMessagesListHead;.../* messages that are currently dispatched by other threads */LIST_ENTRY DispatchingMessagesHead;/* messages that are currently dispatched by this message queue, required for cleanup */LIST_ENTRY LocalDispatchingMessagesHead;... }

消息的接收

GetMessage

描述:從消息隊列中取出消息

BOOL?WINAPI?GetMessage(LPMSG lpMsg, //返回從隊列中取出的消息HWND hWnd, //過濾條件一:窗口句柄UINT wMsgFilterMin, //過濾條件二:最小值UINT wMsgFilterMax //過濾條件三:最大值 );

主要功能:循環判斷是否存在屬于該窗口的消息,若存在,則將消息存儲到MSG指定的結構中,并將消息從列表中刪除。

注意:事實上,GetMessage還做了一件很重要的事情,即在接收消息時,將SentMessagesListHead中的消息進行處理

大致流程

User32!GetMessage ↓ w32k!NtUsrGetMessage ↓ do {//先判斷SentMessagesListHead中是否存在消息,若存在則進行處理do{...KeUserModeCallback(USER32_CALLBACK_WINDOWPROC,Arguments,ArgumentLength,&ResultPinter,&ResultLength);...} }while(SentMessagesListHead != NULL)

源碼(ReactOS v3.12)

BOOL APIENTRY NtUserGetMessage( PNTUSERGETMESSAGEINFO UnsafeInfo,HWND hWnd,UINT MsgFilterMin,UINT MsgFilterMax ) {...do{GotMessage = co_IntPeekMessage(&Msg, Window, MsgFilterMin, MsgFilterMax, PM_REMOVE);...}while (! GotMessage); //對應外層循環... } BOOL FASTCALL co_IntPeekMessage( PUSER_MESSAGE Msg,PWINDOW_OBJECT Window,UINT MsgFilterMin,UINT MsgFilterMax,UINT RemoveMsg ) {.../* Dispatch sent messages here. */while (co_MsqDispatchOneSentMessage(ThreadQueue)); //對應內層循環... } BOOLEAN FASTCALL co_MsqDispatchOneSentMessage(PUSER_MESSAGE_QUEUE MessageQueue) {...if (Message->HookMessage == MSQ_ISHOOK){...}else if (Message->HookMessage == MSQ_ISEVENT){...}else{/* 發送消息 */Result = co_IntSendMessage(Message->Msg.hwnd,Message->Msg.message,Message->Msg.wParam,Message->Msg.lParam);}/* 從消息隊列中刪除該消息 */RemoveEntryList(&Message->ListEntry);.../* 調用回調函數 */if (Message->CompletionCallback != NULL){co_IntCallSentMessageCallback(Message->CompletionCallback,Message->Msg.hwnd,Message->Msg.message,Message->CompletionCallbackContext,Result);}... } VOID APIENTRY co_IntCallSentMessageCallback(SENDASYNCPROC CompletionCallback,HWND hWnd,UINT Msg,ULONG_PTR CompletionCallbackContext,LRESULT Result) {.../* 回到三環進行處理 */Status = KeUserModeCallback(USER32_CALLBACK_SENDASYNCPROC,&Arguments,sizeof(SENDASYNCPROC_CALLBACK_ARGUMENTS),&ResultPointer,&ResultLength);... }

實驗1:理解GetMessage

第一步:編譯并運行程序A

#include <windows.h>LRESULT CALLBACK WindowProc(IN HWND hwnd,IN UINT uMsg,IN WPARAM wParam,IN LPARAM lParam ){switch(uMsg){case 0x401:MessageBoxA(NULL, "測試窗口接收到消息", "新消息", MB_OK);return false;}return DefWindowProc(hwnd, uMsg, wParam, lParam); }int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd ){//窗口的類名TCHAR className[] = "My First Window";//創建一個自己的窗口WNDCLASS wndclass = {0};wndclass.hbrBackground = (HBRUSH)COLOR_MENU;wndclass.lpfnWndProc = WindowProc;wndclass.lpszClassName = className;wndclass.hInstance = hInstance;//注冊RegisterClass(&wndclass);//創建窗口HWND hwnd = CreateWindow(className,TEXT("測試窗口"),WS_OVERLAPPEDWINDOW,10,10,600,300,NULL,NULL,hInstance,NULL);if(hwnd == NULL)return 0;//顯示窗口ShowWindow(hwnd, SW_SHOW);//消息循環MSG msg;while(GetMessage(&msg, NULL, 0, 0)){//TranslateMessage(&msg);//DispatchMessage(&msg);}return 0; }

運行結果

注意:不要關閉窗口

第二步:編譯并運行程序B

#include <stdio.h> #include <windows.h>int main() {HWND hwnd = FindWindow("My First Window", "測試窗口");SendMessage(hwnd, 0x401, 0, 0);return 0; }

運行結果

同步與異步

描述

  • 使用SendMessage發送消息時,會等待消息處理之后再返回,這個過程稱為同步
  • 使用PostMessage發送消息時,不會等待消息處理,直接返回,這種方式稱為異步
  • 實驗2:理解同步與異步

    第一步:編譯并運行實驗1中的程序A

    運行結果

    第二步:編譯并運行實驗1中的程序B

    在return處設置斷點
    運行結果
    此時,程序B并未繼續向下執行。

    第三步:點擊確定按鈕

    執行結果
    程序A處理消息后,程序B繼續向下執行。

    第四步:修改程序B的代碼,再次運行

    新代碼

    #include <stdio.h> #include <windows.h>int main() {HWND hwnd = FindWindow("My First Window", "測試窗口");PostMessage(hwnd, 0x401, 0, 0); //SendMessage改為PostMessagereturn 0; }

    執行結果
    程序B發送消息后未等待目標處理繼續向下執行,程序A并未處理消息。

    消息的分發

    描述:GetMessage能夠處理SentMessagesListHead隊列中的消息,其他隊列中的消息則由DispatchMessage進行分發處理。

    大致流程

  • 根據窗口句柄找到窗口對象
  • 根據窗口對象得到窗口過程函數,由0環進行調用。
  • 源碼(ReactOS v3.12)

    LRESULT APIENTRY NtUserDispatchMessage(PMSG UnsafeMsgInfo) {...if (!Hit) Res = IntDispatchMessage(&SafeMsg);... } LRESULT FASTCALL IntDispatchMessage(PMSG pMsg) {...retval = co_IntCallWindowProc( Window->Wnd->lpfnWndProc,!Window->Wnd->Unicode,pMsg->hwnd,pMsg->message,pMsg->wParam,lParamPacked,lParamBufferSize);... } LRESULT APIENTRY co_IntCallWindowProc(WNDPROC Proc,BOOLEAN IsAnsiProc,HWND Wnd,UINT Message,WPARAM wParam,LPARAM lParam,INT lParamBufferSize) {.../* 回到三環,調用窗口過程函數 */Status = KeUserModeCallback(USER32_CALLBACK_WINDOWPROC,Arguments,ArgumentLength,&ResultPointer,&ResultLength);... }

    DispatchMessage

    描述:用于消息分發,根據窗口句柄調用相關的窗口過程,通常用于分發由GetMessage函數檢索到的消息。

    LRESULT DispatchMessage(CONST MSG *lpmsg // message information );

    思考:為什么DispatchMessage要對消息進行分發而不統一處理?
    答案:所有窗口共享一個消息隊列,且每個窗口都擁有屬于自己的回調函數。

    MSG結構體成員

    typedef struct tagMSG {HWND hwnd; //窗口句柄UINT message; WPARAM wParam; LPARAM lParam; DWORD time; POINT pt; } MSG, *PMSG;

    DispatchMessage根據MSG->hwnd調用各句柄對應窗口對象的回調函數。

    思考:既然GetMessage已經得到了句柄信息,為什么不能直接調用對應的回調函數而需要分發。
    答案:因為句柄指向的是句柄表,而不是窗口對象,需要進入0環通過句柄表找到窗口對象,才能調用對應的回調函數。

    實驗3:理解DispatchMessage

    第一步:編譯并運行以下代碼

    #include <stdio.h> #include <windows.h>LRESULT CALLBACK WindowProc(IN HWND hwnd,IN UINT uMsg,IN WPARAM wParam,IN LPARAM lParam ){switch(uMsg){case 0x401:{MessageBoxA(NULL, "測試窗口接收到消息", "新消息", MB_OK);return 0;}case WM_DESTROY:{ExitProcess(0);return 0;}}return DefWindowProc(hwnd, uMsg, wParam, lParam); }int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd ){//窗口的類名TCHAR className[] = "My First Window";//創建一個自己的窗口WNDCLASS wndclass = {0};wndclass.hbrBackground = (HBRUSH)COLOR_MENU;wndclass.lpfnWndProc = WindowProc;wndclass.lpszClassName = className;wndclass.hInstance = hInstance;//注冊RegisterClass(&wndclass);//創建窗口HWND hwnd = CreateWindow(className,TEXT("測試窗口"),WS_OVERLAPPEDWINDOW,10,10,600,300,NULL,NULL,hInstance,NULL);if(hwnd == NULL)return 0;//顯示窗口ShowWindow(hwnd, SW_SHOW);//消息循環MSG msg;while(GetMessage(&msg, NULL, 0, 0)){//TranslateMessage(&msg);//DispatchMessage(&msg);}return 0; }

    運行結果

    第二步:操作窗口

    拖動窗口、點擊最小化、縮放、關閉按鈕均無反應

    第三步:去掉DispatchMessage注釋,重新運行

    MSG msg; while(GetMessage(&msg, NULL, 0, 0)) {//TranslateMessage(&msg);DispatchMessage(&msg); }

    第四步:再次操作窗口

    拖動窗口、點擊最小化、縮放、關閉按鈕均得到相應

    TranslateMessage

    描述:用于將虛擬鍵碼轉換為字符消息。該字符消息又被發送給對應線程(調用TranslateMessage的線程)的消息隊列,當線程再次調用GetMessage函數或PeekMessage函數獲取消息的時候被讀取。

    實驗4:理解TranslateMessage

    第一步:編譯并運行以下代碼

    #include <stdio.h> #include <windows.h>LRESULT CALLBACK WindowProc(IN HWND hwnd,IN UINT uMsg,IN WPARAM wParam,IN LPARAM lParam ){TCHAR szBuffer[MAX_PATH];switch(uMsg){/*case WM_KEYDOWN:{sprintf(szBuffer, "鍵%d被你按下了\x00", wParam);MessageBox(NULL, szBuffer, "WM_KEYDOWN", MB_OK);return 0;}*/case WM_CHAR:{sprintf(szBuffer, "你按下了%c鍵\x00", wParam);MessageBox(NULL, szBuffer, "WM_CHAR", MB_OK);return 0;}}return DefWindowProc(hwnd, uMsg, wParam, lParam); }int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd ){//窗口的類名TCHAR className[] = "My First Window";//創建一個自己的窗口WNDCLASS wndclass = {0};wndclass.hbrBackground = (HBRUSH)COLOR_MENU;wndclass.lpfnWndProc = WindowProc;wndclass.lpszClassName = className;wndclass.hInstance = hInstance;//注冊RegisterClass(&wndclass);//創建窗口HWND hwnd = CreateWindow(className,TEXT("測試窗口"),WS_OVERLAPPEDWINDOW,10,10,600,300,NULL,NULL,hInstance,NULL);if(hwnd == NULL)return 0;//顯示窗口ShowWindow(hwnd, SW_SHOW);//消息循環MSG msg;while(GetMessage(&msg, NULL, 0, 0)){TranslateMessage(&msg);DispatchMessage(&msg);}return 0; }

    運行結果

    第二步:敲擊不同按鍵

    1)小寫模式,按下"A":

    2)按下大小寫鍵,切換到大寫模式:
    無彈窗

    3)大寫模式,按下”A“:

    4)按下shift鍵:
    無彈窗

    5)小寫模式,按下shift+“A”:

    第三步:修改代碼并重新運行程序

    新代碼

    #include <stdio.h> #include <windows.h>LRESULT CALLBACK WindowProc(IN HWND hwnd,IN UINT uMsg,IN WPARAM wParam,IN LPARAM lParam ){TCHAR szBuffer[MAX_PATH];switch(uMsg){case WM_KEYDOWN:{sprintf(szBuffer, "鍵%d被你按下了\x00", wParam);MessageBox(NULL, szBuffer, "WM_KEYDOWN", MB_OK);return 0;}/*case WM_CHAR:{sprintf(szBuffer, "你按下了%c鍵\x00", wParam);MessageBox(NULL, szBuffer, "WM_CHAR", MB_OK);return 0;}*/}return DefWindowProc(hwnd, uMsg, wParam, lParam); }int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd ){//窗口的類名TCHAR className[] = "My First Window";//創建一個自己的窗口WNDCLASS wndclass = {0};wndclass.hbrBackground = (HBRUSH)COLOR_MENU;wndclass.lpfnWndProc = WindowProc;wndclass.lpszClassName = className;wndclass.hInstance = hInstance;//注冊RegisterClass(&wndclass);//創建窗口HWND hwnd = CreateWindow(className,TEXT("測試窗口"),WS_OVERLAPPEDWINDOW,10,10,600,300,NULL,NULL,hInstance,NULL);if(hwnd == NULL)return 0;//顯示窗口ShowWindow(hwnd, SW_SHOW);//消息循環MSG msg;while(GetMessage(&msg, NULL, 0, 0)){//TranslateMessage(&msg);DispatchMessage(&msg);}return 0; }

    運行結果

    第四步:敲擊不同按鍵

    1)小寫模式,按下"A":
    2)按下大小寫鍵,切換到大寫模式:

    3)大寫模式,按下”A“:

    4)按下shift鍵:

    總結

  • GetMessage的不止能夠取出消息,還能夠處理消息
  • GetMessage處理消息時,從0環回到3環調用窗口過程函數進行處理。
  • SendMessage發送消息后會等待目標處理完畢,PostMessage不會。
  • GetMessage只會處理SentMessagesListHead列表中的消息。
  • DispatchMessage的作用是分發除了SentMessagesListHead隊列以外的消息給各窗口對象進行處理。
  • TranslateMessage只處理鍵盤消息,將虛擬鍵碼轉換為字符消息,再由DispatchMessage進行分發。
  • 未經過TranslateMessage處理的鍵盤消息,虛擬鍵碼由WM_KEYDOWN進行處理;經過TranslateMessage處理的鍵盤消息,字符消息由WM_CHAR進行處理
  • 總結

    以上是生活随笔為你收集整理的Windows消息机制学习笔记(三)—— 消息的接收与分发的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。