什没SendThreadMessage呢?问题时实现程序逻辑中具体场景中样初学者说喜欢通windwos消息机制完成UI线程worker线程间步通信号量做问题直困惑久现搞明白
google问题牛(Raymond Chen)httpblogsmsdncomboldnewthingarchive200812239248851aspx)博客中提引发讨里简单翻译Raymond Chen法
想象中SendThreadMessage工作呢?调SendMessage 消息直接分发窗口程?没消息泵想象中SendThreadMessage会消息分发谁呢?没thread window procedure’样东东处理消息
线程中做消息泵想象中SendThreadMessage需等消息处理完毕够知道消息处理完毕?等DispatchMessage返回DispatchMessage失败知道应该窗口分发消息window manager线程发送消息仅已
会认等知道GetMessage or PeekMessage样确定消息解决保证消息检索函数(GetMessage PeekMessage)前消息泵线程消息启动模态窗口消息检索函数告诉消息已处理完毕事实模态窗口创建消息泵
段然长头GetMessage DispatchMessage2基函数天天行知甚少算第次写HelloWorld 现少1年然朦胧感十分惭愧篇总结做确庞工程解2函数需握windows消息机制windwos 没源代码参考里参考ReactOS实现然windows正统应该差远少win2003相似开始步入正题
首先需解UI线程 普通Worker线程间区什
msdn httpmsdnmicrosoftcomenuslibraryms644927提:
To avoid the overhead of creating a message queue for non–GUI threads all threads are created initially without a message queue The system creates a threadspecific message queue only when the thread makes its first call to one of the specific user functions no GUI function calls result in the creation of a message queue
然系统创建线程时普通non–GUI thread直GDI User函数调线程创建消息队列函数调开始
windwos开始时linux样 图形部分户空间中进程负责面减少进程间环境切换放入核中系统调层2种情况种调原核调种新加进原户空间调部分称扩充系统调部分代码放动态安装模块win32ksys应系统调表2包括前核系统调前基础增加图形图系统调系统调发现扩充系统调时原表满足求windwos会会扩充系统调表装载win32ksys模块普普通通线程开始变GUI线程
激动心旅程里开始
开源代码意够贴出
NTSTATUS
NTAPI
PsConvertToGuiThread(VOID)
{
ULONG_PTR NewStack
PVOID OldStack
PETHREAD Thread PsGetCurrentThread()
PEPROCESS Process PsGetCurrentProcess()
NTSTATUS Status
PAGED_CODE()
* Validate the previous mode *
if (KeGetPreviousMode() KernelMode) return STATUS_INVALID_PARAMETER
* If no win32k crashes later *
ASSERT(PspW32ProcessCallout NULL)
* Make sure win32k is here *
if (PspW32ProcessCallout) return STATUS_ACCESS_DENIED
* Make sure it's not already win32 *
if (Thread>TcbServiceTable KeServiceDescriptorTable)
{
* We're already a win32 thread *
return STATUS_ALREADY_WIN32
}
* Check if we don't already have a kernelmode stack *
if (Thread>TcbLargeStack)
{
* We don't create one *
NewStack (ULONG_PTR)MmCreateKernelStack(TRUE 0)
if (NewStack)
{
* Panic in usermode *
NtCurrentTeb()>LastErrorValue ERROR_NOT_ENOUGH_MEMORY
return STATUS_NO_MEMORY
}
* We're about to switch stacks Enter a guarded region *
KeEnterGuardedRegion()
* Switch stacks *
OldStack KeSwitchKernelStack((PVOID)NewStack
(PVOID)(NewStack KERNEL_STACK_SIZE))
* Leave the guarded region *
KeLeaveGuardedRegion()
* Delete the old stack *
MmDeleteKernelStack(OldStack FALSE)
}
* This check is bizare Check out win32k later *
if (Process>Win32Process)
{
* Now tell win32k about us *
Status PspW32ProcessCallout(Process TRUE)
if (NT_SUCCESS(Status)) return Status
}
* Set the new service table *
Thread>TcbServiceTable KeServiceDescriptorTableShadow
ASSERT(Thread>TcbWin32Thread 0)
* Tell Win32k about our thread *
Status PspW32ThreadCallout(Thread PsW32ThreadCalloutInitialize)
if (NT_SUCCESS(Status))
{
* Revert our table *
Thread>TcbServiceTable KeServiceDescriptorTable
}
* Return status *
return Status
}
前没提里判断线程system stackGUI线程普通线程增加更嵌套调需更system stackMmCreateKernelStack分配空间函数里分配64K普通thread system stack12K然惯例里64K堆栈提交中12K设置guard page超12K产生异常然分配空间进程果线程GUI线程进程GUI 进程果GUI进程然先进程转PspW32ProcessCallout函数指针指Win32kProcessCallback里干会初始化系列结构体键盘格式GDI 句柄表等等里略细节
系统ServiceTable换成表PspW32ThreadCallout指Win32kThreadCallback里完成普通线程转换成GUI线程程操作系统复杂东东说初始化结构体真茫茫里关注点Win32kThreadCallback中找创建消息队列入口Win32Thread>MessageQueue MsqCreateMessageQueue(Thread)
系统消息队列构成真正win32应程序开发者需窗口程序中构造简单Message DumpGetMessage底做什
GetMessage会调NtUserGetMessage
BOOL APIENTRY
NtUserGetMessage(PMSG pMsg
HWND hWnd
UINT MsgFilterMin
UINT MsgFilterMax )
{
MSG Msg
BOOL Ret
if ( (MsgFilterMin|MsgFilterMax) & ~WM_MAXIMUM )
{
EngSetLastError(ERROR_INVALID_PARAMETER)
return FALSE
}
UserEnterExclusive()
RtlZeroMemory(&Msg sizeof(MSG))
Ret co_IntGetPeekMessage(&Msg hWnd MsgFilterMin MsgFilterMax PM_REMOVE TRUE)
UserLeave()
if (Ret)
{
_SEH2_TRY
{
ProbeForWrite(pMsg sizeof(MSG) 1)
RtlCopyMemory(pMsg &Msg sizeof(MSG))
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
SetLastNtError(_SEH2_GetExceptionCode())
Ret FALSE
}
_SEH2_END
}
return Ret
}
原谅略茫茫细节
BOOL FASTCALL
co_IntGetPeekMessage( PMSG pMsg
HWND hWnd
UINT MsgFilterMin
UINT MsgFilterMax
UINT RemoveMsg
BOOL bGMSG )
{
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 )
{
Present (WM_QUIT pMsg>message)
break
}
}
if ( bGMSG )
{
if ( co_IntWaitMessage(Window MsgFilterMin MsgFilterMax) )
{
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
}
IntGetPeekMessage循环断调co_IntPeekMessage 消息队列中取出消息果没消息调co_IntWaitMessage等消息然复非遇WM_QUIT
co_IntPeekMessage 实现关键PeekMessage关键部分样略繁琐细节然指重实太函数整消息机制核心部分需慢慢
说知道消息队列啥模样
typedef struct _USER_MESSAGE_QUEUE
{
* Reference counter only access this variable with interlocked functions *
LONG References
* Owner of the message queue *
struct _ETHREAD *Thread
* Queue of messages sent to the queue *
LIST_ENTRY SentMessagesListHead 发送消息队列
* Queue of messages posted to the queue *
LIST_ENTRY PostedMessagesListHead Post消息队列
* Queue for hardware messages for the queue *
LIST_ENTRY HardwareMessagesListHead 硬件消息队列
* messages that are currently dispatched by other threads *
LIST_ENTRY DispatchingMessagesHead 已发送方尚未处理消息队列
* messages that are currently dispatched by this message queue required for cleanup *
LIST_ENTRY LocalDispatchingMessagesHead 正分发消息队列
} USER_MESSAGE_QUEUE *PUSER_MESSAGE_QUEUE
SentMessagesListHead 队列东西发送消息队列消息 方调SendMessage消息队列时消息会放队列中
PostedMessagesListHead 理方调PostMessage然消息放队列中
PostMessage函数较容易实现需挂目标PostedMessagesListHead队列中SendMessage复杂
果发送方接收方线程中SendMessage会直接调窗口窗口程函数处理消息
果发送方接收方线程中发送方必须等接收方运行结果继续执行形成感觉步程感觉似复杂简单线程步问题
想问题GUI线程AGUI线程B发送消息时线程B处理A消息时需线程A发送消息2线程会死锁? 然会知道windwos搞套构造完整消息驱动机制更抽象讲消息机制算线程通信机制套东东复杂东东需户程序结合起真正运行起说应程序必须符合windwos程序规范windwos消息机制参起参中重东东前提GetMessageDispatchingMessagesHead LocalDispatchingMessagesHead 实现套机制中非常重部分
DispatchingMessagesHead SendMessage方时消息需等面结果需等消息放置里里会windwos菜鸟觉困惑困惑什够形成队列呢?里先问题留
站接受者消息队列角度SendMessage需里处理Message Dispatch搞出消息返回值时接受方必须等面消息返回值走消息算搞定里线程甚进程间数传递东西必须考虑消息放里呢?LocalDispatchingMessagesHead 跳出解决问题
总说SendMessage消息时会挂接收方SentMessagesListHead队列中挂发送方DispatchingMessagesHead
接受方先查SentMessagesListHead 否消息话SendMessageListHead中删掉添加LocalDispatchingMessagesHead队列中等消息处理完毕LocalDispatchingMessagesHead消息删
首先关注4队列硬件队列鼠标键盘东东
第次点晕急笼统概念细节部分非常复杂
*
* Internal version of PeekMessage() doing all the work
*
BOOL FASTCALL
co_IntPeekMessage( PMSG Msg
PWND Window
UINT MsgFilterMin
UINT MsgFilterMax
UINT RemoveMsg
BOOL bGMSG )
{
do
{
* Dispatch sent messages here *
while ( co_MsqDispatchOneSentMessage(ThreadQueue) )
{
}
* Now check for normal messages *
if ((ProcessMask & QS_POSTMESSAGE) &&
MsqPeekMessage( ThreadQueue
RemoveMessages
Window
MsgFilterMin
MsgFilterMax
ProcessMask
Msg ))
{
return TRUE
}
* Now look for a quit message *
if (ThreadQueue>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 ThreadQueue>QuitExitCode
Msg>lParam 0
if (RemoveMessages)
{
ThreadQueue>QuitPosted FALSE
ClearMsgBitsMask(ThreadQueue QS_POSTMESSAGE)
pti>pcti>fsWakeBits & ~QS_ALLPOSTMESSAGE
pti>pcti>fsChangeBits & ~QS_ALLPOSTMESSAGE
}
return TRUE
}
* Check for hardware events *
if ((ProcessMask & QS_MOUSE) &&
co_MsqPeekMouseMove( ThreadQueue
RemoveMessages
Window
MsgFilterMin
MsgFilterMax
Msg ))
{
return TRUE
}
if ((ProcessMask & QS_INPUT) &&
co_MsqPeekHardwareMessage( ThreadQueue
RemoveMessages
Window
MsgFilterMin
MsgFilterMax
ProcessMask
Msg))
{
return TRUE
}
* Check for sent messages again *
while ( co_MsqDispatchOneSentMessage(ThreadQueue) )
{
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
}
co_MsqDispatchOneSentMessage 里做SendMessageListHead 中取出SendMessage里消息 SendMessage消息处理完跳出循环MsqPeekMessage 搞定PostMessage消息次检查次co_MsqDispatchOneSentMessage没发送SendMessage消息间间隔新SendMessage消息然IntGetPaintMessage PostTimerMessages名字容易理解里出消息优先级提高Paint效率Paint统处理Timer消息事实出优先级低Paint样timer中绘制函数次处理timer前够保证Paint消息已处理出timer确准前面太东西做
需解消息结构Post消息挂队列中
typedef struct _USER_MESSAGE
{
LIST_ENTRY ListEntry
MSG Msg
DWORD QS_Flags
} USER_MESSAGE *PUSER_MESSAGE
Send消息里麻烦
typedef struct _USER_SENT_MESSAGE
{
LIST_ENTRY ListEntry 接受方队列
MSG Msg
DWORD QS_Flags Original QS bits used to create this message
PKEVENT CompletionEvent 做线程唤醒操作
LRESULT* Result
LRESULT lResult
struct _USER_MESSAGE_QUEUE* SenderQueue
struct _USER_MESSAGE_QUEUE* CallBackSenderQueue
SENDASYNCPROC CompletionCallback
ULONG_PTR CompletionCallbackContext
* entry in the dispatching list of the sender's message queue *
LIST_ENTRY DispatchingListEntry 发送方DispatchingMessageList
INT HookMessage
BOOL HasPackedLParam
} USER_SENT_MESSAGE *PUSER_SENT_MESSAGE
家伙真正挂发送队列中数结构MSG中数成员里前提消息2队列中存边发送方DispatchingMessageList表示消息正分发边接受方SentMessagesListHead表示消息发送等处理
co_MsqDispatchOneSentMessage究竟
BOOLEAN FASTCALL
co_MsqDispatchOneSentMessage(PUSER_MESSAGE_QUEUE MessageQueue)
{
PUSER_SENT_MESSAGE SaveMsg Message
PLIST_ENTRY Entry
LRESULT Result
PTHREADINFO pti
if (IsListEmpty(&MessageQueue>SentMessagesListHead))
{
return(FALSE)
}
* remove it from the list of pending messages *
Entry RemoveHeadList(&MessageQueue>SentMessagesListHead)
Message CONTAINING_RECORD(Entry USER_SENT_MESSAGE ListEntry)
pti MessageQueue>Thread>TcbWin32Thread
SaveMsg pti>pusmCurrent
pti>pusmCurrent Message
Processing a message sent to it from another thread
if ( ( Message>SenderQueue && MessageQueue Message>SenderQueue) ||
( Message>CallBackSenderQueue && MessageQueue Message>CallBackSenderQueue ))
{ most likely but to be sure
pti>pcti>CTI_flags | CTI_INSENDMESSAGE Let the user know
}
* insert it to the list of messages that are currently dispatched by this
message queue *
InsertTailList(&MessageQueue>LocalDispatchingMessagesHead
&Message>ListEntry)
ClearMsgBitsMask(MessageQueue Message>QS_Flags)
if (Message>HookMessage MSQ_ISHOOK)
{ Direct Hook Call processor
Result co_CallHook( Message>Msgmessage HookId
(INT)(INT_PTR)Message>Msghwnd Code
Message>MsgwParam
Message>MsglParam)
}
else if (Message>HookMessage MSQ_ISEVENT)
{ Direct Event Call processor
Result co_EVENT_CallEvents( Message>Msgmessage
Message>Msghwnd
Message>MsgwParam
Message>MsglParam)
}
else
{ * Call the window procedure *
Result co_IntSendMessage( Message>Msghwnd
Message>Msgmessage
Message>MsgwParam
Message>MsglParam)
}
* remove the message from the local dispatching list because it doesn't need
to be cleaned up on thread termination anymore *
RemoveEntryList(&Message>ListEntry)
* remove the message from the dispatching list if needed so lock the sender's message queue *
if ((Message>HookMessage & MSQ_SENTNOWAIT))
{
if (Message>DispatchingListEntryFlink NULL)
{
* only remove it from the dispatching list if not already removed by a timeout *
RemoveEntryList(&Message>DispatchingListEntry)
}
}
* still keep the sender's message queue locked so the sender can't exit the
MsqSendMessage() function (if timed out) *
if (Message>QS_Flags & QS_SMRESULT)
{
Result Message>lResult
}
* Let the sender know the result *
if (Message>Result NULL)
{
*Message>Result Result
}
if (Message>HasPackedLParam TRUE)
{
if (Message>MsglParam)
ExFreePool((PVOID)Message>MsglParam)
}
* Notify the sender *
if (Message>CompletionEvent NULL)
{
KeSetEvent(Message>CompletionEvent IO_NO_INCREMENT FALSE)
}
* Call the callback if the message was sent with SendMessageCallback *
if (Message>CompletionCallback NULL)
{
co_IntCallSentMessageCallback(Message>CompletionCallback
Message>Msghwnd
Message>Msgmessage
Message>CompletionCallbackContext
Result)
}
* Only if it is not a no wait message *
if ((Message>HookMessage & MSQ_SENTNOWAIT))
{
IntDereferenceMessageQueue(Message>SenderQueue)
IntDereferenceMessageQueue(MessageQueue)
}
* free the message *
ExFreePoolWithTag(Message TAG_USRMSG)
* do not hangup on the user if this is reentering *
if (SaveMsg) pti>pcti>CTI_flags & ~CTI_INSENDMESSAGE
pti>pusmCurrent SaveMsg
return(TRUE)
}
首先SentMessagesListHead消息移动LocalDispatchingMessagesHead略掉细节标志位hook部分co_IntSendMessage消息发送出然结果然消息接收方LocalDispatchingMessagesHead删掉果发送方等消息发送方DispatchingMessagesHead中删掉条消息(消息时间限制已早DispatchingMessagesHead删掉)然返回结果保存起然消息附件资源需释放里消息里赘述关心然通Message>CompletionEvent通知发送方该醒果消息回调函数里没直接调回调函数通消息机制发送消息(Post队列中)确容易理解MSDN相关意思时候真MS文档什全源代码源代码需详细文档?文档真彻底说清楚
转远问题迭代co_IntSendMessage co_IntSendMessage 实co_IntSendMessageTimeout 特殊调
LRESULT FASTCALL
co_IntSendMessageTimeout( HWND hWnd
UINT Msg
WPARAM wParam
LPARAM lParam
UINT uFlags
UINT uTimeout
ULONG_PTR *uResult )
{
PWND DesktopWindow
HWND *Children
HWND *Child
if (HWND_BROADCAST hWnd)
{
return co_IntSendMessageTimeoutSingle(hWnd Msg wParam lParam uFlags uTimeout uResult)
}
DesktopWindow UserGetWindowObject(IntGetDesktopWindow())
if (NULL DesktopWindow)
{
EngSetLastError(ERROR_INTERNAL_ERROR)
return 0
}
* Send message to the desktop window too *
co_IntSendMessageTimeoutSingle(DesktopWindow>headh Msg wParam lParam uFlags uTimeout uResult)
Children IntWinListChildren(DesktopWindow)
if (NULL Children)
{
return 0
}
for (Child Children NULL *Child Child++)
{
co_IntSendMessageTimeoutSingle(*Child Msg wParam lParam uFlags uTimeout uResult)
}
ExFreePool(Children)
return (LRESULT) TRUE
}
考虑广播情况简单单窗口发送消息co_IntSendMessageTimeoutSingle
static LRESULT FASTCALL
co_IntSendMessageTimeoutSingle( HWND hWnd
UINT Msg
WPARAM wParam
LPARAM lParam
UINT uFlags
UINT uTimeout
ULONG_PTR *uResult )
{
NTSTATUS Status
PWND Window NULL
PMSGMEMORY MsgMemoryEntry
INT lParamBufferSize
LPARAM lParamPacked
PTHREADINFO Win32Thread
ULONG_PTR Result 0
DECLARE_RETURN(LRESULT)
USER_REFERENCE_ENTRY Ref
if ((Window UserGetWindowObject(hWnd)))
{
RETURN( FALSE)
}
UserRefObjectCo(Window &Ref)
Win32Thread PsGetCurrentThreadWin32Thread()
IntCallWndProc( Window hWnd Msg wParam lParam)
if ( NULL Win32Thread &&
Window>headpti>MessageQueue Win32Thread>MessageQueue)
{
线程消息直接调户窗口回调函数终结束
Result (ULONG_PTR)co_IntCallWindowProc( Window>lpfnWndProc
Window>Unicode
hWnd
Msg
wParam
lParamPacked
lParamBufferSize )
if(uResult)
{
*uResult Result
}
ObDereferenceObject(Win32Thread>pEThread)
IntCallWndProcRet( Window hWnd Msg wParam lParam (LRESULT *)uResult)
if ( NT_SUCCESS(UnpackParam(lParamPacked Msg wParam lParam FALSE)))
{
DPRINT1(Failed to unpack message parameters\n)
RETURN( TRUE)
}
RETURN( TRUE)
}
线程转发消息
do
{
Status co_MsqSendMessage( Window>headpti>MessageQueue
hWnd
Msg
wParam
lParam
uTimeout
(uFlags & SMTO_BLOCK)
MSQ_NORMAL
uResult )
}
while ((STATUS_TIMEOUT Status) &&
(uFlags & SMTO_NOTIMEOUTIFNOTHUNG) &&
MsqIsHung(Window>headpti>MessageQueue))
IntCallWndProcRet( Window hWnd Msg wParam lParam (LRESULT *)uResult)
if (STATUS_TIMEOUT Status)
{
*
MSDN says
Microsoft Windows 2000 If GetLastError returns zero then the function
timed out
XP+ If the function fails or times out the return value is zero
To get extended error information call GetLastError If GetLastError
returns ERROR_TIMEOUT then the function timed out
*
EngSetLastError(ERROR_TIMEOUT)
RETURN( FALSE)
}
else if ( NT_SUCCESS(Status))
{
SetLastNtError(Status)
RETURN( FALSE)
}
RETURN( TRUE)
CLEANUP
if (Window) UserDerefObjectCo(Window)
END_CLEANUP
}
里终结果然里带出问题系统调写函数呢?什时候调?通什方式?样特第次写windwos程序菜鸟遇第问题问题说清楚挺麻烦部分里先留
脑堆栈弹开始
问题系统调写函数呢?什时候调?通什方式?现回答问题回答系统什时候调窗口程函数
调系统代码说调系统服务API等什通中断机制完成通查找系统调表找相应系统函数时利中断机制执行系统代码(然限制)系统时执行户空间代码?点难思考复杂机制做类似工作思考中种调窗口程函数
容易想时执行户代码难没硬件支持完成类似中断机制系统特定方机会执行窗口程函数显然GetMessage执行户窗口程函数方户程序处理消息时系统没办法作等户次调GetMessage类似函数重新获代码控制co_IntPeekMessage中出端倪果消息队列中没消息GetMessage会退出执行权户代码进入等状态果时SendMessage消息线程会唤醒执行代码非Post消息会GetMessage返回户空间
换句话果Sendmessage发线程GetMessage函数部执行果接收方线程阻塞SendMessage会返回没执行GetMessage
思考问题Sendmessage线程线程没执行GetMessage执行代码线程起显然挂起等?接受线程发送消息显然处理处理前讨种情况确意思windwos角度需实现种强壮消息机制什程呢?清楚点实需种机制等方线程处理完毕前处理发消息哈哈WaitForMultipleObjects等2event等处理完毕消息等sendmessage新消息醒时判断什清醒果面线程力继续循环等sendmessage程
NTSTATUS FASTCALL
co_MsqSendMessage(PUSER_MESSAGE_QUEUE MessageQueue
HWND Wnd UINT Msg WPARAM wParam LPARAM lParam
UINT uTimeout BOOL Block INT HookMessage
ULONG_PTR *uResult)
{
PTHREADINFO pti
PUSER_SENT_MESSAGE Message
KEVENT CompletionEvent
NTSTATUS WaitStatus
PUSER_MESSAGE_QUEUE ThreadQueue
LARGE_INTEGER Timeout
PLIST_ENTRY Entry
LRESULT Result 0 Result could be trashed
if((Message ExAllocatePoolWithTag(PagedPool sizeof(USER_SENT_MESSAGE) TAG_USRMSG)))
{
DPRINT1(MsqSendMessage() Not enough memory to allocate a message)
return STATUS_INSUFFICIENT_RESOURCES
}
KeInitializeEvent(&CompletionEvent NotificationEvent FALSE)
pti PsGetCurrentThreadWin32Thread()
ThreadQueue pti>MessageQueue
ASSERT(ThreadQueue MessageQueue)
TimeoutQuadPart (LONGLONG) uTimeout * (LONGLONG) 10000
* FIXME increase reference counter of sender's message queue here *
Message>Msghwnd Wnd
Message>Msgmessage Msg
Message>MsgwParam wParam
Message>MsglParam lParam
Message>CompletionEvent &CompletionEvent
Message>Result &Result
Message>lResult 0
Message>QS_Flags 0
Message>SenderQueue ThreadQueue
Message>CallBackSenderQueue NULL
IntReferenceMessageQueue(ThreadQueue)
Message>CompletionCallback NULL
Message>CompletionCallbackContext 0
Message>HookMessage HookMessage
Message>HasPackedLParam FALSE
IntReferenceMessageQueue(MessageQueue)
* add it to the list of pending messages *
InsertTailList(&ThreadQueue>DispatchingMessagesHead &Message>DispatchingListEntry)
* queue it in the destination's message queue *
InsertTailList(&MessageQueue>SentMessagesListHead &Message>ListEntry)
Message>QS_Flags QS_SENDMESSAGE
MsqWakeQueue(MessageQueue QS_SENDMESSAGE TRUE)
* we can't access the Message anymore since it could have already been deleted *
if(Block)
{
绝部分阻塞
}
else
{
PVOID WaitObjects[2]
WaitObjects[0] &CompletionEvent
WaitObjects[1] ThreadQueue>NewMessages
do
{
UserLeaveCo()
WaitStatus KeWaitForMultipleObjects(2 WaitObjects WaitAny UserRequest
UserMode FALSE (uTimeout &Timeout NULL) NULL)
UserEnterCo()
if(WaitStatus STATUS_TIMEOUT)
{
}
while (co_MsqDispatchOneSentMessage(ThreadQueue))
}
while (NT_SUCCESS(WaitStatus) && STATUS_WAIT_0 WaitStatus)
}
if(WaitStatus STATUS_TIMEOUT)
*uResult (STATUS_WAIT_0 WaitStatus Result 1)
return WaitStatus
}
GetMessage返回般跑2函数
TranslateMessage(&msg)
DispatchMessage(&msg)
里讨TranslateMessage辅助硬件消息相关
DispatchMessage事情做调相窗口程部分部分系统调代码目前没什兴趣
类似模态窗口产生模态窗口窗口会阻塞消息阻塞消息线程然发SendMessage什呢?间会联系?
Win32k(2) 报文驱动通信机制
.应层api
int APIENTRY _tWinMain(HINSTANCE hInstance
HINSTANCE hPrevInstance
LPTSTR lpCmdLine
intnCmdShow)
{
WNDCLASSEXwcex
wcexlpfnWndProc WndProc
wcexcbClsExtra 0
RegisterClassEx(&wcex)
hWnd CreateWindow(szWindowClass szTitle WS_OVERLAPPEDWINDOWCW_USEDEFAULT 0CW_USEDEFAULT 0 NULL NULL hInstance NULL)
while(GetMessage(&msg NULL 0 0))NtUserGetMessage
{
NtUserGetMessage目循环获取普通报文
循环中会检查发送报文方sendmessage会NtUserGetMessage部直接调wndproc立响应
果没普通报文(post 硬件报文定时器报文等)睡眠循环等
if(TranslateAccelerator(msghwnd hAccelTable &msg))
{
TranslateMessage(&msg)键盘扫描码变成unicode
DispatchMessage(&msg)wndproc调
}
}
然WndProc函数中调SendMessagePostQuitMessage等等函数
应层消息结构
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 sentmessage 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>DispatchingListEntryFlink 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 发送方队列
TimeoutQuadPart (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>SentMessagesListHeadFlink
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>DispatchingMessagesHeadFlink
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>DispatchingListEntryFlink 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
}
文档香网(httpswwwxiangdangnet)户传
《香当网》用户分享的内容,不代表《香当网》观点或立场,请自行判断内容的真实性和可靠性!
该内容是文档的文本内容,更好的格式请下载文档