windows message manager
来源:互联网 发布:批量导入数据到excel 编辑:程序博客网 时间:2024/05/22 07:54
SendMessage
窗口过程函数的调用有两个入口,一个是自己的线程给自己窗口发通知,这样直接调用内部函数进行调用,使用IntCallMessageProc来调用函数,另一种方式是转换用户消息为内核消息,调用NtUserMessageCall来传递消息。
LRESULT WINAPI SendMessageW(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam){ MSG UMMsg, KMMsg; LRESULT Result; PWND Window; PTHREADINFO ti = GetW32ThreadInfo(); if ( Msg & ~WM_MAXIMUM ) { SetLastError( ERROR_INVALID_PARAMETER ); return 0; } if (Wnd != HWND_TOPMOST && Wnd != HWND_BROADCAST && (Msg < WM_DDE_FIRST || Msg > WM_DDE_LAST)) //进行前期参数判断,要求窗体句柄不能超过范围,消息号也要在正常的范围中。 { Window = ValidateHwnd(Wnd); //根据窗体句柄获取具体窗体 if ( Window != NULL && Window->head.pti == ti && !ISITHOOKED(WH_CALLWNDPROC) && !ISITHOOKED(WH_CALLWNDPROCRET) && !(Window->state & WNDS_SERVERSIDEWINDOWPROC) ) { /* NOTE: We can directly send messages to the window procedure if *all* the following conditions are met: * Window belongs to calling thread * The calling thread is not being hooked for CallWndProc * Not calling a server side proc: Desktop, Switch, ScrollBar, Menu, IconTitle, or hWndMessage */ return IntCallMessageProc(Window, Wnd, Msg, wParam, lParam, FALSE); } } UMMsg.hwnd = Wnd; UMMsg.message = Msg; UMMsg.wParam = wParam; UMMsg.lParam = lParam; if (! MsgiUMToKMMessage(&UMMsg, &KMMsg, FALSE)) { return FALSE; } Result = NtUserMessageCall( Wnd, KMMsg.message, KMMsg.wParam, KMMsg.lParam, (ULONG_PTR)&Result, FNID_SENDMESSAGE, FALSE); MsgiUMToKMCleanup(&UMMsg, &KMMsg); return Result;}
函数提取出windows 的消息处理例程
另外一种,往其他进程或线程传递消息。我们使用的是NtUserMessageCall,在调用之前,我们转换用户信息到内核信息,就是进行数据拷贝。
NtUserMessageCall中有个switch ,我们传递的参数是FNID_SENDMESSAGE,指定我们是发送消息。于是乎我们进入函数中,可以看到如下,我们调用win32k的内部发送函数,然后记录调用的结果,返回给用户。
case FNID_SENDMESSAGE: { Ret = co_IntDoSendMessage(hWnd, Msg, wParam, lParam, 0); if (ResultInfo) { _SEH2_TRY { ProbeForWrite((PVOID)ResultInfo, sizeof(ULONG_PTR), 1); RtlCopyMemory((PVOID)ResultInfo, &Ret, sizeof(ULONG_PTR)); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Ret = FALSE; _SEH2_YIELD(break); } _SEH2_END; } break; }
函数中定义的消息如下:
// FNID's for NtUserSetWindowFNID, NtUserMessageCall#define FNID_FIRST 0x029A#define FNID_SCROLLBAR 0x029A#define FNID_ICONTITLE 0x029B#define FNID_MENU 0x029C#define FNID_DESKTOP 0x029D#define FNID_DEFWINDOWPROC 0x029E#define FNID_MESSAGEWND 0x029F#define FNID_SWITCH 0x02A0#define FNID_BUTTON 0x02A1#define FNID_COMBOBOX 0x02A2#define FNID_COMBOLBOX 0x02A3#define FNID_DIALOG 0x02A4#define FNID_EDIT 0x02A5#define FNID_LISTBOX 0x02A6#define FNID_MDICLIENT 0x02A7#define FNID_STATIC 0x02A8#define FNID_IME 0x02A9#define FNID_GHOST 0x02AA#define FNID_CALLWNDPROC 0x02AB#define FNID_CALLWNDPROCRET 0x02AC#define FNID_HKINLPCWPEXSTRUCT 0x02AD#define FNID_HKINLPCWPRETEXSTRUCT 0x02AE#define FNID_MB_DLGPROC 0x02AF#define FNID_MDIACTIVATEDLGPROC 0x02B0#define FNID_SENDMESSAGE 0x02B1#define FNID_SENDMESSAGEFF 0x02B2// Kernel has option to use TimeOut or normal msg send, based on type of msg.#define FNID_SENDMESSAGEWTOOPTION 0x02B3#define FNID_SENDMESSAGECALLPROC 0x02B4#define FNID_BROADCASTSYSTEMMESSAGE 0x02B5#define FNID_TOOLTIPS 0x02B6#define FNID_SENDNOTIFYMESSAGE 0x02B7#define FNID_SENDMESSAGECALLBACK 0x02B8#define FNID_LAST 0x02B9
接着我们来研究下内核如何传递这个消息的,来看看内部的co_IntDoSendMessage
同样函数中我们判断句柄是否有效,我们根据窗口句柄使用UserGetWindowObject 来查找窗体wnd,并判断窗体是否存在。如果不存在返回。这个是从全局句柄表中查找的窗体
static LRESULT FASTCALLco_IntDoSendMessage( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, PDOSENDMESSAGE dsm){ LRESULT Result = TRUE; NTSTATUS Status; PWND Window = NULL; MSG UserModeMsg, KernelModeMsg; PMSGMEMORY MsgMemoryEntry; PTHREADINFO ptiSendTo; if (hWnd != HWND_BROADCAST && hWnd != HWND_TOPMOST) { Window = UserGetWindowObject(hWnd); if ( !Window ) { return 0; } } /* Check for an exiting window. */ if (Window && Window->state & WNDS_DESTROYED) { ERR("co_IntDoSendMessage Window Exiting!\n"); } /* See if the current thread can handle this message */ ptiSendTo = IntSendTo(Window, gptiCurrent, Msg); // If broadcasting or sending to another thread, save the users data. if (!Window || ptiSendTo ) { UserModeMsg.hwnd = hWnd; UserModeMsg.message = Msg; UserModeMsg.wParam = wParam; UserModeMsg.lParam = lParam; MsgMemoryEntry = FindMsgMemory(UserModeMsg.message); Status = CopyMsgToKernelMem(&KernelModeMsg, &UserModeMsg, MsgMemoryEntry); if (!NT_SUCCESS(Status)) { EngSetLastError(ERROR_INVALID_PARAMETER); return (dsm ? 0 : -1); } } else { KernelModeMsg.hwnd = hWnd; KernelModeMsg.message = Msg; KernelModeMsg.wParam = wParam; KernelModeMsg.lParam = lParam; } if (!dsm) { Result = co_IntSendMessage( KernelModeMsg.hwnd, KernelModeMsg.message, KernelModeMsg.wParam, KernelModeMsg.lParam ); } else { Result = co_IntSendMessageTimeout( KernelModeMsg.hwnd, KernelModeMsg.message, KernelModeMsg.wParam, KernelModeMsg.lParam, dsm->uFlags, dsm->uTimeout, &dsm->Result ); } if (!Window || ptiSendTo ) { Status = CopyMsgToUserMem(&UserModeMsg, &KernelModeMsg); if (!NT_SUCCESS(Status)) { EngSetLastError(ERROR_INVALID_PARAMETER); return(dsm ? 0 : -1); } } return (LRESULT)Result;}
我们可以详细的分析co_IntSendMessageTimeout的内部实现,对于sendMessage来说,还是比较麻烦的,其内部有着很繁琐的嵌套程序,情况分类较多,可以广播send信息,也可以顶层窗口发送信息。
PostMessage
postmessage 使用的是win32k!NtUserPostMessage,其内部调用UserPostMessage
于是我们可以详细的分析一下这个函数,来看看系统是如何投递消息的。
BOOL FASTCALLUserPostMessage( HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam ){ PTHREADINFO pti; MSG Message, KernelModeMsg; LARGE_INTEGER LargeTickCount; Message.hwnd = Wnd; Message.message = Msg; Message.wParam = wParam; Message.lParam = lParam; Message.pt = gpsi->ptCursor; KeQueryTickCount(&LargeTickCount); Message.time = MsqCalculateMessageTime(&LargeTickCount); if (is_pointer_message(Message.message)) { EngSetLastError(ERROR_MESSAGE_SYNC_ONLY ); return FALSE; } if( Msg >= WM_DDE_FIRST && Msg <= WM_DDE_LAST ) { NTSTATUS Status; PMSGMEMORY MsgMemoryEntry; MsgMemoryEntry = FindMsgMemory(Message.message); Status = CopyMsgToKernelMem(&KernelModeMsg, &Message, MsgMemoryEntry); if (! NT_SUCCESS(Status)) { EngSetLastError(ERROR_INVALID_PARAMETER); return FALSE; } co_IntSendMessageNoWait(KernelModeMsg.hwnd, KernelModeMsg.message, KernelModeMsg.wParam, KernelModeMsg.lParam); if (MsgMemoryEntry && KernelModeMsg.lParam) ExFreePool((PVOID) KernelModeMsg.lParam); return TRUE; } if (!Wnd) {//给当前线程发送消息 pti = PsGetCurrentThreadWin32Thread(); return UserPostThreadMessage( pti, Msg, wParam, lParam); } if (Wnd == HWND_BROADCAST) { HWND *List; PWND DesktopWindow; ULONG i; DesktopWindow = UserGetDesktopWindow(); List = IntWinListChildren(DesktopWindow); if (List != NULL) {//遍历所有窗口发送消息 UserPostMessage(DesktopWindow->head.h, Msg, wParam, lParam); for (i = 0; List[i]; i++) { PWND pwnd = UserGetWindowObject(List[i]); if (!pwnd) continue; if ( pwnd->fnid == FNID_MENU || // Also need pwnd->pcls->atomClassName == gaOleMainThreadWndClass pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_SWITCH] ) continue; UserPostMessage(List[i], Msg, wParam, lParam); } ExFreePoolWithTag(List, USERTAG_WINDOWLIST); } } else { PWND Window; Window = UserGetWindowObject(Wnd); if ( !Window ) { ERR("UserPostMessage: Invalid handle 0x%p Msg %d!\n",Wnd,Msg); return FALSE; } pti = Window->head.pti; if ( pti->TIF_flags & TIF_INCLEANUP ) { ERR("Attempted to post message to window %p when the thread is in cleanup!\n", Wnd); return FALSE; } if ( Window->state & WNDS_DESTROYED ) { ERR("Attempted to post message to window %p that is being destroyed!\n", Wnd); /* FIXME: Last error code? */ return FALSE; } if (WM_QUIT == Msg) { MsqPostQuitMessage(pti, wParam); } else { MsqPostMessage(pti, &Message, FALSE, QS_POSTMESSAGE, 0); } } return TRUE;}
函数中我们判断当前的wnd窗口参数是否为空,如果为空,说明我们投递的是当前线程的消息,我们获得当前线程的win32线程信息,调用UserPostThreadMessage 向当前的线程队列中安置消息,其函数内部调用MsqPostMessage. 如果wnd 参数是HWND_BROADCAST,说明这是一个广播消息,于是我们获取桌面DesktopWindow这个顶级窗口,从这个窗口开始形成一个窗口列表,然后遍历这个列表,使用UserPostMessage向每个窗口的消息队列中投递消息。
如果不是广播消息,我们通过UserGetWindowObject来得到窗口WND对象,通过window->head.pti获得到窗口所在线程的线程信息结构体。根据线程信息判断这个这个线程是否正在清理或者是销毁,如果是则返回失败。如果不是,我们判断这个消息是不是退出消息,如果是,我们使用特殊的MsqPostQuitMessage函数来标记信息退出,如果这个消息不是退出消息我们同样使用MsqPostMessage来投递Post信息。
postmessage 从界面上向下调用经历了user32.dll 和win32k.sys,在win32k.sys 中,内部的核心函数为MsqPostMessage来做具体的消息投递。下面我们来分析一下这个函数。
VOID FASTCALLMsqPostMessage(PTHREADINFO pti, MSG* Msg, BOOLEAN HardwareMessage, DWORD MessageBits, DWORD dwQEvent){ PUSER_MESSAGE Message; PUSER_MESSAGE_QUEUE MessageQueue; if ( pti->TIF_flags & TIF_INCLEANUP || pti->MessageQueue->QF_flags & QF_INDESTROY ) { ERR("Post Msg; Thread or Q is Dead!\n"); return; } if(!(Message = MsqCreateMessage(Msg))) //我们创建消息 { return; } MessageQueue = pti->MessageQueue; if (dwQEvent) { ERR("Post Msg; System Qeued Event Message!\n"); InsertHeadList(&pti->PostedMessagesListHead, &Message->ListEntry); } else if (!HardwareMessage) { InsertTailList(&pti->PostedMessagesListHead, &Message->ListEntry); //排队消息 } else { InsertTailList(&MessageQueue->HardwareMessagesListHead, &Message->ListEntry); } if (Msg->message == WM_HOTKEY) MessageBits |= QS_HOTKEY; // Justin Case, just set it. Message->dwQEvent = dwQEvent; Message->QS_Flags = MessageBits; Message->pti = pti; MsqWakeQueue(pti, MessageBits, TRUE); //设置消息位}
我们的PostQuitMessage函数调用,ntuser中如下实现,主要是修改线程信息结构体的内部信息。
VOID FASTCALLMsqPostQuitMessage(PTHREADINFO pti, ULONG ExitCode){ pti->QuitPosted = TRUE; pti->exitCode = ExitCode; MsqWakeQueue(pti, QS_POSTMESSAGE|QS_ALLPOSTMESSAGE, TRUE); //设置对应消息位计数}
我们主要是控制_THREADINFO这个信息,内部放有消息队列。
typedef struct _THREADINFO{ W32THREAD; PTL ptl; PPROCESSINFO ppi; struct _USER_MESSAGE_QUEUE* MessageQueue; struct tagKL* KeyboardLayout; PCLIENTTHREADINFO pcti; struct _DESKTOP* rpdesk; PDESKTOPINFO pDeskInfo; PCLIENTINFO pClientInfo; FLONG TIF_flags; PUNICODE_STRING pstrAppName; /* Messages that are currently dispatched to other threads */ LIST_ENTRY DispatchingMessagesHead; // psmsSent struct _USER_SENT_MESSAGE *pusmCurrent; /* Queue of messages sent to the queue. */ LIST_ENTRY SentMessagesListHead; // psmsReceiveList /* Last time PeekMessage() was called. */ LONG timeLast; ULONG_PTR idLast; /* True if a WM_QUIT message is pending. */ BOOLEAN QuitPosted; /* The quit exit code. */ INT exitCode; HDESK hdesk; UINT cPaintsReady; /* Count of paints pending. */ UINT cTimersReady; /* Count of timers pending. */ DWORD dwExpWinVer; DWORD dwCompatFlags; DWORD dwCompatFlags2; struct _USER_MESSAGE_QUEUE* pqAttach; PTHREADINFO ptiSibling; ULONG fsHooks; PHOOK sphkCurrent; LPARAM lParamHkCurrent; WPARAM wParamHkCurrent; struct tagSBTRACK* pSBTrack; /* Set if there are new messages specified by WakeMask in any of the queues. */ HANDLE hEventQueueClient; /* Handle for the above event (in the context of the process owning the queue). */ PKEVENT pEventQueueServer; LIST_ENTRY PtiLink; INT iCursorLevel; POINT ptLast; /* Queue of messages posted to the queue. */ LIST_ENTRY PostedMessagesListHead; // mlPost UINT fsChangeBitsRemoved; UINT cWindows; UINT cVisWindows; LIST_ENTRY aphkStart[NB_HOOKS]; CLIENTTHREADINFO cti; // Used only when no Desktop or pcti NULL. /* ReactOS */ /* Thread Queue state tracking */ // Send list QS_SENDMESSAGE // Post list QS_POSTMESSAGE|QS_HOTKEY|QS_PAINT|QS_TIMER|QS_KEY // Hard list QS_MOUSE|QS_KEY only // Accounting of queue bit sets, the rest are flags. QS_TIMER QS_PAINT counts are handled in thread information. DWORD nCntsQBits[QSIDCOUNTS]; // QS_KEY QS_MOUSEMOVE QS_MOUSEBUTTON QS_POSTMESSAGE QS_SENDMESSAGE QS_HOTKEY /* Messages that are currently dispatched by this message queue, required for cleanup */ LIST_ENTRY LocalDispatchingMessagesHead; LIST_ENTRY WindowListHead; LIST_ENTRY W32CallbackListHead; SINGLE_LIST_ENTRY ReferencesList; ULONG cExclusiveLocks;#if DBG USHORT acExclusiveLockCount[GDIObjTypeTotal + 1];#endif} THREADINFO;
DispatchMessageW
我们来看一下消息的派发,sendmessage 和 postmessage 是用户主动发送消息,这个函数是将消息派发到处理函数中。
typedef struct tagMSG { HWND hwnd; UINT message; WPARAM wParam; LPARAM lParam; DWORD time; POINT pt;} MSG,*LPMSG,*PMSG;
消息结构如上,里面有窗口句柄,消息号,两个消息参数,以及消息时间和位置。
于是乎函数中我们先判断消息号是否大于最大消息号码
然后我们通过窗口句柄获得窗口对象wnd。
之后我们判断是否是系统timer或是timer,并且lParam不空,于是我们通过lParam得到过程处理函数。如果是systimer,我们使用NtUserDispatchMessage向系统继续派发消息。否则我们验证一下这个回调函数(即在timer列表中判断是否存在这个timer处理函数)然后我们调用这个函数。
如果是其他消息,我们在user32一层先进行一次处理,判断出不是绘制消息,我们使用IntCallMessageProc 在user32层进行一下调用
如果是WM_PAINT并且窗口状态WNDS_SERVERSIDEWINDOWPROC我们将消息派发到系统中,使用函数
NtUserDispatchMessage。
LRESULTWINAPIDECLSPEC_HOTPATCHDispatchMessageW(CONST MSG *lpmsg){ LRESULT Ret = 0; PWND Wnd; BOOL Hit = FALSE; if ( lpmsg->message & ~WM_MAXIMUM ) { SetLastError( ERROR_INVALID_PARAMETER ); return 0; } if (lpmsg->hwnd != NULL) { Wnd = ValidateHwnd(lpmsg->hwnd); if (!Wnd) return 0; } else Wnd = NULL; if (is_pointer_message(lpmsg->message)) { SetLastError( ERROR_MESSAGE_SYNC_ONLY ); return 0; } if ((lpmsg->message == WM_TIMER || lpmsg->message == WM_SYSTIMER) && lpmsg->lParam != 0) { WNDPROC WndProc = (WNDPROC)lpmsg->lParam; if ( lpmsg->message == WM_SYSTIMER ) return NtUserDispatchMessage( (PMSG) lpmsg ); //系统timer 消息我们继续派发 if (!NtUserValidateTimerCallback(lpmsg->hwnd, lpmsg->wParam, lpmsg->lParam)) { WARN("Validating Timer Callback failed!\n"); return 0; } _SEH2_TRY { Ret = WndProc(lpmsg->hwnd, //调用用户注册的消息处理函数 lpmsg->message, lpmsg->wParam, GetTickCount()); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Hit = TRUE; } _SEH2_END; } else if (Wnd != NULL) { if ( (lpmsg->message != WM_PAINT) && !(Wnd->state & WNDS_SERVERSIDEWINDOWPROC) ) { Ret = IntCallMessageProc(Wnd, lpmsg->hwnd, lpmsg->message, lpmsg->wParam, lpmsg->lParam, FALSE); } else Ret = NtUserDispatchMessage( (PMSG) lpmsg ); } if (Hit) { WARN("Exception in Timer Callback WndProcW!\n"); } return Ret;}
如果用不上系统的功能,此处直接进行的是user32处理,内部判断ansi还是unicode,不同的编码进行不同的处理,在IntCallWindowProc中我们依旧是判断编码,判断是否hook以调用hook函数,之后调用wnd->lpfnWndProc.
static LRESULT WINAPIIntCallMessageProc(IN PWND Wnd, IN HWND hWnd, IN UINT Msg, IN WPARAM wParam, IN LPARAM lParam, IN BOOL Ansi){ WNDPROC WndProc; BOOL IsAnsi; PCLS Class; Class = DesktopPtrToUser(Wnd->pcls); WndProc = NULL; if ( Wnd->head.pti != GetW32ThreadInfo()) { // Must be inside the same thread! SetLastError( ERROR_MESSAGE_SYNC_ONLY ); return 0; } /* This is the message exchange for user32. If there's a need to monitor messages, do it here! */ TRACE("HWND %p, MSG %u, WPARAM %p, LPARAM %p, Ansi %d\n", hWnd, Msg, wParam, lParam, Ansi);// if (Class->fnid <= FNID_GHOST && Class->fnid >= FNID_BUTTON ) if (Class->fnid <= FNID_GHOST && Class->fnid >= FNID_FIRST ) {//提取出函数 if (Ansi) { if (GETPFNCLIENTW(Class->fnid) == Wnd->lpfnWndProc) WndProc = GETPFNCLIENTA(Class->fnid); } else { if (GETPFNCLIENTA(Class->fnid) == Wnd->lpfnWndProc) WndProc = GETPFNCLIENTW(Class->fnid); } IsAnsi = Ansi; if (!WndProc) { IsAnsi = !Wnd->Unicode; WndProc = Wnd->lpfnWndProc; } } else { IsAnsi = !Wnd->Unicode; WndProc = Wnd->lpfnWndProc; }/* Message caller can be Ansi/Unicode and the receiver can be Unicode/Ansi or the same. */ if (!Ansi) return IntCallWindowProcW(IsAnsi, WndProc, Wnd, hWnd, Msg, wParam, lParam); else return IntCallWindowProcA(IsAnsi, WndProc, Wnd, hWnd, Msg, wParam, lParam);}
接下来我们看看内核的消息派发即NtUserDispatchMessage,当然每次进入内核的调用首先都要转换用户参数到内核参数,转接内核函数IntDispatchMessage
我们在NtUserDispatchMessage中调用内部函数IntDispatchMessage来具体处理系统消息的派发。
LRESULT FASTCALLIntDispatchMessage(PMSG pMsg){ LARGE_INTEGER TickCount; LONG Time; LRESULT retval = 0; PTHREADINFO pti; PWND Window = NULL; BOOL DoCallBack = TRUE; if (pMsg->hwnd) { Window = UserGetWindowObject(pMsg->hwnd); if (!Window) return 0; } pti = PsGetCurrentThreadWin32Thread(); if ( Window && Window->head.pti != pti) { EngSetLastError( ERROR_MESSAGE_SYNC_ONLY ); return 0; } if (((pMsg->message == WM_SYSTIMER) || (pMsg->message == WM_TIMER)) && (pMsg->lParam) ) { if (pMsg->message == WM_TIMER) { if (ValidateTimerCallback(pti,pMsg->lParam)) { KeQueryTickCount(&TickCount); Time = MsqCalculateMessageTime(&TickCount); retval = co_IntCallWindowProc((WNDPROC)pMsg->lParam, TRUE, pMsg->hwnd, WM_TIMER, pMsg->wParam, (LPARAM)Time, -1); } return retval; } else { PTIMER pTimer = FindSystemTimer(pMsg); if (pTimer && pTimer->pfn) { KeQueryTickCount(&TickCount); Time = MsqCalculateMessageTime(&TickCount); pTimer->pfn(pMsg->hwnd, WM_SYSTIMER, (UINT)pMsg->wParam, Time); } return 0; } } // Need a window! if ( !Window ) return 0; if (pMsg->message == WM_PAINT) Window->state |= WNDS_PAINTNOTPROCESSED; if ( Window->state & WNDS_SERVERSIDEWINDOWPROC ) { TRACE("Dispatch: Server Side Window Procedure\n"); switch(Window->fnid) { case FNID_DESKTOP: DoCallBack = !DesktopWindowProc( Window, pMsg->message, pMsg->wParam, pMsg->lParam, &retval); break; case FNID_MESSAGEWND: DoCallBack = !UserMessageWindowProc( Window, pMsg->message, pMsg->wParam, pMsg->lParam, &retval); break; } } /* Since we are doing a callback on the same thread right away, there is no need to copy the lparam to kernel mode and then back to usermode. We just pretend it isn't a pointer */ if (DoCallBack) retval = co_IntCallWindowProc( Window->lpfnWndProc, !Window->Unicode, pMsg->hwnd, pMsg->message, pMsg->wParam, pMsg->lParam, -1); if (pMsg->message == WM_PAINT) { PREGION Rgn; Window->state2 &= ~WNDS2_WMPAINTSENT; /* send a WM_NCPAINT and WM_ERASEBKGND if the non-client area is still invalid */ Rgn = IntSysCreateRectpRgn( 0, 0, 0, 0 ); co_UserGetUpdateRgn( Window, Rgn, TRUE ); REGION_Delete(Rgn); } return retval;}
从函数中我们获知,在函数开始我们获得到窗口结构wnd,然后得到线程信息threadinfo,做前期的数据结构的准备工作。
然后我们判断信息类型,如果是WM_TIMER信息,我们验证回调函数是否可用,然后计算下timer,使用co_IntCallWindowProc进行处理函数调用,传递WM_TIMER参数
如果是系统timer消息,即WM_SYSTIMER,我们在TimersListHead系统timer链表中找到这个timer,计算timer时间,调用位于timer结构中的处理函数。
如果是WM_PAINT消息,我们重新绘制,使用co_UserGetUpdateRgn来更新
GetMessageW
同样我们的getmessage依旧需要和win32k交互获得信息,因为获得信息是从队列中获得的,这个队列存在内核win32子系统中。我们内部使用的是NtUserGetMessage 进行实际操作的是co_IntGetPeekMessage.我们如下调用这个函数
co_IntGetPeekMessage(&Msg, hWnd, MsgFilterMin, MsgFilterMax, PM_REMOVE, TRUE);
PM_REMOVE从队列中移除这个消息。
系统中定义如下:可以从队列中移除或不移除,也可以
#define PM_NOREMOVE 0#define PM_REMOVE 1#define PM_NOYIELD 2 此标志使系统不释放等待调用程序空闲的线程
BOOL FASTCALLco_IntGetPeekMessage( PMSG pMsg, HWND hWnd, UINT MsgFilterMin, UINT MsgFilterMax, UINT RemoveMsg, BOOL bGMSG ){ PWND Window; PTHREADINFO pti; BOOL Present = FALSE; NTSTATUS Status; if ( hWnd == HWND_TOPMOST || hWnd == HWND_BROADCAST ) hWnd = HWND_BOTTOM; /* Validate input */ if (hWnd && hWnd != HWND_BOTTOM) { if (!(Window = UserGetWindowObject(hWnd))) { if (bGMSG) return -1; else return FALSE; } } else { Window = (PWND)hWnd; } if (MsgFilterMax < MsgFilterMin) { MsgFilterMin = 0; MsgFilterMax = 0; } if (bGMSG) { RemoveMsg |= ((GetWakeMask( MsgFilterMin, MsgFilterMax ))<< 16); } pti = PsGetCurrentThreadWin32Thread(); pti->pClientInfo->cSpins++; // Bump up the spin count. do { Present = co_IntPeekMessage( pMsg, Window, MsgFilterMin, MsgFilterMax, RemoveMsg, bGMSG ); if (Present) { /* GetMessage or PostMessage must never get messages that contain pointers */ ASSERT(FindMsgMemory(pMsg->message) == NULL); if (pMsg->message != WM_PAINT && pMsg->message != WM_QUIT) { pti->timeLast = pMsg->time; pti->ptLast = pMsg->pt; } // The WH_GETMESSAGE hook enables an application to monitor messages about to // be returned by the GetMessage or PeekMessage function. co_HOOK_CallHooks( WH_GETMESSAGE, HC_ACTION, RemoveMsg & PM_REMOVE, (LPARAM)pMsg); if ( bGMSG ) break; } if ( bGMSG ) { Status = co_MsqWaitForNewMessages( pti, Window, MsgFilterMin, MsgFilterMax); if ( !NT_SUCCESS(Status) || Status == STATUS_USER_APC || Status == STATUS_TIMEOUT ) { Present = -1; break; } } else { if (!(RemoveMsg & PM_NOYIELD)) { IdlePing(); // Yield this thread! UserLeave(); ZwYieldExecution(); UserEnterExclusive(); // Fall through to exit. IdlePong(); } break; } } while( bGMSG && !Present ); // Been spinning, time to swap vinyl... if (pti->pClientInfo->cSpins >= 100) { // Clear the spin cycle to fix the mix. pti->pClientInfo->cSpins = 0; //if (!(pti->TIF_flags & TIF_SPINNING)) // FIXME: Need to swap vinyl... } return Present;}
一般情况下,在自己的程序中,我们如下设置消息循环。我们使用GetMessage 最后两个参数传递为两个0,即代表获取所有消息。
while( ::GetMessage( &msg, NULL, 0, 0 ) ) { if( !CPaintManagerUI::TranslateMessage( &msg ) ) { ::TranslateMessage(&msg); // 用户输入消息 ::DispatchMessage(&msg); }}
所以接着调用这个核心函数的时候,我们多数情况下是获取所有消息
BOOL FASTCALLco_IntPeekMessage( PMSG Msg, PWND Window, UINT MsgFilterMin, UINT MsgFilterMax, UINT RemoveMsg, BOOL bGMSG ){ PTHREADINFO pti; LARGE_INTEGER LargeTickCount; BOOL RemoveMessages; UINT ProcessMask; BOOL Hit = FALSE; pti = PsGetCurrentThreadWin32Thread(); RemoveMessages = RemoveMsg & PM_REMOVE; ProcessMask = HIWORD(RemoveMsg); /* Hint, "If wMsgFilterMin and wMsgFilterMax are both zero, PeekMessage returns all available messages (that is, no range filtering is performed)". */ if (!ProcessMask) ProcessMask = (QS_ALLPOSTMESSAGE|QS_ALLINPUT); IdlePong(); do { KeQueryTickCount(&LargeTickCount); pti->timeLast = LargeTickCount.u.LowPart; pti->pcti->tickLastMsgChecked = LargeTickCount.u.LowPart; /* Dispatch sent messages here. */ while ( co_MsqDispatchOneSentMessage(pti) ) { /* if some PM_QS* flags were specified, only handle sent messages from now on */ if (HIWORD(RemoveMsg) && !bGMSG) Hit = TRUE; // wine does this; ProcessMask = QS_SENDMESSAGE; } if (Hit) return FALSE; /* Clear changed bits so we can wait on them if we don't find a message */ if (ProcessMask & QS_POSTMESSAGE) { pti->pcti->fsChangeBits &= ~(QS_POSTMESSAGE | QS_HOTKEY | QS_TIMER); if (MsgFilterMin == 0 && MsgFilterMax == 0) // Wine hack does this; ~0U) { pti->pcti->fsChangeBits &= ~QS_ALLPOSTMESSAGE; } } if (ProcessMask & QS_INPUT) { pti->pcti->fsChangeBits &= ~QS_INPUT; } /* Now check for normal messages. */ if (( (ProcessMask & QS_POSTMESSAGE) || (ProcessMask & QS_HOTKEY) ) && MsqPeekMessage( pti, RemoveMessages, Window, MsgFilterMin, MsgFilterMax, ProcessMask, Msg )) { return TRUE; } /* Now look for a quit message. */ if (pti->QuitPosted) { /* According to the PSDK, WM_QUIT messages are always returned, regardless of the filter specified */ Msg->hwnd = NULL; Msg->message = WM_QUIT; Msg->wParam = pti->exitCode; Msg->lParam = 0; if (RemoveMessages) { pti->QuitPosted = FALSE; ClearMsgBitsMask(pti, QS_POSTMESSAGE); pti->pcti->fsWakeBits &= ~QS_ALLPOSTMESSAGE; pti->pcti->fsChangeBits &= ~QS_ALLPOSTMESSAGE; } return TRUE; } /* Check for hardware events. */ if ((ProcessMask & QS_INPUT) && co_MsqPeekHardwareMessage( pti, RemoveMessages, Window, MsgFilterMin, MsgFilterMax, ProcessMask, Msg)) { return TRUE; } if ((ProcessMask & QS_MOUSE) && co_MsqPeekMouseMove( pti, RemoveMessages, Window, MsgFilterMin, MsgFilterMax, Msg )) { return TRUE; } /* Check for sent messages again. */ while ( co_MsqDispatchOneSentMessage(pti) ) { if (HIWORD(RemoveMsg) && !bGMSG) Hit = TRUE; } if (Hit) return FALSE; /* Check for paint messages. */ if ((ProcessMask & QS_PAINT) && pti->cPaintsReady && IntGetPaintMessage( Window, MsgFilterMin, MsgFilterMax, pti, Msg, RemoveMessages)) { return TRUE; } /* This is correct, check for the current threads timers waiting to be posted to this threads message queue. If any we loop again. */ if ((ProcessMask & QS_TIMER) && PostTimerMessages(Window)) { continue; } return FALSE; } while (TRUE); return TRUE;}
从函数中我们可以获知,系统是如何捡取消息的。
首先系统派发send message ,调用co_MsqDispatchOneSentMessage来派发消息。
然后系统处理POSTMESSAGE和HOTKEY,调用MsqPeekMessage来处理
接着系统查看处理退出信息,即quit message,这个信息你可以用postquitmessage来置位。
我们处理硬件输入信息co_MsqPeekHardwareMessage
处理鼠标信息co_MsqPeekMouseMove
又一次处理此时没有有发送信息即sendmessage。
接着处理绘制信息即PAINT,调用IntGetPaintMessage处理
最后的时候处理timer信息,PostTimerMessages。
MsqPeekMessage
正常消息的提取需要这个函数 提取POSTMESSAGE和HOTKEY消息
我们现在来看看这个函数,是如何来提取消息的
主要是检索队列 这个队列位于pti->PostedMessagesListHead中
BOOLEAN APIENTRYMsqPeekMessage(IN PTHREADINFO pti, IN BOOLEAN Remove, IN PWND Window, IN UINT MsgFilterLow, IN UINT MsgFilterHigh, IN UINT QSflags, OUT PMSG Message){ PLIST_ENTRY CurrentEntry; PUSER_MESSAGE CurrentMessage; PLIST_ENTRY ListHead; BOOL Ret = FALSE; CurrentEntry = pti->PostedMessagesListHead.Flink; ListHead = &pti->PostedMessagesListHead; if (IsListEmpty(CurrentEntry)) return FALSE; CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE, ListEntry); do { if (IsListEmpty(CurrentEntry)) break; if (!CurrentMessage) break; CurrentEntry = CurrentEntry->Flink;/* MSDN: 1: any window that belongs to the current thread, and any messages on the current thread's message queue whose hwnd value is NULL. 2: retrieves only messages on the current thread's message queue whose hwnd value is NULL. 3: handle to the window whose messages are to be retrieved. */ if ( ( !Window || // 1 ( Window == PWND_BOTTOM && CurrentMessage->Msg.hwnd == NULL ) || // 2 ( Window != PWND_BOTTOM && Window->head.h == CurrentMessage->Msg.hwnd ) ) && // 3 ( ( ( MsgFilterLow == 0 && MsgFilterHigh == 0 ) && CurrentMessage->QS_Flags & QSflags ) || ( MsgFilterLow <= CurrentMessage->Msg.message && MsgFilterHigh >= CurrentMessage->Msg.message ) ) ) { *Message = CurrentMessage->Msg; if (Remove) { RemoveEntryList(&CurrentMessage->ListEntry); ClearMsgBitsMask(pti, CurrentMessage->QS_Flags); MsqDestroyMessage(CurrentMessage); } Ret = TRUE; break; } CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE, ListEntry); } while (CurrentEntry != ListHead); return Ret;}
- windows message manager
- Windows Message
- windows message
- windows Message
- Windows Message
- Windows Message
- Android APP: Message Manager 建立资料库
- Windows Heap Manager
- windows qemu manager
- Credential manager of windows
- windows config manager
- windows Heap manager
- windows handle manager
- windows Object Manager
- Windows Message消息详解
- Windows Message ID constants
- windows, thread ,message queue
- Windows Message Codes
- 一套原创分布式即时通讯(IM)系统理论架构方案
- at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:224)
- Python文件读写
- 面向连接的套接字通信
- 在Mac系统中安装配置Tomcat及和Eclipse 配置
- windows message manager
- StringBuilder,StringBuffer的深入了解
- 编程之美读书笔记-最短摘要的生成
- #define和const struct 与 union的区别
- Foundation
- Python简介
- 【以太坊】Definitions of Data Structure
- Bootstrap vs Foundation
- html5中介绍的是拖动一个元素到指定的元素框内