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.