modula-2 home

  Home  
  Tutorial  
  Win32 API  
  Reference  
  Projects  
 

 

Handling Messages

Example: window_click

[images/window_click.gif]

Alright, we've got a window, but it doesn't do anything except what DefWindowProc() allows it to, like be sized, maximised, etc... Not really all that exciting.

In the next section I am going to show you how to modify what you already have to do somehting new. This way I can just tell you "Handle this message, and do this in it..." and you will know what I mean and be able to do so without seeing an entire example. That's the hope anyway, so pay attention.

OK for starters take the example code for the last window we worked on and make sure it compiles and runs as expected. Then you can either keep working on it for the next little bit or copy it to a new project to modify.

We're going to add the capability to show the user what the name of our program is when they click on our window. Not very exciting, it's basically to get the hang of handling messages. Lets look at what we have in our WndProc():

PROCEDURE WndProc(hwnd : HWND; msg : UINT;
                  wParam : WPARAM;  lParam : LPARAM): LRESULT [EXPORT, OSCall];
VAR FileName  : ARRAY [0..MAX_PATH] OF CHAR;
BEGIN
    CASE msg OF
    | WM_CLOSE       : FUNC DestroyWindow(hwnd);
    | WM_DESTROY     : PostQuitMessage(0);
    ELSE RETURN DefWindowProc(hwnd, msg, wParam, lParam);
    END;
    RETURN 0;
END WndProc;

If we want to handle mouse clicks, we need to add a WM_LBUTTONDOWN handler (or WM_RBUTTONDOWN, WM_MBUTTONDOWN, for right and middle clicks respectively).

If I or someone else refers to handling a message they mean to add it into the WndProc() of your window class as follows:

PROCEDURE WndProc(hwnd : HWND; msg : UINT;
                  wParam : WPARAM;  lParam : LPARAM): LRESULT [EXPORT, OSCall];
VAR FileName  : ARRAY [0..MAX_PATH] OF CHAR;
BEGIN
    CASE msg OF
    | WM_CLOSE       : FUNC DestroyWindow(hwnd);
    | WM_DESTROY     : PostQuitMessage(0);
    | WM_LBUTTONDOWN :                         (* <-  we just added this *)
    ELSE RETURN DefWindowProc(hwnd, msg, wParam, lParam);
    END;
    RETURN 0;
END WndProc;

The order in which you handle your messages rarely matters. As you can see we added another case into our CASE. Now we want something to happen when we get to this part of our program.

First I will present the code we want to add (that will show the user the filename of our program) and then I will integrate it into our program. Later on I will probably just show you the code and let you integrate it into your program. This is of course better for me as I don't have to type as much and it's better for you because you will be able to add the code into ANY program and not just the ones I present. If you aren't sure how to do it, look at the example zip file included with the section.

FUNC GetModuleFileName(Instance, FileName, MAX_PATH);
FUNC MessageBox(hwnd, FileName,
                "This program is:", MB_OK BOR MB_ICONINFORMATION);

Now this code does not stand on it's own, it can't just be slapped into our code any old place. We specifically want it to run when the user clicks the mouse button so this is how I would merge this small bit of code into our skeleton program:

PROCEDURE WndProc(hwnd : HWND; msg : UINT;
                  wParam : WPARAM;  lParam : LPARAM): LRESULT [EXPORT, OSCall];
VAR FileName : ARRAY [0..MAX_PATH] OF CHAR;
BEGIN
    CASE msg OF
    | WM_CLOSE       : FUNC DestroyWindow(hwnd);
    | WM_DESTROY     : PostQuitMessage(0);
    (* BEGIN NEW CODE *)
    | WM_LBUTTONDOWN : FUNC GetModuleFileName(Instance, FileName, MAX_PATH);
                       FUNC MessageBox(hwnd, FileName,
                       "This program is:", MB_OK BOR MB_ICONINFORMATION);
    (* END NEW CODE *)
    ELSE RETURN DefWindowProc(hwnd, msg, wParam, lParam);
    END;
    RETURN 0;
END WndProc;

So if you've added in that code, compile it now. If it works, click on the window and you should see a box with the name of the .exe pop up.

VAR FileName : ARRAY [0..MAX_PATH] OF CHAR;

MAX_PATH is a constant declared in WIN32.DEF that is the maximum length of a buffer needed to store a filename under Win32. We also pass MAX_PATH to GetModuleFileName() so it knows the size of the buffer.

Look up GetModuleFileName() and you will see that the first parameter is a HINSTANCE refering to the executable module (our program, the .exe file). Again we can use the variable Instance, which is imported from the WINX module.

After GetModuleFileName() is called, the buffer FileName will be filled with a null terminated string containing the name of our .exe file. We pass this value to MessageBox() as an easy way of displaying it to the user.

So if you've added in that code, compile it now. If it works, click on the window and you should see a box with the name of the .exe pop up.

If it doesn't work, here's the full code to the program. Compare it to what you have and see what, if any, mistakes you made.

MODULE window_click;

FROM WIN32      IMPORT HWND,UINT,WPARAM,LPARAM,LRESULT,HBRUSH,MAX_PATH,
                       GetModuleFileName ;
FROM WINUSER    IMPORT WM_CLOSE,WM_DESTROY,WM_LBUTTONDOWN,
                       IDI_APPLICATION,IDC_ARROW,COLOR_WINDOW,
                       WS_EX_CLIENTEDGE,WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,
                       WNDCLASS,MSG,
                       DestroyWindow,PostQuitMessage,DefWindowProc,
                       RegisterClass,CreateWindowEx,
                       ShowWindow,UpdateWindow,
                       GetMessage,TranslateMessage,DispatchMessage,
                       LoadCursor,LoadIcon,
                       MessageBox,MB_ICONEXCLAMATION,MB_OK,MB_ICONINFORMATION;
FROM WINX       IMPORT Instance,CmdShow;

FROM SYSTEM     IMPORT CAST,ADR,FUNC;

CONST
    g_szClassName = "myWindowClass";

(* Exported procedure *)
PROCEDURE WndProc(hwnd : HWND; msg : UINT;
                  wParam : WPARAM;  lParam : LPARAM): LRESULT [EXPORT, OSCall];
VAR FileName  : ARRAY [0..MAX_PATH] OF CHAR;
BEGIN
    CASE msg OF
    | WM_CLOSE       : FUNC DestroyWindow(hwnd);
    | WM_DESTROY     : PostQuitMessage(0);
    | WM_LBUTTONDOWN : FUNC GetModuleFileName(Instance, FileName, MAX_PATH);
                       FUNC MessageBox(hwnd, FileName,
                       "This program is:", MB_OK BOR MB_ICONINFORMATION);
    ELSE RETURN DefWindowProc(hwnd, msg, wParam, lParam);
    END;
    RETURN 0;
END WndProc;

VAR
    wc   : WNDCLASS;
    hwnd : HWND;
    Msg  : MSG;

BEGIN

    wc.style         := 0;
    wc.lpfnWndProc   := WndProc;
    wc.cbClsExtra    := 0;
    wc.cbWndExtra    := 0;
    wc.hInstance     := Instance;
    wc.hIcon         := LoadIcon(NIL, IDI_APPLICATION^);
    wc.hCursor       := LoadCursor(NIL, IDC_ARROW^);
    wc.hbrBackground := CAST(HBRUSH, COLOR_WINDOW+1);
    wc.lpszMenuName  := NIL;
    wc.lpszClassName := ADR(g_szClassName);

    IF RegisterClass(wc)=0 THEN
       FUNC MessageBox(NIL, "Window Class registration failed!", "Error!",
                       MB_ICONEXCLAMATION BOR MB_OK);
       RETURN ;
    END;

    hwnd := CreateWindowEx(WS_EX_CLIENTEDGE, g_szClassName,
                           "The title of my window",WS_OVERLAPPEDWINDOW,
                           CW_USEDEFAULT, CW_USEDEFAULT, 240, 120,
                           NIL, NIL, Instance, NIL);
    IF hwnd = NIL THEN
       FUNC MessageBox(NIL, "Window Creation failed!", "Error!",
                       MB_ICONEXCLAMATION BOR MB_OK);
       RETURN ;
    END;

    FUNC ShowWindow(hwnd, CmdShow);
    FUNC UpdateWindow(hwnd);

    WHILE GetMessage( Msg, NIL, 0, 0) DO
       FUNC TranslateMessage(Msg);
       FUNC DispatchMessage(Msg);
    END;
END window_click.

Copyright © 1998-2011, Brook Miles. All rights reserved. Adapted for Modula-2 by Frank Schoonjans, with permission.