I do not understand what Application.ProcessMessages in Delphi is doing

There is no short way to answer this question properly.

The primary means by which Windows applications interact with the operating system is via a messaging system. Everything that happens in a windows application happens in response to a message.

For example :

If you click on the screen, the operating system decides which application got clicked and posts a message to that application indicating that it has received a click (along with the location of that click).

If a window is moved and reveals a part of your application beneath it, the operating system sends a message telling your application to repaint itself.

The list goes on. Everything that happens is driven by messages.

Now, each application has one primary user-interface thread (the "Main" thread) and this thread has one primary function - it runs in an infinite loop that checks for these messages from the operating system and then executes the necessary code in response to those messages.

The Problem

You come along and start writing an application. You might write some code like this :

procedure TForm1.Button1Click(Sender: TObject);
var i : Integer;
begin
  for i := 0 to 99999999999 do begin
    SolveTheProblemsOfTheWorld(i);
    CalculatePiToABillionPlaces;
  end;
end;

In the larger structure of the program, execution in the main thread looks like this:

  1. Check for messages
  2. For each message - execute the associated handlers
  3. Go back to Check for messages (loop)

So this loop is happily wizzing along when suddenly one of the associated handlers (Button1Click above) starts taking a very long time to complete. Key to understand is that one message handler must complete before the next one can run. If you click a scrollbar, for example, and drag it, but you've attached a handler to OnClick of the scrollbar that takes 10s to complete, then the drag operation will not be seen by your application until that click handler completes. In the meantime, the message queue is filling up and the main thread is not doing anything about it.

Surely you've experienced this - suddenly your application does not respond to clicks. You can't interact with it, you can't move the window, if you drag another window around overtop of it the application won't even repaint itself - it just fills up with whatever garbage you left on top of it.

Enter ProcessMessages

The lazy, terrible solution to not putting your long-running code into a thread

What you are doing when you are calling Application.ProcessMessages is, in the middle of one of these handlers, instructing the main thread to take a break to go back to check the message queue and empty any messages that have been piling up; to handle any clicks, window movements, inputs, keystrokes, to repaint itself if needed, etc.

procedure TForm1.Button1Click(Sender: TObject);
var i : Integer;
begin
  for i := 0 to 99999999999 do begin
    SolveTheProblemsOfTheWorld(i);
    CalculatePiToABillionPlaces;
    Application.ProcessMessages;
  end;
end;

This may seem superficially like a sensible thing to do since it allows you to keep the application responsive during a long-running loop. Ultimately, however, this style of programming is widely considered to be extremely poor practice for a large number of very good reasons. Suffice it to say that anywhere you are tempted to use Application.ProcessMessages is a solid case for moving that work to a background thread instead.

For more detail, let's have a look at the actual code :

procedure TApplication.ProcessMessages;
var
  Msg: TMsg;
begin
  while ProcessMessage(Msg) do {loop};
end;

So when you make this call to Application.ProcessMessages you are running a loop that is, one by one, emptying messages from the message queue (and executing all of the code that is attached to handlers that are reacting to those messages) until it is empty. When it is empty and there are no more messages to process, control will return to the next line in your program.

The important point to take away is that the fluid, smooth interaction you experience when using a program is a complete illusion that depends exactly on this message loop being processed as quickly as possible. The smaller the time delay between a message being posted to your application and the message being handled, the more your application will feel like it is alive and responsive.

It is for this reason that all code that is attached to user-interface handlers should be fast running. Long running operations either need to be interrupted so that message handling can continue (ie: Application.ProcessMessages) or those operations need to be moved to a separate thread where they can execute without tying up the main thread and taking it away from its primary responsibility (which is to keep the user-interface alive).


For a really good article on the topic, see :

A Key's Odyssey by Peter Below

(Internet Archive link... in case the above dies)

Abstract: This article follows the path of a keystroke message through the VCL. You will learn how the key processing is implemented, how the OnKey events work and what intervention points for the programmer can be found in the whole process. In addition, things like message processing are explained, and you will learn how to trace messages in the debugger from the message loop to their eventual destination.


Messaging

A Delphi VCL application is a Windows application and a lot of communication between Windows components (like forms, edit boxes, but also hidden things like timers) is done using things called messages.

These messages are sent to a special queue. You can send such messages directly (using the SendMessage and PostMessage API functions), but messages are also sent indirectly when you set a property, like the Text of a TEdit.

These messages are used to notify controls that significant events have occurred within the program so they can respond appropriately. That concept of sending and responding to messages is the core principle of a paradigm known as event-driven programming.

Many modern operating systems are event-driven, such as Windows. In fact the Delphi VCL wraps a lot of Windows functionality, and many things you do will result in messages being sent between controls, which notify those controls of mouse clicks, keyboard presses, Windows-settings changing, application closing, control moving, etc.

Mainthread doesn't process messages when executing code

Operating systems also allow programs to have things called threads. Threads are like small pieces of a program that appear to operate simultaneously (and in some cases actually do run simultaneously). An application can have more than one thread, but there is always the main thread, which is also the thread responsible for accepting incoming messages and updating the GUI.

When the main thread of the application is idle (not executing code), it will check the queue to see if there are messages and process them. While other unrelated code is executing, this cannot happen. Consider this example:

Label1.Caption := 'A';
Sleep(5000); // Sleep is simulating a long, blocking process.
Label1.Caption := 'B';

If you execute this code, you would expect the label to get a caption of 'A', which changes to 'B' five seconds later. But this is not true. Setting the caption of a label triggers a repaint of the control through a message. Because the main thread is still blocked by this code (even by the Sleep command), the message isn't processed yet.

Only when the 5 seconds have passed and the caption is set to 'B', the main thread becomes idle and will execute the repaints that we triggered by setting the caption. Also note that other interaction, like clicking a button or dragging the window are postponed until the 5 seconds have passed. The entire UI freezes while the main thread is executing that code.

Application.ProcessMessages to the rescue

Application.ProcessMessages will just force the application to empty its message queue, so you can 'fix' the code like this:

Label1.Caption := 'A';
Application.ProcessMessages;
Sleep(5000);
Label1.Caption := 'B';

Usually there won't be just a Sleep in your code, but a lot of actual processing. By using Application.ProcessMessage frequently, you can keep the interface of the application responding, even when you are executing code.

But this is a bit dirty, the main thread (and the interface) will still be blocked, unless you manage to squeeze in a lot of calls to Application.ProcessMessages. And apart from processing paint messages, other messages are processed as well, so the application could process a click event right in the middle of your process.

So the docs you read are right: Application.ProcessMessages will empty the message queue. Your supervisor is not exactly right. It doesn't allocate extra processing time per say, but it just integrates the emptying of the message queue into the code that is executed, while normally the messages would remain in the queue until the application becomes idle.

Internals

Internally, the application itself does the same all the time. Its main procedure, Application.Run, looks like this (simplified pseudo code):

  repeat

    ProcessMessageFromTheQueue;
    If QueueIsEmpty then
      TellWindowsToWakeMeUpWhenANewMessageArrives;   

  until Terminated;

It's the processing that executes the code, for instance when a WM_MOUSECLICK message is processed, it will trigger (through some Delphi VCL magic), your Button1Click event handler. Since this is a single thread, everything runs sequentially, so you'll understand that ProcessMessageFromTheQueue only returns when the event handler is finished. Delphi handles only one message at a time, and only does the next when processing the previous one is finished.

Or does it?

Application.ProcessMessages looks like this:

  repeat

    ProcessMessageFromTheQueue;

  until QueueIsEmpty;

So it does almost the same as the application main loop: it picks a message from the queue and processes it. So this means, you can actually process the next messages while you are inside the processing of the last one. A convenient trick, but it can easily make your application messy and cause all kinds of unwanted side effects.

The right way: Threads

Most people consider it a better solution to use a separate thread for executing your code and just send signals to the main thread when it needs to update a label or a progress bar. That way, the main thread is idle all the time, and will keep responding to mouse clicks etc.

Threading is tough for a beginner, though. Some things to keep in mind:

  • A thread (other than the main thread) should not directly update the GUI (for instance, a thread cannot set Label1.Caption. It might fail.
  • Variables and other resources must not be changed by multiple threads at the same time, so you have to take precautions to prevent that (critical sections).
  • You must (in most cases) prevent that a user executes the same action that is already executing.
  • In many cases you must find a proper way to interrupt a running thread if a user wants to cancel the process or close the application.

Anyway, this answer shouldn't be a complete course about threading, but now at least you know what Application.ProcessMessages is for. Also note, it is considered 'evil' by many people, but as a beginner you can try it anyway to find out its strengths and weaknesses by yourself. If you do, I hope this information will at least inspire you to move to threads as soon as you are experienced enough for that next chapter.

Tags:

Delphi