Win32 Programming: How to create a simple GUI (Graphical User Interface) based application? (Part – 3)

As of now, we have created our window and successfully displayed on the screen. We have seen this in our previous article. And also we observed that the window is immediately closed; because of no user interaction code was added. We had couple of questions in our previous article; we will try to address those here.

Why our window immediately closed? Before answering this question; let’s have a look at a simple console based application.

// hello.cpp
#include <stdio.h>

void main()
{
    printf("Hello, World!\n");
}

When we compile and run this simple application; it will display “Hello, World!” message on the console window and exit the program. Observe that, this application also exits immediately. I hope now you got the answer; there are no statements to execute or there is no user input, hence the application quits. The same reason applies to our GUI based application also; there was no user interaction or statements to execute; that was the reason our application window disappears immediately.

So, it is clear that, we need to add user interaction code to our application. How to add this? Usually for console-based applications, we use printf (for display purpose) and scanf statements to read inputs from the user. Then process the inputs and display the results to the user. It is so simple right? But for GUI (Graphical User Interface) based applications it is bit different. Let’s discuss about this.

GUI based applications are message or event driven. More user interaction is required for GUI based applications; hence provide an user interface to enable users to interact with the application.

What do you mean by a message or an event? Whenever the user clicks the mouse button or press the key on the keyboard etc., Windows Operating System will generate an appropriate message or event.

As mentioned earlier, GUI applications are message driven; Windows Operating System sends these generated messages to the applications. So, applications will handle these messages to do an appropriate actions.

Actually Windows Operating System sends the generated messages to a Message Queue of an application (actually it is a Thread’s Message Queue). Application’s responsibility is, to read each message from the Message Queue (actually it is Thread’s responsibility.) and process the message using its Window Procedure (actually it is Thread’s Window Procedure).

So, what we have to do in our application to add message processing functionality? We have to provide a Message Loop in our application to retrieve each message from the Message Queue. And provide a Window Procedure to process these messages.

Then what about Message Queue? Do we need to create it in our code? No. It has been created for each Thread (if Thread creates a window) by Windows Operating System. So, no need to worry about creation of a Message Queue.

Now we have to add Message Loop functionality to our code. This loop fetches each message in the Message Queue and dispatch the message to the Window Procedure. Window Procedure will execute the functionality based on the generated message.

Let’s discuss how to create a Message Loop.

Step 1. As mentioned above, we need to take each message from the Message Queue. We have GetMessage Win32 API function to do this job for us. The syntax of this function looks like below:

BOOL WINAPI GetMessage(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax);

Where lpMsg is an address to hold the MSG structure that receives from the Message Queue. hWnd is a valid window handle whose messages are to be retrieved. wMsgFilterMin and wMsgFilterMax entries are used to filter the messages; that means, GetMessage function only retrieves the messages whose values in-between wMsgFilterMin and wMsgFilterMax entries, inclusive. If you want to retrieve all available messages you can just pass the value “0” to these two parameters.

If there is an error while retrieving the message from the Message Queue, this function returns the value “-1”.

typedef struct tagMSG {
  HWND   hwnd;
  UINT   message;
  WPARAM wParam;
  LPARAM lParam;
  DWORD  time;
  POINT  pt;
} MSG, *PMSG, *LPMSG;

Step 2. Now we know how to retrieve messages from Message Queues. Once we have the message, we need to dispatch the message to Window Procedure. To dispatch the message, Win32 provides an API function, DispatchMessage. The syntax of the function, looks like below:

LRESULT WINAPI DispatchMessage(CONST MSG *lpMsg);

Where lpMsg is a pointer to the valid MSG structure. The MSG structure should be filled with values. Usually we will pass the MSG structure which is received from GetMessage, through this function.

Step 3. Are we done with our Message Loop? Not really. Another important thing we need to remember is, we need to translate the message, before dispatching them to Window Procedure.

Why we need to translate the message.? Let’s take a simple example. When we press “Alt + F4” key combinations from the keyboard; the current active window will be closed. This is the same effect when we click on “Close” button from the window. Actually when we press close button on the window, WM_CLOSE message will be generated.

What does translate message will do then.? Translate message will translate the virtual-key messages to character messages. These messages will be dispatched to the Application’s message queue (actually it is Thread’s message queue). Win32 Provides an API function, TranslateMessage, to translate virtual-message to character message.

BOOL WINAPI TranslateMessage(const MSG *lpMsg);

Where lpMsg is a pointer to the valid MSG structure. The MSG structure should be filled with values. Usually we will pass the MSG structure which is received from GetMessage, through this function.

So when do we need to call this function.? We need to call this before dispatch the message to the message queue, that means, before we call DispatchMessage function.

Step 4. And one more important thing we need to consider is, in our Program, we need to call these functions with in the loop, to process the messages whenever there is an event occurred. Events can be trigger by user intervention or by Operating System. So all these events can be handled only when we call above functions with in the loop. When do you terminate the loop, then.? Its’ simple, we need to terminate the loop until there is no messages in the message queue.

Step 5. Lets’ put all together in a simple Program and the code looks like below.

// sample.cpp
#include <windows.h>
#include <stdio.h>

int main()
{
   WNDCLASSEX wc;

   char szClassName[256] = "SampleWindowClass";

   wc.cbSize = sizeof(WNDCLASSEX);
   wc.style = CS_HREDRAW | CS_VREDRAW;
   wc.lpfnWndProc = DefWindowProc;
   wc.cbClsExtra = 0;
   wc.cbWndExtra = 0;
   wc.hInstance = (HINSTANCE) GetModuleHandle(NULL);
   wc.hIcon = NULL;
   wc.hCursor = NULL;
   wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
   wc.lpszMenuName = NULL;
   wc.lpszClassName = szClassName;
   wc.hIconSm = NULL;

   ATOM atom = RegisterClassEx(&wc);
   if ( atom == 0 )
       return 0;

   MSG msg;
   HWND hWnd = CreateWindow(szClassName, "My first GUI window", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 400, 400, NULL, NULL, NULL, NULL);
   if ( hWnd != NULL )
   {
       while ( GetMessage(&msg, hWnd, 0, 0) != -1 )
       {
              TranslateMessage(&msg);
              DispatchMessage(&msg);
       }
   }

   return 0;
}

Step 6. Lets’ compile & run the above code. And you observe that Windows will open a window and that doesn’t disappear unless you click on Close button on the window.

That concludes this series of Article and I hope you enjoyed these Articles. Please post your feedback in the comments section.

**

Leave a Reply