Recent

Author Topic: Program doesn't close. PeekMessage always has WM_QUIT  (Read 11162 times)

wk

  • New member
  • *
  • Posts: 8
Program doesn't close. PeekMessage always has WM_QUIT
« on: July 06, 2021, 05:17:24 pm »
Hi,

I am running Lazaurs 2.0.13 (fixes, with fpcupdeluxe).
I converted an older and big Delphi program to Lazarus. It is for Windows with win32 widgetset. After some work it runs nice.
The program is multithreaded, some timers.

But when I close it PeekMessage in win32object.inc:399 always has a message of WM_QUIT. So the outer loop of AppProcessMessages can not be left.

I tried to replace the 'break' on line 404 with an 'exit': program ends normal. Heap shows no problems.

Is there a way to see where the message is from? How can I debug this problem?

Thanks for your help.

440bx

  • Hero Member
  • *****
  • Posts: 2558
Re: Program doesn't close. PeekMessage always has WM_QUIT
« Reply #1 on: July 06, 2021, 11:31:21 pm »
Is there a way to see where the message is from? How can I debug this problem?

Thanks for your help.
That can take a little work.  The "easy" way, which may turn out not to be easy, would be to use GetMessagePos and initially _presume_ the message is caused by one of the windows that contains the position.

The other way, more reliable, is to use an API spy.  That will tell you which window posted the message.   One free and capable API monitor is at rohitab.com

HTH.
FPC v3.0.4 and Lazarus 1.8.2 on Windows 7 64bit.

wk

  • New member
  • *
  • Posts: 8
Re: Program doesn't close. PeekMessage always has WM_QUIT
« Reply #2 on: July 07, 2021, 12:20:49 pm »
Thank you, that helped.

I tried with the API Monitor, enabled "Windows Application UI Development" -> "Windows and Messages" -> All of "Messages and Message Queues".
The hangs in a loop with
PeekMessageW and PostQuitMessage(0).
Information from PeekMessageW is the same I can get from AMessage in the code.

I got the Window with following code:
Code: Pascal  [Select][+][-]
  1. Buffer: array[0..256] of Char;      
  2.  
and changed the if to
Code: Pascal  [Select][+][-]
  1.         if AMessage.message = WM_QUIT then
  2.         begin
  3.           GetWindowTextA(WindowFromPoint(AMessage.pt), Buffer, Length(Buffer));
  4.           PostQuitMessage(AMessage.wParam);
  5.           break;
  6.         end;
  7.  

This showed, that the Message is from the main form.
I'm not sure, if WindowFromPoint is reliable, because after letting it run a while Buffer has other window names.

Is there a way to get more information from the API Monitor?



440bx

  • Hero Member
  • *****
  • Posts: 2558
Re: Program doesn't close. PeekMessage always has WM_QUIT
« Reply #3 on: July 07, 2021, 04:21:35 pm »
The question in my mind is, if there is already a WM_QUIT message in the message queue, there is no need to post another one, letting Windows process the WM_QUIT is the natural thing to do. 

My question is, why are you posting another WM_QUIT ?
FPC v3.0.4 and Lazarus 1.8.2 on Windows 7 64bit.

wk

  • New member
  • *
  • Posts: 8
Re: Program doesn't close. PeekMessage always has WM_QUIT
« Reply #4 on: July 07, 2021, 05:04:24 pm »
Thanks for the hint.

Code: Pascal  [Select][+][-]
  1. PostQuitMessage(AMessage.wParam);
is original code from win32object.inc:403

When I remove PostQuitMessage, the program closes without problems.

Is this a bug in the LCL?

440bx

  • Hero Member
  • *****
  • Posts: 2558
Re: Program doesn't close. PeekMessage always has WM_QUIT
« Reply #5 on: July 08, 2021, 02:02:39 am »
Thanks for the hint.

Is this a bug in the LCL?
You're welcome and, as far as that being a bug, since I know nothing about the LCL, I am unable to answer that question.
FPC v3.0.4 and Lazarus 1.8.2 on Windows 7 64bit.

engkin

  • Hero Member
  • *****
  • Posts: 2977
Re: Program doesn't close. PeekMessage always has WM_QUIT
« Reply #6 on: July 08, 2021, 04:51:00 am »
 A little more from the source code:
Code: Pascal  [Select][+][-]
  1.       while PeekMessage(AMessage, HWnd(nil), 0, 0, PM_REMOVE) do
  2.       begin
  3.         if AMessage.message = WM_QUIT then
  4.         begin
  5.           PostQuitMessage(AMessage.wParam);
  6.           break;
  7.         end;
  8.         TranslateMessage(@AMessage);
  9.         DispatchMessageW(@AMessage);
  10.       end;

Notice PM_REMOVE in the marked line. This code is part of AppProcessMessages which is called when you call ProcessMessages. So this is not the main loop, and exiting this procedure will not end the application.

I suspected something else is wrong in your code.

Here is the main loop:
Code: Pascal  [Select][+][-]
  1. procedure TApplication.RunLoop;
  2. begin
  3.   repeat
  4.     if CaptureExceptions then
  5.       try // run with try..except
  6.         HandleMessage;
  7.       except
  8.         HandleException(Self);
  9.       end
  10.     else
  11.       HandleMessage; // run without try..except
  12.   until Terminated;
  13. end;

The only way to exit is to have Terminated = True.

As in:
Code: Pascal  [Select][+][-]
  1. procedure TCustomForm.Close;
  2. ..
  3.     if CloseQuery then
  4. ..
  5.       case CloseAction of
  6. ..
  7.         caFree:
  8.           begin
  9.             // if form is MainForm, then terminate the application
  10. ..
  11.             if IsMainForm then
  12.               Application.Terminate
  13. ..

Otherwise, RunLoop --> HandleMessage --> AppProcessMessages, repeat :)
« Last Edit: July 08, 2021, 05:08:32 am by engkin »

440bx

  • Hero Member
  • *****
  • Posts: 2558
Re: Program doesn't close. PeekMessage always has WM_QUIT
« Reply #7 on: July 08, 2021, 05:05:43 am »
A little more from the source code:
Code: Pascal  [Select][+][-]
  1.       while PeekMessage(AMessage, HWnd(nil), 0, 0, PM_REMOVE) do
  2.       begin
  3.         etc...
  4.       end;

Notice PM_REMOVE in the marked line.
Now, that makes sense (finally!)
FPC v3.0.4 and Lazarus 1.8.2 on Windows 7 64bit.

engkin

  • Hero Member
  • *****
  • Posts: 2977
Re: Program doesn't close. PeekMessage always has WM_QUIT
« Reply #8 on: July 08, 2021, 05:14:54 am »
Now, that makes sense (finally!)

The code is more complicated than I showed, but I hope it is a bit more clear now.

wk

  • New member
  • *
  • Posts: 8
Re: Program doesn't close. PeekMessage always has WM_QUIT
« Reply #9 on: July 08, 2021, 09:53:58 am »
I think we need to look at the complete code (line numbers are from the source):
Code: Pascal  [Select][+][-]
  1. 362 procedure TWin32WidgetSet.AppProcessMessages;
  2. 363     var
  3. 364       AMessage: TMsg;
  4. 365       retVal, index: dword;
  5. 366       pHandles: Windows.LPHANDLE;
  6. 367    
  7. 368         procedure CallWaitHandler;
  8. 369         begin
  9. 370           FWaitHandlers[index].OnEvent(FWaitHandlers[index].UserData, 0);
  10. 371         end;
  11. 372    
  12. 373     begin
  13. 374       repeat
  14. 375         if FPendingWaitHandlerIndex >= 0 then
  15. 376         begin
  16. 377           index := FPendingWaitHandlerIndex;
  17. 378           FPendingWaitHandlerIndex := -1;
  18. 379           CallWaitHandler;
  19. 380         end;
  20. 381     {$ifdef DEBUG_ASYNCEVENTS}
  21. 382         if Length(FWaitHandles) > 0 then
  22. 383           DebugLn('[ProcessMessages] WaitHandleCount=', IntToStr(FWaitHandleCount),
  23. 384             ', WaitHandle[0]=', IntToHex(FWaitHandles[0], 8));
  24. 385     {$endif}
  25. 386         if FWaitHandleCount > 0 then
  26. 387           pHandles := @FWaitHandles[0]
  27. 388         else
  28. 389           pHandles := nil;
  29. 390         retVal := Windows.MsgWaitForMultipleObjects(FWaitHandleCount,
  30. 391           pHandles, False, 0, QS_ALLINPUT);
  31. 392         if (retVal < WAIT_OBJECT_0 + FWaitHandleCount) then
  32. 393         begin
  33. 394           index := retVal-WAIT_OBJECT_0;
  34. 395           CallWaitHandler;
  35. 396         end else
  36. 397         if retVal = WAIT_OBJECT_0 + FWaitHandleCount then
  37. 398         begin
  38. 399           while PeekMessage(AMessage, HWnd(nil), 0, 0, PM_REMOVE) do
  39. 400           begin
  40. 401             if AMessage.message = WM_QUIT then
  41. 402             begin
  42. 403               PostQuitMessage(AMessage.wParam);
  43. 404               break;
  44. 405             end;
  45. 406             // Handle MDI form accelerators
  46. 407             if Assigned(Application) and
  47. 408                Assigned(Application.MainForm) and
  48. 409                (Application.MainForm.FormStyle=fsMDIForm) and
  49. 410                TranslateMDISysAccel(Win32WidgetSet.MDIClientHandle, @AMessage)
  50. 411             then begin
  51. 412               // handled by TranslateMDISysAccel
  52. 413             end else begin
  53. 414               TranslateMessage(@AMessage);
  54. 415               DispatchMessageW(@AMessage);
  55. 416             end;
  56. 417           end;
  57. 418         end else
  58. 419         if retVal = WAIT_TIMEOUT then
  59. 420         begin
  60. 421           // check for pending to-be synchronized methods
  61. 422           CheckSynchronize;
  62. 423           CheckPipeEvents;
  63. 424           break;
  64. 425         end else
  65. 426         if retVal = $FFFFFFFF then
  66. 427         begin
  67. 428           DebugLn('[TWin32WidgetSet.AppProcessMessages] MsgWaitForMultipleObjects returned: ', IntToStr(GetLastError));
  68. 429           break;
  69. 430         end;
  70. 431       until false;
  71. 432     end;
  72.  

When the program closes, debugger gets to line 404. The 'break' jumps out of 'while PeekMessage' (line 399) to the outer 'repeat until false' to line 375. This repeats forever. I hope the highlights in the code help.

Can I maybe catch the 'PostQuitMessage' in the main form or somewhere else?


Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1021
    • Lebeau Software
Re: Program doesn't close. PeekMessage always has WM_QUIT
« Reply #10 on: July 08, 2021, 06:30:19 pm »
My question is, why are you posting another WM_QUIT ?

Because it is the right thing to do, see: Modality, part 3: The WM_QUIT message
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

engkin

  • Hero Member
  • *****
  • Posts: 2977
Re: Program doesn't close. PeekMessage always has WM_QUIT
« Reply #11 on: July 09, 2021, 02:02:25 am »
When the program closes, debugger gets to line 404. The 'break' jumps out of 'while PeekMessage' (line 399) to the outer 'repeat until false' to line 375. This repeats forever. I hope the highlights in the code help.

This loop depends on the value retVal gets in line 390. If that value is WAIT_TIMEOUT, it exits the outer loop in line 424.

Let's approach this in a different way,  can you produce a small project that gets stuck in the inner loop with WM_QUIT?

wk

  • New member
  • *
  • Posts: 8
Re: Program doesn't close. PeekMessage always has WM_QUIT
« Reply #12 on: July 09, 2021, 04:12:12 pm »
Thanks for the help so far.
I'm going on vacation for two weeks without computer. So this has to wait a bit.

wk

  • New member
  • *
  • Posts: 8
Re: Program doesn't close. PeekMessage always has WM_QUIT
« Reply #13 on: August 10, 2021, 05:30:01 pm »
To end this:
I found
Code: Pascal  [Select][+][-]
  1. PostQuitMessage(0);
hidden in some old part of the code. Removing this line fixed everything.
Thanks for all your help.

engkin

  • Hero Member
  • *****
  • Posts: 2977
Re: Program doesn't close. PeekMessage always has WM_QUIT
« Reply #14 on: August 10, 2021, 08:42:55 pm »
To end this:
I found
Code: Pascal  [Select][+][-]
  1. PostQuitMessage(0);
hidden in some old part of the code. Removing this line fixed everything.
Thanks for all your help.

That's better. Thank you for the feedback.

 

TinyPortal © 2005-2018