What explains this strange behavior of PeekMessage (an attempt to deal with the full message queue, filtering for specific messages)?

Our application acts as a COM server, where all automation takes place in one STA apartment (in the main application thread), and some VBS scripts that make long (> 10-minute) calls fail with the error "System call error (80010100) " Some research ( one , two , three ) indicates that this is probably caused by the message queue being full, so when COM tries to call the next method, it cannot.

In case this is important, the application is developed using Embarcadero RAD Studio 2010 (mainly C ++ , smatterings Delphi for some COM classes.)

I thought I would review the thread's message queue at the end of a long COM method call (i.e. before it returns) to see what it contains using GetQueueStatus and PeekMessage . Although it seems that the queue is full, I see some strange behavior, and itโ€™s hard for me to understand why PeekMessage behaves as it is, and thatโ€™s why the queue is full - that is, what it fills.

A slightly long explanation ahead:

Testing thread message queue full

Code like this:

 int iMessages = 0; DWORD dwThreadId = GetCurrentThreadId(); while (::PostThreadMessage(dwThreadId, WM_USER, 0, 0)) { iMessages++; } if (GetLastError() == ERROR_NOT_ENOUGH_QUOTA) { String strError = L"Not enough quota, posted " + IntToStr(iMessages) + L" messages"; // Do something with strError } 

when launched at the end of a short method called by COM, it can send thousands of messages (say, 9996); at the end of a long method call that causes a script error, it can send a message 0. My conclusion is that the message queue is full really is the cause of the problem. My system has a default message queue limit of 10,000 (see the "Notes" section.)

Calling Application->ProcessMessages() (calls the application message loop until it is empty, for those of you who are not Delphi / C ++ Builder users, this is a pretty normal "get / translate / dispatch", until more messages appear, the "method) solves the problem, and the COM script can successfully invoke the next method. Although it is probably good in this particular situation, calling ProcessMessages() at effectively random points is something that should be avoided - this can lead to re-entry: I would like to know what causes the queue to fill up, if possible.

Examine the contents of the message queue

Using GetQueueStatus to determine which messages are in the queue, shows that there is a timer ( QS_TIMER ), sent messages ( QS_POSTMESSAGE ), "all sent messages" (i.e., others posted, QS_ALLPOSTMESSAGE ) and color messages ( QS_PAINT ).

Here where it gets weird. I am trying to delete selected messages or message types using PeekMessage with PM_REMOVE to both partially remove the queue and count the number of messages of each type.

If I call:

 while (::PeekMessage(&oMsg, NULL, 0, 0, PM_REMOVE | PM_NOYIELD | (QS_TIMER << 16)) != 0) {... 

I get a little over ten thousand messages, usually 10006 or so. Not all of them are WM_TIMER: several thousand WM_APP + 202, a message that we use internally, which, it seems, is not published (by us) anywhere near such huge quantities. I checked this: it is only sent a few times. There are also several thousand other WM_APP+something messages that we use; this one is probably sent too often.

If I call it:

 while (::PeekMessage(&oMsg, NULL, WM_TIMER, WM_TIMER, PM_REMOVE | PM_NOYIELD) != 0) {... 

I get about ten messages, all of which are really WM_TIMER. What for? PeekMessage documentation indicates that passing QS_TIMER <16 should only process timer messages, but it generates significantly more messages, many of which are not timers at all.

Finally, if you instead call the third variation:

 while (::PeekMessage(&oMsg, NULL, WM_APP+202, WM_APP+202, PM_REMOVE | PM_NOYIELD) != 0) {... 

which filters directly for a custom message that the first line of code returns thousands, I get seventeen messages.

I reproduced all this several times - none of them work.

So:

  • Why does the first PeekMessage call remove more than just timers (compared to the second call)? Curiosity, really.
  • Why does the first PeekMessage call delete several thousand WM_APP + 202 messages (which we define and use and do not send many of them), but if I instead name the third option, which directly filters this message, I get 17?
  • If I get such different results for the same message, how do I find out what has filled the queue and what is the best way to avoid it?
  • Or, to avoid all of the above: can I completely ignore all this, and then how should I work with a full message queue when COM is going to try to use the method?

I am puzzled and quite able to make an elementary mistake - he went to the stage of considering something puzzling where you do it. Any help for a COM problem or explanation of the behavior of the message will be greatly appreciated, including "You made a basic mistake X, golly, which was stupid with you").

+6
source share
1 answer

GetQueueStatus () accepts QS_xxx parameters, but PeekMessage () accepts only PM_QS_xxx constants.

This explains the mismatch between the number of WM_TIMER messages indicated by the QueueStatus symbol, and then is deleted using PeekMessage (). Your call to PeekMessage(PM_REMOVE) does not delete the WM_TIMER messages, but something else.

I think you misunderstood the PeekMessage () documentation. PM_QS_POSTMESSAGE documented as an equivalent value as:

 ((QS_POSTMESSAGE | QS_HOTKEY | QS_TIMER) << 16) 

And other constants PM_QS_xxx documented as equal to the corresponding constant QS_xxx << 16 , but they do not say anywhere that it is sequential and can be extrapolated to ALL QS_xxxx constants.

I suspect that QS_TIMER << 16 gives some filter that does more than just filter WM_TIMER messages (itโ€™s clear that I just canโ€™t say with certainty which filter it gives).

As far as I know, WM_TIMER is the only timer-related message, so there is no need to have a wider filter for a larger super-set of timer messages - there is no such super-set. If you want to filter timer messages, just filter WM_TIMER .

+1
source

Source: https://habr.com/ru/post/902123/


All Articles