Soji 1 Posted December 12, 2019 I have a legacy Delphi app which was running fine on windows server 2008. When the server updated to 2012, the app starts to have a paint issue. There are some big tasks running on a modal form (In main thread). It is big for/while loops. When those tasks runs, the main form turns black and some cases, the border turns white. The correct solution to this is to run the task in threads or use parellel library. But that is a big overhaul and that is not possible to do. I was looking for some quick fix to handle it and I used the following code snippet and it worked: PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE); I have to do this everywhere in the code where big bloat is there. Is there any other solutions available? Share this post Link to post
aehimself 396 Posted December 12, 2019 What you see is how a specific operating system is handling unresponsive windows. It's not only your program, it's all which are freezing the main thread (therefore blocks message processing). If you don't mind getting your hand dirty by writing REALLY BAD code you can get away with periodically calling Application.ProcessMessages during your long operation: Procedure StartLongOperaton; Begin Operation1; Application.ProcessMessages; Operation2; Application.ProcessMessages; End; or Procedure WorkWithDataSet(inDataSet: TDataSet); Begin inDataSet.First; While Not inDataSet.Eof Do Begin DoSomethingWithRecord; inDataSet.Next; Application.ProcessMessages; End; End; ...but if you don't know what you are doing, you'll quickly face more issues than just a frozen window. PeekMessage works, because it is forcing the processing of windows messages, therefore your form will APPEAR not frozen. Application.ProcessMessages does the same, it's basically a better wrapper for it. The real solution is... well, you guessed it: threads. There is no such thing as a method is "not possible to put in a thread". Share this post Link to post
Soji 1 Posted December 12, 2019 (edited) Thanks for your response. Application.ProcessMessages is not a solution I am fond of. I wanted to avoid that and came into the peekmessage option. 45 minutes ago, aehimself said: There is no such thing as a method is "not possible to put in a thread". Yes. It is possible to move to thread. But it is a big overhaul and in sustain mode, it is not allowed to do that overhaul. Edited December 12, 2019 by Soji Share this post Link to post
Attila Kovacs 629 Posted December 12, 2019 (edited) As your blocking task runs in a modal form with an own message loop I would not concern about calling Application.Processmessages. However, with a small addition. I would hook Application.OnMessages (as long as the blocking code runs) to handle only WM_PAINT and the messages sent to the Abort button (if any). (in this example WM_TIMER is included only for animations, and btnStart for not be stuck in down state) Of course this would also prevent the system from a restart, for that, you should watch for other messages too, and handle properly, if its a problem. procedure TfrmDoSomething.MyOnMessage(var Msg: TMsg; var Handled: boolean); begin if (Msg.hwnd = btnStart.Handle) and (Msg.Message = WM_MOUSELEAVE) then else if Msg.hwnd = btnAbort.Handle then else case Msg.Message of WM_PAINT, WM_TIMER: ; else Handled := True; end; end; Edited December 12, 2019 by Attila Kovacs Share this post Link to post
Soji 1 Posted December 12, 2019 Thanks @Attila Kovacs. This is interesting. I will give a try. If I can make it work, then it avoids going through loop by loop and adding the peekmessage. Share this post Link to post
Attila Kovacs 629 Posted December 12, 2019 (edited) You still have to call Application.Processmessages in your loops. Otherwise you have to hook in a lower level. ( I think, I would have to test to be sure if SetWindowsHookEx would work) Edited December 12, 2019 by Attila Kovacs Share this post Link to post
aehimself 396 Posted December 12, 2019 11 minutes ago, Soji said: Application.ProcessMessages is not a solution I am fond of. I wanted to avoid that and came into the peekmessage option. Well, you are effectively - almost - doing the same 🙂 12 minutes ago, Soji said: But it is a big overhaul and in sustain mode, it is not allowed to do that overhaul. This is a difference in point of views by different coders / companies. Sustain mode means only to fix critical bugs for me. Main window is frozen? Live with it; it's only visual, not critical. Share this post Link to post
Soji 1 Posted December 12, 2019 Just now, aehimself said: Live with it; it's only visual, not critical. I tried that comment. Did not work out well though 😒 Share this post Link to post
Fr0sT.Brutal 900 Posted December 12, 2019 6 minutes ago, aehimself said: Well, you are effectively - almost - doing the same 🙂 Effectively not the same 😛 PeekMessage with NOREMOVE just "pings" the message queue telling OS the app is not frozen but without consuming/executing the messages so it seems to me absolutely safe and side effect-free (oppositely to PrMsgs that, without additional GUI actions, could allow such things as pushing Launch button again while the loop is already running, closing a form which is expected to receive results and even closing the app itself). Share this post Link to post
Soji 1 Posted December 12, 2019 Just now, Fr0sT.Brutal said: Effectively not the same 😛 Yes. I was about to type the same. It is not same. Share this post Link to post
Attila Kovacs 629 Posted December 12, 2019 10 minutes ago, Fr0sT.Brutal said: seems to me absolutely safe Except it's kingly slow and the queue will be processed right after the job is finished, also replaying every mouse / keyboard event happened meanwhile the loop was running. 1 Share this post Link to post
Anders Melander 1783 Posted December 12, 2019 (edited) 3 hours ago, Soji said: Is there any other solutions available? No. But while you're peeking at the message queue you might as well do something useful with it: procedure BusyBusyBusy; begin // Allow threads to synchronize CheckSynchronize; Msg.message := 0; try // Process cursor update messages for this window so cursor stays responsive while (PeekMessage(Msg, Handle, WM_SETCURSOR, WM_SETCURSOR, PM_REMOVE)) do begin if (Msg.message = WM_QUIT) then exit; DispatchMessage(Msg); end; // Process paint messages for all windows so UI can repaint itself while PeekMessage(Msg, 0, WM_PAINT, WM_PAINT, PM_REMOVE) or PeekMessage(Msg, 0, WM_ERASEBKGND, WM_ERASEBKGND, PM_REMOVE) or PeekMessage(Msg, 0, DXM_SKINS_POSTREDRAW, DXM_SKINS_POSTREDRAW, PM_REMOVE) or PeekMessage(Msg, 0, WM_PRINT, WM_PRINT, PM_REMOVE) or PeekMessage(Msg, 0, WM_PRINTCLIENT, WM_PRINTCLIENT, PM_REMOVE) do begin if (Msg.message = WM_QUIT) then exit; DispatchMessage(Msg); end; PeekMessage(Msg, 0, WM_NULL, WM_NULL, PM_NOREMOVE); // Avoid window ghosting due to unresponsiveness on Vista+ finally if (Msg.message = WM_QUIT) then begin PostQuitMessage(Msg.wParam); Application.Terminate; end; end; end; The above was snipped from existing code so might need to be tweaked slightly. Edited December 12, 2019 by Anders Melander 1 1 Share this post Link to post
Fr0sT.Brutal 900 Posted December 12, 2019 4 hours ago, Attila Kovacs said: Except it's kingly slow and the queue will be processed right after the job is finished, also replaying every mouse / keyboard event happened meanwhile the loop was running. Sure but that's not a big problem. Not necessary to call it too frequently, and after the long operation one can cleanup the queue manually. Or simply disable the main window and let these postponed messages go to NUL. However, I like Anders ' solution Share this post Link to post
Remy Lebeau 1396 Posted December 13, 2019 On 12/12/2019 at 5:07 AM, Fr0sT.Brutal said: PeekMessage with NOREMOVE just "pings" the message queue telling OS the app is not frozen but without consuming/executing the messages so it seems to me absolutely safe and side effect-free That is not entirely true. It does have side-effects if you are not careful. It allows pending messages from SendMessage...() from other threads to be processed (even though they don't go through the message queue themselves, they still require the receiving thread to perform message retrieval operations so they can be delivered). More importantly, it allows on-demand synthetic messages, like WM_TIMER, to be added to the queue (see Why is my message queue full of WM_TIMER messages?). 3 Share this post Link to post
Fr0sT.Brutal 900 Posted December 16, 2019 On 12/13/2019 at 11:54 PM, Remy Lebeau said: That is not entirely true. It does have side-effects if you are not careful. It allows pending messages from SendMessage...() from other threads to be processed (even though they don't go through the message queue themselves, they still require the receiving thread to perform message retrieval operations so they can be delivered). More importantly, it allows on-demand synthetic messages, like WM_TIMER, to be added to the queue (see Why is my message queue full of WM_TIMER messages?). Good point Remy! Share this post Link to post