Win32k(2) 报文驱动的通信机制

本文探讨Win32k系统中的报文驱动通信机制,包括应用层API如WndProc函数,消息结构,接收报文过程如NtUserGetMessage,以及发送报文的PostMessage和SendMessage详情。讲解了消息如何在窗口对象、消息队列和线程间传递,并涉及到窗口过程和消息钩子的调用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.应用层的api

int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
intnCmdShow)
{
    WNDCLASSEXwcex;
    wcex.lpfnWndProc  = WndProc;
    wcex.cbClsExtra      = 0;
RegisterClassEx(&wcex);
 
    hWnd= CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, 0,CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
 
    while(GetMessage(&msg, NULL, 0, 0))//NtUserGetMessage
    {
// NtUserGetMessage目的是循环获取一个普通报文,
在这个循环中会检查被发送的报文,对方send过来的message会由NtUserGetMessage内部直接调用wndproc立即响应
//如果没有普通报文(post 硬件报文定时器报文等)就睡眠循环等待
       if(!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
       {
           TranslateMessage(&msg);//键盘扫描码变成unicode
           DispatchMessage(&msg);//对wndproc的调用
       }
    }

然后在WndProc函数中还能调用到SendMessage、PostQuitMessage等等函数

 

应用层消息的结构是

struct MSG
{
   HWND hwnd;
   UINT message;
   WPARAM wParam;
   LPARAM lParam;
   DWORD time;
   POINT pt;
};

二.创建窗口NtUserCreateWindowEx

控件什么也都算window。

创建好的窗口对应的是window_object返回句柄。

Window_object里面包含了许多字段,里面就有用户填充的WndProc地址,消息队列结构


三.接收报文NtUserGetMessage

 

根据HWNDhWnd在全局句柄表中找到窗口

 

主要关注co_IntPeekMessage这个调用,他从w32thread里面拆取消息队列中的消息

Win下

typedefstruct _tagTHREADINFO           // 156 elements, 0x208 bytes (sizeof)
{
/*0x0BC*/     struct _tagQ* pq;// input queue
/*0x0E0*/     struct _tagSMS* psmsSent;// send queue (sent)
/*0x0E4*/     struct _tagSMS* psmsCurrent;// send queue (current)
/*0x0E8*/     struct _tagSMS* psmsReceiveList;// send queue (received)
/*0x174*/     struct _tagMLISTmlPost;// post queue
} tagTHREADINFO, *PtagTHREADINFO;

Ros下

typedef struct _USER_MESSAGE_QUEUE
{
/* Reference counter, only access this variable with interlocked functions! */
LONGReferences;
/* Owner of the message queue */
struct _ETHREAD *Thread;
/* Queue of messages sent to the queue. */
LIST_ENTRYSentMessagesListHead;
/* Queue of messages posted to the queue. */
LIST_ENTRYPostedMessagesListHead;
/* Queue of sent-message notifies for the queue. */
LIST_ENTRYNotifyMessagesListHead;
/* Queue for hardware messages for the queue. */
LIST_ENTRYHardwareMessagesListHead;
/* List of timers, sorted on expiry time (earliest first) */
LIST_ENTRYTimerListHead;
 
/* messages that are currently dispatched by other threads */
LIST_ENTRYDispatchingMessagesHead;
/* messages that are currently dispatched by this message queue, required for cleanup */
LIST_ENTRYLocalDispatchingMessagesHead;

(1)获取send报文:

同步方式的消息传递

 

发送方要把消息挂入发送方的DispatchingMessagesHead,也挂入接收方的SentMessagesListHead,等待Message->CompletionEvent

 

接收方从SentMessagesListHead提出消息挂入自己的LocalDispatchingMessagesHead,调用窗口的wndproc或者其消息钩子,完成后摘除,用Message->CompletionEvent通知发送方

BOOLFASTCALL
co_IntPeekMessage(PUSER_MESSAGEMsg,
PWINDOW_OBJECTWindow,
UINTMsgFilterMin,
UINTMsgFilterMax,
UINTRemoveMsg)
{
 
。。。。。。
while (co_MsqDispatchOneSentMessage(ThreadQueue))
;
这一句从循环摘消息发送消息
 
BOOLEAN FASTCALL
co_MsqDispatchOneSentMessage(PUSER_MESSAGE_QUEUE MessageQueue)
{
。。。。。
 
//SentMessagesListHead中获取消息
   Entry = RemoveHeadList(&MessageQueue->SentMessagesListHead);
   Message = CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry);
 
//放入LocalDispatchingMessagesHead
InsertTailList(&MessageQueue->LocalDispatchingMessagesHead,
&Message->ListEntry);
//消息钩子另行处理
if (Message->HookMessage == MSQ_ISHOOK)
   {
      Result = co_HOOK_CallHooks(。。。)
;
   }
elseif (Message->HookMessage == MSQ_ISEVENT)
   {
      Result = co_EVENT_CallEvents(。。。);                                  
   }
else
   {
//调用应用层窗口函数wndproc
        Result = co_IntSendMessage(。。。);
   }
//已经送达就可以删除了
RemoveEntryList(&Message->ListEntry);
 
/* remove the message from the dispatching list, so lock the sender's message queue */
SenderReturned = (Message->DispatchingListEntry.Flink == NULL);
if(!SenderReturned)
   {
//从DispatchingListEntry中移除
RemoveEntryList(&Message->DispatchingListEntry);
   }
/* Let the sender know the result. */
if (Message->Result != NULL)
   {
//返回wndproc执行的结果
      *Message->Result = Result;
   }
 
/* Notify the sender. */
if (Message->CompletionEvent != NULL)
   {
//通知发送方可以唤醒
KeSetEvent(Message->CompletionEvent, IO_NO_INCREMENT, FALSE);
   }
 
//对方回调函数的处理,略
/* Notify the sender if they specified a callback. */
if (!SenderReturned&& Message->CompletionCallback != NULL)
   {
。。。
   }
。。。
/* free the message */
ExFreePool(Message);
return(TRUE);
}

这里面的co_IntSendMessage最终调用co_IntCallWindowProc

在调用KeUserModeCallback(USER32_CALLBACK_WINDOWPROC。。。返回r3

此处暂略


(2)获取其他报文

NtUserGetMessage -co_IntPeekMessage


BOOLFASTCALL
co_IntPeekMessage(PUSER_MESSAGEMsg,
PWINDOW_OBJECTWindow,
UINTMsgFilterMin,
UINTMsgFilterMax,
UINTRemoveMsg)
{
 
。。。。。。//sent报文的处理,已经分析过
while (co_MsqDispatchOneSentMessage(ThreadQueue))
//接下来看其他报文,伪代码
 
一般的post报文
/* Now check for normal messages. */
Present = co_MsqFindMessage(ThreadQueue,
if (Present)
{
gotoMessageFound;
}
硬件报文
/* Check for hardware events. */
Present = co_MsqFindMessage(ThreadQueue,
if (Present)
{
gotoMessageFound;
}
再次检查sent报文
/* Check for sent messages again. */
while (co_MsqDispatchOneSentMessage(ThreadQueue))
paint报文
/* Check for paint messages. */
if (IntGetPaintMessage(Window, MsgFilterMin, MsgFilterMax, pti, &Msg->Msg, RemoveMessages))
{
Msg->FreeLParam = FALSE;
gotoMsgExit;
}
定时器报文(包括鼠标)
Present = MsqGetTimerMessage(ThreadQueue, Window, MsgFilterMin, MsgFilterMax,
&Msg->Msg, RemoveMessages);
if (Present)
{
Msg->FreeLParam = FALSE;
gotoMessageFound;
}
 

报文可以来自于其他窗口、程序发送的,也可能是鼠标键盘线程投递的


(3)DispatchMessage对应NtUserDispatchMessage这个也简单了,就是把消息派发出去,调用wndproc或者hook

四.发送视窗报文

(1)Post message

Post很简单,插入链表发送消息根据Wnd找到对应window object,然后插入其post链表并且时间通知有消息了

 

 

NtUserPostMessage–UserPostMessage - UserPostThreadMessage - MsqPostMessage

VOIDFASTCALL
MsqPostMessage(PUSER_MESSAGE_QUEUEMessageQueue, MSG* Msg, BOOLEANFreeLParam,
DWORDMessageBits)
{
PUSER_MESSAGEMessage;
 
if(!(Message = MsqCreateMessage(Msg, FreeLParam)))
   {
return;
   }
InsertTailList(&MessageQueue->PostedMessagesListHead,
&Message->ListEntry);
MessageQueue->QueueBits |= MessageBits;
MessageQueue->ChangedBits |= MessageBits;
if (MessageQueue->WakeMask&MessageBits)
KeSetEvent(MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
}

(2)send message

如果是向自身线程发送消息,就不用send了,直接回调本线程wndproc

否则的话就调用co_MsqSendMessage

co_MsqSendMessage(PUSER_MESSAGE_QUEUE MessageQueue,//接收方队列
                  HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam,
                  UINT uTimeout, BOOL Block, INT HookMessage,
                  ULONG_PTR *uResult)
{
……
if(!(Message = ExAllocatePoolWithTag(PagedPool, sizeof(USER_SENT_MESSAGE), TAG_USRMSG)))
……
 
KeInitializeEvent(&CompletionEvent, NotificationEvent, FALSE);
 
pti = PsGetCurrentThreadWin32Thread();
ThreadQueue = pti->MessageQueue; //发送方队列
 
Timeout.QuadPart = (LONGLONG) uTimeout * (LONGLONG) -10000;
 
……//初始化Message结构
 
/* 挂入两个队列自己进程的DispatchingListEntry,对方进程的SentMessagesListHead* /
InsertTailList(&ThreadQueue->DispatchingMessagesHead, &Message->DispatchingListEntry);
InsertTailList(&MessageQueue->SentMessagesListHead, &Message->ListEntry);
 
……//如果对方正在等待报文,将其唤醒
if (MessageQueue->WakeMask&QS_SENDMESSAGE)
KeSetEvent(MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
 
//
if(Block)//阻塞方式,单纯等待这个消息被完成的事件CompletionEvent
   {
……
/* 等待这个报文得到处理*/
WaitStatus = KeWaitForSingleObject(&CompletionEvent, UserRequest, UserMode,
                                         FALSE, (uTimeout ?&Timeout : NULL));
 
……
//超时的话
if(WaitStatus == STATUS_TIMEOUT)
      {
/* 从对方的sent链表里面消除消息*/
         Entry = MessageQueue->SentMessagesListHead.Flink;
while (Entry != &MessageQueue->SentMessagesListHead)
         {
if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry)
                  == Message)
            {
               Message->CompletionEvent = NULL;
               Message->Result = NULL;
break;
            }
            Entry = Entry->Flink;
         }
 
/*从自己的Dispatching消除消息 */
         Entry = ThreadQueue->DispatchingMessagesHead.Flink;
while (Entry != &ThreadQueue->DispatchingMessagesHead)
         {
if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, DispatchingListEntry)
                  == Message)
            {
               Message->CompletionEvent = NULL;
               Message->Result = NULL;
RemoveEntryList(&Message->DispatchingListEntry);
               Message->DispatchingListEntry.Flink = NULL;
break;
            }
            Entry = Entry->Flink;
         }
      }
//这里要捎带脚吧发过来的消息处理一下
while (co_MsqDispatchOneSentMessage(ThreadQueue));
   }
else//非阻塞方式,等待这个消息被完成的事件CompletionEvent以及有对方的报文被贴过来时短暂唤醒,但并不处理这些
   {
 PVOID WaitObjects[2];
 
WaitObjects[0] = &CompletionEvent;
WaitObjects[1] = ThreadQueue->NewMessages;
do
      {
IdlePing();
 
UserLeaveCo();
 
WaitStatus = KeWaitForMultipleObjects(2, WaitObjects, WaitAny, UserRequest,
UserMode, FALSE, (uTimeout ?&Timeout : NULL), NULL);
 
……
//超时的话和上面一样,移除两个链表中的消息,处理sent链表上对方发送过来的消息
      }
while (NT_SUCCESS(WaitStatus) && STATUS_WAIT_0 != WaitStatus); //只要CompletionEvent不满足就要等待
   }
 
if(WaitStatus != STATUS_TIMEOUT)
      *uResult = (STATUS_WAIT_0 == WaitStatus ?Result : -1);
 
returnWaitStatus;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值