Click here to Skip to main content
Licence 
First Posted 20 Dec 2004
Views 43,327
Bookmarked 22 times

An Event Driven Windows GUI Framework

By | 20 Dec 2004 | Article
Shows how to build a simple event based Windows GUI framework using template function specializations.

Introduction

In this article, I show a method for building a simple event-based framework for Windows GUI applications based on a technique I outlined in a previous article: Event Driven Programming using Template Specialization.

This event driven framework uses template function specializations, which allow the user of an event-driven library to write event handlers, without having to explicitly register them with some kind of call-back system. This is an attractive alternative to typical event-driven libraries which use function pointers or require overloading of virtual functions.

Responding to Windows Events

In order to respond to Windows events using the "winevent.hpp" library, we include the header and write our event handlers. The only requirement is that they have the following signature:

  template<>
  LRESULT OnMsg<WM_xxx>(HWND hWnd, WPARAM wParam, LPARAM lParam)
  {
    // do custom processing here
  }

The WM_xxx is the Windows message code that you want to respond to. It is common to call the function DefWindowProc() from within a custom handler.

Nothing else is required from the programmer, this function will be called when the application receives the given Windows message. Registration occurs automatically using some template magic.

Sample Application

I have included a sample application which demonstrates the event-driven framework. This program is a minimal Windows application that upon a key-press, sends 100,000 WM_TIMER messages to itself. It then writes to the screen how long this process took in msec.

  #include <windows.h>
  #include "winevent.hpp"

  #include <time.h>
  #include <stdio.h>

  int nInt = 0;
  int nStart = 0;
  int nEnd = 0;
  char buf[255];

  //////////////////////////////////////////////////////////////
  // Event handlers

  template<>
  LRESULT OnMsg<WM_CREATE>(HWND hWnd, WPARAM wParam, LPARAM lParam)
  {
    strcpy(buf, "Press any key to start test");
    return DefWindowProc(hWnd, WM_CREATE, wParam, lParam);
  }

  template<>
  LRESULT OnMsg<WM_KEYDOWN>(HWND hWnd, WPARAM wParam, LPARAM lParam)
  {
    nInt = 0;
    strcpy(buf, "running test, please wait ...");
    InvalidateRect(hWnd, NULL, true);
    SendMessage(hWnd, WM_PAINT, 0, 0);
    nStart = GetTickCount();
    PostMessage(hWnd, WM_TIMER, 0, 0);
    return DefWindowProc(hWnd, WM_KEYDOWN, wParam, lParam);
  }

  template<>
  LRESULT OnMsg<WM_TIMER>(HWND hWnd, WPARAM wParam, LPARAM lParam)
  {
    if (nInt++ < 100000) {
      PostMessage(hWnd, WM_TIMER, 0, 0);
    } else {
      nEnd = GetTickCount();
      sprintf(buf, "Time elapsed = %d msec", 
             ((nEnd - nStart) * 1000) / CLOCKS_PER_SEC);
      InvalidateRect(hWnd, NULL, true);
    }
    return 0;
  }

  template<>
  LRESULT OnMsg<WM_DESTROY>(HWND hWnd, WPARAM wParam, LPARAM lParam)
  {
    PostQuitMessage(0);
    return 0;
  }

  template<>
  LRESULT OnMsg<WM_PAINT>(HWND hWnd, WPARAM wParam, LPARAM lParam)
  {
    PAINTSTRUCT ps;
    HDC hdc;
    hdc = BeginPaint(hWnd, &ps);
    TextOut(hdc, 100, 100, buf, static_cast<int>(strlen(buf)));
    EndPaint(hWnd, &ps);
    return DefWindowProc(hWnd, WM_PAINT, wParam, lParam);
  }

  //////////////////////////////////////////////////////////////
  // Entry point for the application.

  int WINAPI WinMain(HINSTANCE hInstance, 
      HINSTANCE hPrevInstance, LPSTR szCmdLine, int nCmdShow)
  {
    InitMsgHandlers<NULL>();

    static char szAppName[] = "demo";
    HWND        hwnd;
    MSG         msg;
    WNDCLASSEX  wndclass;

    wndclass.cbSize         = sizeof(wndclass);
    wndclass.style          = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc    = WndProc;
    wndclass.cbClsExtra     = 0;
    wndclass.cbWndExtra     = 0;
    wndclass.hInstance      = hInstance;
    wndclass.hIcon          = LoadIcon(NULL, IDI_APPLICATION);
    wndclass.hIconSm        = LoadIcon(NULL, IDI_APPLICATION);
    wndclass.hCursor        = LoadCursor(NULL, IDC_ARROW);
    wndclass.hbrBackground  = (HBRUSH) GetStockObject(WHITE_BRUSH);
    wndclass.lpszClassName  = szAppName;
    wndclass.lpszMenuName   = NULL;

    RegisterClassEx(&wndclass);

    hwnd = CreateWindow(szAppName, "Static Dispatch Demo",
      WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, CW_USEDEFAULT,
      CW_USEDEFAULT, CW_USEDEFAULT,
      NULL, NULL, hInstance, NULL);

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    while ( GetMessage(&msg, NULL, 0, 0) ) {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }
    return static_cast<int>(msg.wParam);
  }

The only requirement that is required to use the winevent.hpp event handling library is that there is a call to InitMsgHandlers<NULL>();. The NULL parameter is ignored, but is required to assure the correct order of compilation to use the library.

Event Dispatching

The winevent.hpp file takes care of the responsibility of dispatching events to the user defined specialization or to call the default handler.

  // this is the function that is called if the event is not handled by the user
  template<typename int Msg_N>
  LRESULT OnMsg(HWND hWnd, WPARAM wParam, LPARAM lParam) {
    return DefWindowProc(hWnd, Msg_N, wParam, lParam);
  }

  // this is the type of a function pointer used to represent
  typedef LRESULT (*MsgFxnPtr)(HWND, WPARAM, LPARAM);

  // this is a list of function pointers which is populated with
  // either default handlers (as above) or with user defined specializations
  MsgFxnPtr MsgHandlers[WM_APP + 1024];

  // this is the core event dispatcher
  LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    return MsgHandlers[message](hWnd, wParam, lParam);
  }

  // this populates the array of event pointers with either the default function
  // of the user defined specialization of one exists.
  template<typename int Dummy>
  void InitMsgHandlers() {
    // these are the windows reserved messages
    MsgHandlers[0] = &OnMsg<0>;
    MsgHandlers[1] = &OnMsg<1>;
    ...
    MsgHandlers[1023] = &OnMsg<1023>;

    // these are user defined messages
    MsgHandlers[WM_APP + 0] = &OnMsg<WM_APP + 0>;
    MsgHandlers[WM_APP + 1] = &OnMsg<WM_APP + 1>;
    ...
    MsgHandlers[WM_APP + 1023] = &OnMsg<WM_APP + 1023>;
  }

The user may notice a lot of superfluous Windows messages included, but this way, we can support future Windows messages as long as they follow below 1024 (WM_USER).

The InitMsgHandlers() function fills an array of message handlers with either a user defined template function specialization if it exists, otherwise it simply uses the default OnMsg(...) function which calls DefWindowProc(...). We use the compiler to do the work of deciding which function to use.

Summary

There are other ways to implement an event dispatching system for a Windows GUI, but this represents an easy to use alternative, function pointer or virtual function dispatch systems, which require significantly more work on behalf of the user of an event-driven framework.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Christopher Diggins

Architect
Autodesk
Canada Canada

Member

Follow on Twitter Follow on Twitter
I've been programming personal computers since my I got my first Atari 400 in 1980. I currently work at Autodesk as an SDK specialist.
 
One of my passions is the design and implementation of programming languages. My current project is the Jigsaw library which includes a parsing engine and other utilities for implementing programming languages in C#.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
QuestionWhy a vector for MsgFxnPtr ? PinmemberPotsie0:19 22 Dec '04  
AnswerRe: Why a vector for MsgFxnPtr ? Pinmembercdiggins4:47 22 Dec '04  
QuestionTwo Windows? PinmemberPotsie3:55 21 Dec '04  
AnswerRe: Two Windows? Pinmembercdiggins4:57 21 Dec '04  
GeneralCool articles... PinsitebuilderUwe Keim22:32 20 Dec '04  
GeneralRe: Cool articles... Pinmembercdiggins4:54 21 Dec '04  
GeneralDemo don't work :( PinmemberPJ Arends21:27 20 Dec '04  
GeneralOnly VC++ 7.1 Pinmembercdiggins4:53 21 Dec '04  
QuestionCan you elaborate ? PinmemberSébastien Lorion20:01 20 Dec '04  
AnswerRe: Can you elaborate ? Pinmembercdiggins5:01 21 Dec '04  
AnswerRe: Can you elaborate ? Pinmembercdiggins5:14 21 Dec '04  

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Mobile
Web01 | 2.5.120517.1 | Last Updated 20 Dec 2004
Article Copyright 2004 by Christopher Diggins
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid