hur.cn - 华软网

 热门搜索

MFC学习第三天心得(GetMessage底层处理)—winapi篇

  作者:未知    来源:网络    更新时间:2011/9/29
BOOL GetMessage(LPMSG lpMsg,HWND hWnd,UINT wMsgFilterMin,UINT wMsgFilterMax)
在所有的书上,都只有简单的讲述,这条语句是从消息队列里取出一条消息放进lpmsg结构,如果HWND为NULL,就取线程里所有的窗口消息,如果为某一窗口的句柄,就只取那个窗口的所有消息(当然在wMsgfilterMin和wMsgFilterMax为0)的情况下。

可既然没有一本书上说getmessage内部是如何处理的!如何从系统和任务里的消息队列里提取消息!是如何检索系统和任务里的消息队列。当然我也无聊的时候弄弄!

(以下是经过汇编和结合部分reactos源码得出来的类似于windows源码,这只是个实现过程,非windows源码,大家看看就行,源码来自reactos)
C++">
BOOL STDCALL GetMessage(LPMSG lpmsg,HWND hwnd,UINT wMsgFilterMin,UINT wMsgFilterMax)
{
  BOOL Res;
  MSGCONVERSION Conversion;     //看程序能明白意思!
  NTUSERGETMESSAGEINFO info;    //消息结构,非MSG,得到的是内核层的消息
PUSER32_THREAD_DATA ThreadData=User32GetThreadData();//获得当前线程相关的数据
MsgConversionCleanup(lpmsg,false,false,null);//消息转换清除
Res=NtUserGetMessage(&info,hwnd,wMsgFilterMin,wMsgFilterMax);//获取窗口的消息到info
If(-1==(int)Res)
{
  Return Res;  //大家都懂的

}
/*以下过程,因为ntusergetmessage得到的消息是内核级的,因为内核级和用户级的消息结构是不同的,所以这里要把它转成用户级的。*/
Conversion.LParamsize=info.LParamsize;
Conversion.KMMsg=info.Msg;
If(!MsgiKMToUMMessage(&conversion.KMMsg,&conversion.UnicodeMsg))
{
   Return (BOOL)-1;
}
*lpmsg=Conversion.UnicodeMsg;//这里就是得到的消息放进lpmsg指针里
/*说实话,我到现在还不清楚下面的代码在整个程序里的作用,已经得到了消息为什么还要做如下处理*/
Conversion.Ansi=False;
Conversion.FinalMsg=lpmsg;
MsgConversionAdd(&Conversion);
// 这里我也不清楚为什么要把内核消息保存线程的数据里!
If(Res&&lpmsg->Message!=WM_PAINT&&lpmsg->message!=WM_QUIT)

ThreadData->LastMessage=info.msg;
}
Return Res;
}
}
/*以上的就是GetMessage的实现过程!大概意思也就是从内核级得到消息然后转换成用户级但是还是无法得知是如何从消息队列里获取消息的!只好接着往下弄.
Getmessage()NtuserGetMessage(••• •••)*/

BOOL STDCALL NtuserGetMessage(PNTUSERGETMESSAGEINFO unsafeinfo,HWND hwnd,UINT MsgFilterMin,UINT MsgFilterMax)
{
BOOL GotMessage;
NTUSERGETMESSAGEINFO info;
NTSTATUS Status;
PWINDOW_OBJECT Window=NULL;
PMSGMEMORY MsgMemoryEntry;
PVOID UserMem;
UINT Size;
USER_MESSAGE Msg;
DECLARE_RETURN(BOOL);
UserEnterExclusive();
If(hwnd&&!(Window=UserGetWindowObject(hwnd)))//这个就不解释哒
{
Return (-1);
}
//大家可以在winapi试一试,反正我是试过了
If(MsgFilterMax<MsgFilterMin)
{
  MsgFilterMin=0;
  MsgFilterMax=0;
}
Do
{/*根据窗口句柄,从相应窗口的消息队列中取下一个消息放入Msg结构!在这里我曾经奇
怪过,窗口句柄为空,他是如何找到相应窗口的!*/
  GotMessage=co_IntPeekMessage(&Msg,hwnd,MsgFilterMin,MsgFilterMax,PM_REMOVE);
{
If(GotMessage)
{
 Info.Msg=Msg.Msg;
//找到消息类型的描述项
MsgMemoryEntry=FindMsgMemory(info.Msg.message);
//看是否消息有附件!
If(NULL==MsgMemoryEntry)
{
Info.LParamSize=0;
}
Else
{
/*如果消息有附件,就分配内存缓冲区,复制附近到用户模式等等杂七杂八的东西
很麻烦,然后再把消息和附件组合成一起传给unsafeinfo(消息结构),其实从这里看,也
只知道ntusergetmessage是把内核的消息弄成一个完整体发给上一层的消息结构,看来核
心还要接着往下挖*/
••• ••• ••• ••• ••• •••
}
Else if(!co_IntWaitMessage(hwnd,MsfFilterMin,MsgFilterMax))//无消息,则等待
{
 Return  -1;
}
While(!GotMessage)
Return (WM_QUIT!=info.Msg.message);//这个大家就都清楚了
//••• ••• •••
// 扫尾工作,互斥啊等等
/*接着往下找核心层GetMessage()NtuserGetMessage()co_IntPeekMessage()*/
BOOL FASTCALL co_IntPeekMessage(PUSER_MESSAGE Msg,HWND hwnd, UINT MsgFilterMin,UINTMsgFilterMax,UINT RemoveMsg)
{
   LARGE_INTEGER LargeTickCount;
/*这里一定要说一下线程消息队列,在一个线程消息队列里,有7个小队列
  LIST_ENTRY SentMessagesListHead;  接到的”send”消息的队列
  LIST_ENTRY PostedMessagesListHead; 接到的”post”消息的队列
  LIST_ENTRY NotifyMessagesListHead;  接到的通知消息队列
  LIST_ENTRY HardwareMessagesListHead; 来自硬件设备的消息队列等等,我实在懒得打了,大概知道有这几个消息队列就行了*/

   PUSER_MESSAGE_QUEUE ThreadQueue;
   PUSER_MESSAGE Message;
   BOOL Present, RemoveMessages;
   USER_REFERENCE_ENTRY Ref;
   USHORT HitTest;
//得到线程的消息队列
ThreadQueue=(PUSER_MESSAGE_QUEUE)PsGetCurrentThreaeWin32Thread()->MessageQueue;
RemoveMessages=RemoveMsg&PM_REMOVE;//PM_REMOVE的意思是从消息队列得到消息后,//清除队列的消息
CheckMessages:
Present=FALSE;
//我的理解是最后一次peekmessage被调用的时间,我也不知道对不对.
KeQueryTickCount(&LargeTickCount);
ThreadQueue->LastMsgRead=LargeTickCount.u.LowPart;
//循环在消息队列里查找sendMessage,直到为0,
While(co_MsqDispatchOneSentMessage(ThreadQueue))
;
/*co_MsqDispatchOneSentMessage流程大概是,先从线程队列里SentMessagesListHead队列;取下一条发送消息,如果有钩子,就处理钩子,如果没有,就直接发到窗口处理函数,当然这里面的机制还是相当复杂的,而且是非常非常非常复杂的*/
//处理完发送消息,就会在这里查询是否有退出消息
If(ThreadQueu->QuitPosted)
{
 Msg->Msg.hwnd=NULL;
 Msg->Msg.message=WM_QUIT;
 Msg->Msg.wParam=ThreadQueue->QuitExitCode;
 Msg->Msg.lParam=0;
 Msg->FreeLParam=FALSE;//其实我到现在为止,我都不知道FreeLParam到底起个什么作用
//我的猜测是如果是true,有附件,false,没有附件!

If(RemoveMessages)
{
 ThreadQueue->QuitPosted=FALSE;
}
Return TRUE;
}
/*以下就是所有消息的处理,硬件消息要转换等等,实在打得太累了,你们自己看看吧,具体实现你们大概不感兴趣,只要知道大概消息的流程就差不多了!
Present = co_MsqFindMessage(ThreadQueue,
                               FALSE, //true为硬件消息,false为普通消息
                               RemoveMessages,
                               hWnd,
                               MsgFilterMin,
                               MsgFilterMax,
                               &Message);
   if (Present)
   {
      RtlCopyMemory(Msg, Message, sizeof(USER_MESSAGE));
      if (RemoveMessages)
      {
         MsqDestroyMessage(Message);
      }
      goto MessageFound;
   }

   //这里是硬件消息
   Present = co_MsqFindMessage(ThreadQueue,
                               TRUE,
                               RemoveMessages,
                               hWnd,
                               MsgFilterMin,
                               MsgFilterMax,
                               &Message);
   if (Present)
   {
      RtlCopyMemory(Msg, Message, sizeof(USER_MESSAGE));
      if (RemoveMessages)
      {
         MsqDestroyMessage(Message);
      }
      goto MessageFound;
   }

   //再次检查有没有发送消息,我也没有弄懂,为什么还要检查一次
   while (co_MsqDispatchOneSentMessage(ThreadQueue))
      ;

   //检查WM_PAINT消息
   if(IntGetPaintMessage(hWnd, MsgFilterMin, MsgFilterMax, PsGetCurrentThreadWin32Thread(), &Msg->Msg, RemoveMessages))
   {
      Msg->FreeLParam = FALSE;
      return TRUE;
   }

   //检查WM_(SYS)TIMER消息
   Present = MsqGetTimerMessage(ThreadQueue, hWnd, MsgFilterMin, MsgFilterMax,
                                &Msg->Msg, RemoveMessages);
   if (Present)
   {
      Msg->FreeLParam = FALSE;
      goto MessageFound;
   }

   if(Present)
   {
MessageFound:

      if(RemoveMessages)
      {
         PWINDOW_OBJECT MsgWindow = NULL;

         if(Msg->Msg.hwnd && (MsgWindow = UserGetWindowObject(Msg->Msg.hwnd)) &&
               Msg->Msg.message >= WM_MOUSEFIRST && Msg->Msg.message <= WM_MOUSELAST)
         {
            USHORT HitTest;

            UserRefObjectCo(MsgWindow, &Ref);

            if(co_IntTranslateMouseMessage(ThreadQueue, &Msg->Msg, &HitTest, TRUE))
              
            {
               UserDerefObjectCo(MsgWindow);
               
               goto CheckMessages;
            }

            if(ThreadQueue->CaptureWindow == NULL)
            {
               co_IntSendHitTestMessages(ThreadQueue, &Msg->Msg);
               if((Msg->Msg.message != WM_MOUSEMOVE && Msg->Msg.message != WM_NCMOUSEMOVE) &&
                     IS_BTN_MESSAGE(Msg->Msg.message, DOWN) &&
                     co_IntActivateWindowMouse(ThreadQueue, &Msg->Msg, MsgWindow, &HitTest))
               {
                  UserDerefObjectCo(MsgWindow);
                  
                  goto CheckMessages;
               }
            }
            
            UserDerefObjectCo(MsgWindow);
         }
         else
         {
            co_IntSendHitTestMessages(ThreadQueue, &Msg->Msg);
         }


         return TRUE;
      }

      if((Msg->Msg.hwnd && Msg->Msg.message >= WM_MOUSEFIRST && Msg->Msg.message <= WM_MOUSELAST) &&
            co_IntTranslateMouseMessage(ThreadQueue, &Msg->Msg, &HitTest, FALSE))
       
      {
        
         goto CheckMessages;
      }

      return TRUE;
   }

   return Present;
}
}
/*以上就是getmessage的一部分底层,因为这里面其实涉及到消息驱动的核心,是windows编程中核心的核心!基本要大概能够看得懂,没有一两个月的时间是不可能的,因为这都是我一手一手打出来的,实在太累了,如果下次有机会再打吧,如果对你们有帮助,非常高兴!*/




---华软 网友回答---
学习了


---华软网友回复---
如果对大家有帮助,希望大家多顶顶,这里面有几个我没搞懂的,希望大家一起讨论哈!这是我一手一手打出来的,请大家见谅!
---华软网友回复---
可既然没有一本书上说getmessage内部是如何处理的!如何从系统和任务里的消息队列里提取消息!是如何检索系统和任务里的消息队列。当然我也无聊的时候弄弄!
-----------------------
是应用程序的消息队列
---华软网友回复---
帮你顶顶,呵呵
---华软网友回复---
现在的书一般用程序队列,老一点的书籍一般喜欢叫任务队列,系统消息队列和任务消息队列,这是年代稍微老一点的书喜欢写的
---华软网友回复---
学习了呀
---华软网友回复---
学习!      
华软声明:本内容来自网络,如有侵犯您版权请来信指出,本站立即删除。