一.应用层的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下
typedefstruct_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;
}
阅读全文
类别: 内核 查看评论