Smart questions
Smart answers
Smart people
INTELLIGENT WORK FORUMS
FOR COMPUTER PROFESSIONALS

Member Login




Remember Me
Forgot Password?
Join Us!

Come Join Us!

Are you a
Computer / IT professional?
Join Tek-Tips now!
  • Talk With Other Members
  • Be Notified Of Responses
    To Your Posts
  • Keyword Search
  • One-Click Access To Your
    Favorite Forums
  • Automated Signatures
    On Your Posts
  • Best Of All, It's Free!

Join Tek-Tips
*Tek-Tips's functionality depends on members receiving e-mail. By joining you are opting in to receive e-mail.

Donate Today!

Do you enjoy these
technical forums?
Donate Today! Click Here

Posting Guidelines

Promoting, selling, recruiting, coursework and thesis posting is forbidden.
Jobs from Indeed

Link To This Forum!

Partner Button
Add Stickiness To Your Site By Linking To This Professionally Managed Technical Forum.
Just copy and paste the
code below into your site.

Performance

What does Application.ProcessMessages do, and how do I use it?
Posted: 31 May 11 (Edited 31 May 11)

From the Forms unit in Delphi, Application.ProcessMessages is a command which can save lives, yet can also kill. It has a very important yet sensitive task. It's simple actually, all it does is makes sure that Windows finishes processing all the commands sent to it before the application continues with the code. This especially applies for the drawing of controls.

Suppose you have a list. You are loading data from a database and populating hundreds of values into this list. While you loop through the dataset record by record, you're also doing some heavy work per record, which in turn adds a new item to this list. Let's say this one procedure of refreshing the list typically takes 10 seconds to finish. During those 10 seconds, your application will become unresponsive, and Windows will start calling your application "Not Responding", because really, it isn't. Doesn't mean your application isn't working though.

You may also notice that while this loop is being performed, each item which is added to the list doesn't actually visually appear in the list yet, until the procedure is 100% finished. This is a performance enhancement built in. It's sending messages to Windows to redraw the controls, but it's being put into a queue, waiting for your code to finish before it's actually processed. Application.ProcessMessages forcefully processes this queue of messages to windows to make sure everything you told it to do is actually done before continuing.

Put Application.ProcessMessages inside this loop at the end. After it's there, you will notice the program will start responding again, and that the items will appear in the list as they're actually added. No more wait until your procedure is finished to see the new list.

The problem is, Application.ProcessMessages can also be very dangerous. It's costly because it's of course forcefully making things happen which would have otherwise been queued until a time when it would be more legit. Using more of this command means a slower, yet more responsive application. Not using it results in faster, but nonresponsive applications. Use it only when you really need to. For example, when I need to set the caption of a particular label, and want to make sure this label is in fact displayed before the code is continued, I put a procedure around it, where just after I set the caption label, I call this command...

CODE

procedure Form1.SetStatus(Value: String);
begin
  lblStatus.Caption:= Value; //Set the label's caption
  Application.ProcessMessages; //Make sure it's actually displayed before continuing
end;


This way, I just call 1 line of code to do 2 things, Set the caption, and make sure that caption is actually shown. Also of course is really handy when making properties, but that's another story.

On your program's startup, it can be used to forcefully show your main form before the OnCreate event is even finished (thus showing your form as it's loading)...

CODE

procedure Form1.FormCreate(Sender: TObject);
begin
  Self.Show;
  Self.BringToFront;
  Application.ProcessMessages;
  //Do some other heavy loading, knowing the form is already visible
  //to the user, such as a progress bar showing the loading status
end;

and on your form's close event...

CODE

procedure Form1.FormClose(Sender: TObject;
  var Action: TCloseAction);
begin
  Hide;
  Application.ProcessMessages;
end;


Now if you decide to use it in a timer, there's an important flaw to be aware of. Take this scenario for example...

- You have a timer with an interval smaller than 50 ms
- OnTimer event of Timer has procedure which takes longer than 50 ms to finish
- Application.ProcessMessages is being called at the end of this OnTimer event procedure

If you have something like this, you're risking the same procedure being called many times in a row. Suppose it takes the procedure 500 ms to finish (100 times the timer's interval). Without using this command, (chances are) you'll be fine. But if you do use Application.ProcessMessages in the timer, be aware that the procedure can and most likely will be called many times in a row. I once got the same large bitmap to be loaded over 12,000 times in a row at the same time, thus filling my memory entirely up and crashing my computer (thank God for debug mode). Many would like to think and wish such a thing was not possible, but it is, and I have suffered from it myself, until I discovered the proper way to go about this...

CODE

var
  fExecuting: Bool;

procedure TForm1.FormCreate(Sender: TObject);
begin
  fExecuting:= False; //Set fExecuting by default to False
end;

procedure TForm1.TimerOnTimer(Sender: TObject);
begin
  if not fExecuting then begin //Check if executing
    fExecuting:= True; //Set fExecuting to True
    try


      //Do your heavy work here


    finally //If exception occurs, fExecuting still reverted back to False
      Application.ProcessMessages;
      fExecuting:= False;
    end;
  end;
end;


In the end, it won't overlap.

Here's a test I put together to validate this claim, and see that I'm not kidding...

Form1
lblStatus: TLabel
Timer1: TTimer

CODE

var
  fRunning: Integer; //For tracking the number of times procedure is running

procedure TForm1.FormCreate(Sender: TObject);
begin
  fRunning:= 0; //Set fRunning to 0 by default (not yet running)
  Timer1.Interval:= 1;
  Timer1.Enabled:= True;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
const
  FN = 'D:\Media\Pictures\GForce\Toxic.bmp';
var
  X: Integer;
  B: TBitmap;
begin
  fRunning:= fRunning + 1;
  lblStatus.Caption:= IntToStr(fRunning)+' running at a time';
  Application.ProcessMessages;
  try
    for X:= 0 to 50 do begin
      B:= TBitmap.Create;
      try
        B.LoadFromFile(FN);
        Canvas.StretchDraw(Rect(X,X,Width,Height), B);
        Application.ProcessMessages; //Try commenting this out
      finally
        B.Free;
      end;
    end;
    for X:= 50 downto 0 do begin
      B:= TBitmap.Create;
      try
        B.LoadFromFile(FN);
        Canvas.StretchDraw(Rect(X,X,Width,Height), B);
        Application.ProcessMessages; //Try commenting this out
      finally
        B.Free;
      end;
    end;
  finally
    lblStatus.Caption:= IntToStr(fRunning)+' running at a time';
    Application.ProcessMessages;
    fRunning:= fRunning - 1;
  end;
end;

Now I'm not saying it's OK to create/load/draw/destroy a bitmap repeatedly in a loop like above, that's merely a sample of a very heavy process being performed. Replace the bitmap path with your own large bitmap. Comment out the Application.ProcessMessages inside the 2 loops as mentioned in the commented code to see the picture stretch and move on the form (as a sample of heavily redrawing the image). You will see the label displaying the number of times it's running will steadily grow faster than it shrinks. Then check the Task Manager and watch the memory usage. The line will grow to the top and finally result in a Windows Error saying it's out of memory.

This is simply because by calling Application.ProcessMessages inside the loop, you're telling Windows that it's "OK" to go ahead and execute the timer again.

This theory can still use more extensive testing, and it's still not a definite conclusion on the reaction of Application.ProcessMessages. It's also been mentioned that a timer such as the one mentioned above will "stop executing" any prior procedure and forcefully restart that procedure. If this is true, then objects created at the beginning of the timer execution will never get destroyed. I'm sure that has already been identified as an issue and has been accounted for, but you never know.


 

Back to Embarcadero: Delphi FAQ Index
Back to Embarcadero: Delphi Forum

My Archive

Close Box

Join Tek-Tips® Today!

Join your peers on the Internet's largest technical computer professional community.
It's easy to join and it's free.

Here's Why Members Love Tek-Tips Forums:

Register now while it's still free!

Already a member? Close this window and log in.

Join Us             Close