Jump to content
Soji

Black screen while processing longer task on windows server 2012 R2

Recommended Posts

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

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

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 by Soji

Share this post


Link to post

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 by Attila Kovacs

Share this post


Link to post

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

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 by Attila Kovacs

Share this post


Link to post
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
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
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
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
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.

  • Like 1

Share this post


Link to post
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 by Anders Melander
  • Like 1
  • Thanks 1

Share this post


Link to post
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
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?).

 

  • Like 3

Share this post


Link to post
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

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×