1. Tech

Your suggestion is on its way!

An email with a link to:

http://delphi.about.com/od/windowsshellapi/l/aa111503d.htm

was emailed to:

Thanks for sharing About.com with others!

Creating an API GUI Windows program with message loop
Page 4: On message loops and WndMessageProc function
 Win prizes by sharing code!
Do you have some Delphi code you want to share? Are you interested in winning a prize for your work?
Delphi Programming Quickies Contest
 More of this Feature
• Page 1: Preparing to create your first Windows API GUI program
• Page 2: What makes an API application: the window Class
• Page 3: Intro to Windows Messaging
• Page 5: Windows handles and the CreateWindow function
• Page 6: Your first Windows API GUI Delphi application
 Join the Discussion
Post your views and comments to this chapter of the free "raw API programming" Delphi Course
Discuss!
 Related Resources
• A Guide to raw API programming.TOC

• Handing messages using Delphi

The message loop
A rather magical function loop, which keeps all Windows programs running and responding to user input, is the GetMessage loop. Without this GetMessage loop, a program's process would not keep running, because the code progression would continue to the end of the .DPR file and the process would exit.

A program's main thread can start its message loop (while GetMessage do) after registering a Class and creating at least one window. The GetMessage function is called by the OS system from a different thread (an operating system thread) when there is a message for a window in your program. You do not call the GetMessage function in your code, the system will do all of the work for that.
GetMessage removes a keyboard or a mouse message from its thread's message queue (there are functions that get a message without removing it from the queue), a program then uses the DispatchMessage(Msg) function to direct Windows to send the message to a "Window Proc". DispatchMessage sends the window handle, the message identifier, and the two message parameters (LParam and WParam) to the "Window Proc", but it does not pass the time the message was posted or mouse cursor position. If the time or cursor position are important to the message (cursor position in a mouse message), then the info is placed in the TMsg, LParam or WParam.

A simple message loop consists of one function call to each of these three functions: GetMessage, TranslateMessage, and DispatchMessage.

var
  Msg: TMsg;

while GetMessage(Msg,0,0,0) do
begin
  TranslateMessage(Msg);  
 {Translate any WM_KEYDOWN keyboard Msg 
  to a WM_CHAR message}
 DispatchMessage(Msg);  
 {this Sends Msg to the address of the 
  "Window Proc" set in the Resistered 
  Window's Class for that window, hWnd} 
end;

Ending the loop
The GetMessage function returns TRUE unless it gets the WM_QUIT message, when it returns FALSE and ends the while loop, since the GetMessage function is just before the final "end" it usually keeps your Process from ending, allowing your program to "run". A program can end its own GetMessage loop by calling the PostQuitMessage function, usually in response to the WM_DESTROY message in the "Window Proc".

Processing messages
A thread's message loop must include TranslateMessage(Msg) if the thread is to use character input from the keyboard. Windows sends virtual-key messages (WM_KEYDOWN and WM_KEYUP) every time the user presses a key. These virtual-key messages contain a code that identifies which key was pressed, but not its character value (which might depend on the state of the Shift key or the character Language). To get the character value, the message loop uses TranslateMessage, which translates the virtual-key codes into a character message (WM_CHAR) and places that into the application's message queue. The WM_CHAR message is then used by DispatchMessage(Msg) and sent to a "Window Proc".

The DispatchMessage function sends a message to the "Window Proc" associated with the window handle (hWnd) specified in the Msg structure. If hWnd is 0, DispatchMessage does nothing with the message.

Only one GetMessage loop is needed for a thread's message queue, even if a program contains many windows. DispatchMessage always sends the message to the correct window; this is because messages in the queue are in a TMsg structure that contains the handle (hWnd) of the destination window.

Not all messages go through the GetMessage loop, some messages are sent directly to the Window Proc. Windows uses two methods to get messages to a Window Proc. One method sends messages to a first-in, first-out waiting area called a message queue (a system memory object that stores messages), the other method sends the messages directly to a window procedure. Messages sent to the message queue are called Queued messages. They are primarily the result of user input with the mouse or keyboard, such as the WM_MOUSEMOVE, WM_LBUTTONDOWN, WM_KEYDOWN, and WM_CHAR messages. Other queued messages include the timer, paint, and quit messages: WM_TIMER, WM_PAINT, and WM_QUIT. Most other messages, which are sent directly to a window procedure, are called NonQueued messages

Since all queued messages for a thread go through the GetMessage( ) loop, you can catch specific messages or certain windows (hWnd) messages Before they are sent to that window, and modify the message or block it.

In your app's registered WndClass you set a "Window Proc" to get and use messages sent by DispatchMessage. Window controls like button, edit, static, listbox, etc. have a system "Window Proc" that processes that control's messages, so you do not need to have a Window Proc for controls.

Application.Terminate
If you are used to working with the Delphi form unit, you know you need to call "Close" or Application.Terminate" to cause your program to quit. But this is a .dpr "program" (no Application object here), and you can end the program execution by ending the GetMessage loop, so the "end." line of the program code page will be executed. To end the GetMessage loop send the WM_QUIT with PostQuitMessage, and the GetMessage function will be called and will return False, exiting the loop. When your program exits, the windows system will unregister your Instance's registered window's classes. Note: try NOT to call "Halt;" or "ExitProcess(hInstance);" to stop your program if you have created windows, just call PostMessage(hAppHandle, WM_CLOSE, 0, 0); so the WM_CLOSE can be processed by the DefWindowProc().

In the WM_DESTROY Msg use PostQuitMessage(0) to end the GetMessage loop, so Windows can clean up. When the WM_CLOSE message is sent to the DefWndProc it calls DestroyWindow for that window. When the DestroyWindow is called, a WM_DESTROY message is created, then the window and all of its children are destroyed. This releases the window's system memory used to record its properties (handle, width, height, position, Window Proc, style, children, and many others). If DestroyWindow is not called, then this memory with the objects properties remains until the system is restarted. Avoid this type of memory leak if possible. If you have a critical error and must Halt the execution, at least try to call DestroyWindow(hForm1) for your main window.

WndMessageProc function, the "Window Proc"
The WndMessageProc function in the program code below, can determine what is displayed on screen for our program's client area and how this program responds to user and system input. I have used the function name of "WndMessageProc" just to show you that you do not have to call it "WndProc", as many code examples seem to imply.

There are four parameters and a Result value that give us the ability to communicate with the operating system. Be aware that this function is usually called by Windows from a different thread (a system thread). Here I use the C code variable types of "HWND", "UINT", "WPARAM" and "LPARAM" to correspond to the Win32 API Help presentation, the HWND and UINT would be Cardinal types in Delphi, and the WPARAM, LPARAM would be Integer types. However, for practical programming usage in Delphi (which does not support a UINT type), they could all be Integer types.

WndMessageProc(hWnd: HWND;  // Handle of window to process message
               Msg: UINT;  // Message to process
               WParam: WPARAM;  // First Information Integer
               LParam: LPARAM  // Second Information Integer
               ): UNIT;  // Result sent back to message sender

The first parameter is hWnd: HWND; which has the window handle that the message is sent to. In the first GUI program we create only one window of the class 'First Class', there will be only one hWnd value sent - hAppHandle, so we can ignore this value. If there is more that one window of this class created then we would filter the messages with the hWnd value.

The next parameter is Msg: UINT; (Cardinal) which will contain a numeric value to indicate the "Message" that is sent to this function. When this function is called by the System, this message is usually a predefined standard Windows Message. We use only 5 messages in this program - WM_CREATE (value 1), WM_DESTROY (value 2), and WM_CLOSE (value 16), WM_COMMAND (value 273), and WM_CHAR (value 258). There are hundreds of Windows Messages (look in the Win32 API help index for WM_ ) and hundreds of specific control messages (like BM_SETCHECK, a button message). Each of the different messages are sent from the OS to a window for a specific event or reason. A Case statement is used to run code for that message.

Many messages need additional information to determine what should be done, this info comes in the LParam and WParam parameters. These 2 integer parameters can contain many different kinds of information and they will be completely different information for different types of messages, anything from a number or window handle, to a Pointer value for a TRect, PChar, or a complex record. Look at the WM_CHAR message in WndMessageProc, the wParam is the integer number for the character. Now look at the WM_COMMAND message, notice the lParam is tested for the button's handle indicating that it was clicked.

Many times the "Handle" of a windows system object will be used to identify it (like in the WM_COMMAND message). So we'll look at Window's Handles now ...

Next page > Windows handles and the CreateWindow function > Page 1, 2, 3, 4, 5, 6

A guide to developing Delphi programs in raw Windows API: Next Chapter >>
>> The TOC

©2014 About.com. All rights reserved.