且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

如何获取Windows线程消息队列的当前长度?

更新时间:2023-01-27 17:04:18

这不是一件容易的事.在执行此操作之前,您应该考虑是否确实需要它.通常,您不需要知道队列中有多少条消息.没有一种完全非侵入性的方式来了解它-也许是因为它从来都不是必需的.并且基于此知识的代码可能不正确-应该正确处理所有消息.

首先,在执行此任务之前,请记住并非所有邮件都已排队.有排队的消息和非排队的消息.请参阅 http://msdn.microsoft.com/en-us/library /ms644927%28v=VS.85%29.aspx [ ^ ].同时使用两者的机制非常狡猾.例如,WM_PAINT消息超出了队列,因为它优化了Invalidate请求:如果窗口完全或部分无效,则将消息数据修改为合并("OR",理论设置析取运算)旧请求和新请求最多保留不处理的WM_PAINT消息,以避免过多的重新呈现.

现在,检查消息队列中是否存在某种类型的消息,您可以使用API​​ PeekMessage,请参见此处的代码示例:^ ],此处的功能说明: http://msdn.microsoft. com/en-us/library/ms644943(v = VS.85).aspx [ http://msdn.microsoft.com/en-us/library/ms644928(v=VS.85).aspx#3 [ http://msdn.microsoft.com/zh-CN/library/ms644944%28v=VS.85%29.aspx [
This is not a simple thing to do. Before doing it you should think if you really need it. Usually you don''t need to know how many messages are there in the queue. There is no a completely non-intrusive way to know it — maybe because it is never required; and the code based on this knowledge might be incorrect — all messages should be properly processed.

First, before posing this task, bear in mind that not all messages are queued. There are queued messages and nonqueued messages. See http://msdn.microsoft.com/en-us/library/ms644927%28v=VS.85%29.aspx[^]. The mechanism of using both is kind of very cunning. For example, WM_PAINT message goes outside the queue because it optimizes Invalidate requests: if a windows is fully or partially invalidated, the message data is modified to merge ("OR", theory-set disjunction operation) old and new requests to keep no more than on non-handled WM_PAINT message to avoid excessive re-rendering.

Now, the way to check is there is a message of certain type in the message queue, you can use the API PeekMessage, see the code sample here: http://msdn.microsoft.com/en-us/library/ms644928%28v=VS.85%29.aspx[^], the description of the function here: http://msdn.microsoft.com/en-us/library/ms644943(v=VS.85).aspx[^]. See also the article "Why to use PeekMessage": http://msdn.microsoft.com/en-us/library/ms644928(v=VS.85).aspx#3[^].

Now, the trick is: if you use PeekMessage in the UI thread repeatedly, you will pause processing message during this operation, but it will return the same very message until you break the loop unless you remove the message (see the last parameter of the function). But if you remove the message to get to new message every time and ultimately count them all, you would disrupt the normal message processing!

So, what would be the work-around? You could get all messages from the queue using PeekMessage with remove option, learn the results (count/classify them or whatever) and put all the messages in some internal container. You will need them to stick them back in the queue using PostMessage, see http://msdn.microsoft.com/en-us/library/ms644944%28v=VS.85%29.aspx[^].

I still wonder: why would you need all that?

—SA


一个简单的解决方案就是拥有自己的计数器.只需在添加作业时将其递增,而在检索作业时将其递减.

考虑使用诸如InterlockedIncrement之类的函数来确保您的计数不会被弄乱.
A simple solution is simply to have your own counter. Just increment it when adding a job and decrement it when retriving it.

Consider using function like InterlockedIncrement to ensure that your count is not messed up.


最后,我发现了Windows内核实现的一些详细信息.实际上,Windows知道队列长度.真可惜.

Windows XP SP2,消息队列位于_W32THREAD的偏移量0xD0处,看起来像:
At last I found some details of windows kernel implement. And actually windows knows the queue length. What a pity.

Windows XP SP2, the message queue is found at offset 0xD0 of _W32THREAD, and looks like:
typedef struct _MSG_QUEUE 
{
PMSG_QUEUE_ENTRY Head;
PMSG_QUEUE_ENTRY Tail;
unsigned long NumberOfMessages;
} MSG_QUEUE;



每个队列条目依次如下:



Each queue entry, in turn, looks like:

typedef struct _MSG_QUEUE_ENTRY {
PMSG_QUEUE_ENTRY pNext;
PMSG_QUEUE_ENTRY pPrev;
struct _MSG 
{ 
unsigned long hWnd; 
unsigned long ulMsg; 
unsigned long wParam; 
unsigned long lParam; 
unsigned long time; // In milliseconds 
POINT pt; // Mouse cursor position 
unsigned long dwUnknown; 
unsigned long dwExtraInfo; 
} Msg;
} MSG_QUEUE_ENTRY, * PMSG_QUEUE_ENTRY;


因此,对于每个线程,我们可以仅从队列的开头开始,并继续遵循pNext直到结束,此时pNext将为NULL.


So for each thread, we can just start at the head of the queue, and keep following pNext until we get to the end, at which point pNext will be NULL.