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

来源:互联网 发布:notepad pro mac 编辑:程序博客网 时间:2024/05/20 22:38

一.应用层的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通知发送方

BOOLFASTCALLco_IntPeekMessage(PUSER_MESSAGEMsg,PWINDOW_OBJECTWindow,UINTMsgFilterMin,UINTMsgFilterMax,UINTRemoveMsg){ 。。。。。。while (co_MsqDispatchOneSentMessage(ThreadQueue));这一句从循环摘消息发送消息 BOOLEAN FASTCALLco_MsqDispatchOneSentMessage(PUSER_MESSAGE_QUEUE MessageQueue){。。。。。 //SentMessagesListHead中获取消息   Entry = RemoveHeadList(&MessageQueue->SentMessagesListHead);   Message = CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry); //放入LocalDispatchingMessagesHeadInsertTailList(&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


BOOLFASTCALLco_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

VOIDFASTCALLMsqPostMessage(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;}



原创粉丝点击