当前位置:   article > 正文

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

psmsreceivelist

一.应用层的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;

}


阅读全文
类别: 内核  查看评论

转载于:https://www.cnblogs.com/retme/archive/2012/03/26/2494892.html

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/247297
推荐阅读
相关标签
  

闽ICP备14008679号