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...
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)...
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...
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...
var fExecuting: Bool;
procedure TForm1.FormCreate(Sender: TObject); begin fExecuting:= False; //Set fExecuting by default to False end;
procedure TForm1.TimerOnTimer(Sender: TObject); begin ifnot fExecuting thenbegin//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
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:= 0to50dobegin 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:= 50downto0dobegin 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.