This section of the archives stores flipcode's complete Developer Toolbox collection, featuring a variety of mini-articles and source code contributions from our readers.

| Application Wrapper Submitted by

This header/sourcefile set is what I normally use as a standard wrapper for all of my realtime experiments. It is a really easy way to wrap up a Windows application with simple little virtual methods for Initialization, Updating, Rendering, and Finalization. It can set up a window in fullscreen or windowed mode, and works well either way. The main advantage of this class is that it's well documented in the code, and cleanly written to be easy to modify to fit your needs. Need a DC with that window? No problem, easy to add to the base code. Want OpenGL support? Even easier to add to the base code. It can be rapidly fit to whatever you are writing at the moment. Although it should compile with no warnings, this code ain't perfect, and is really meant as a highly customizable base more than a complete library unto itself. I've used it over and over with different modifications, adding OpenGL, DirectX, or DirectDraw support as needed, but as with any code which you did not yourself write, your mileage may vary. As if it's needed for a wrapper this simple, there is a little do-nothing minimal-boilerplate test application included that will paint a nice pretty blank window for you, let you move him around, resize and close, and introduce you to a guy named Hugh. Anyway, hope you enjoy it! LICENSE
You may use this library for whatever you wish, use it in a commercial product, use it in a demo, steal it and sell it as your own, or print it out to line your parrot's cage. Just read and understand the important disclaimer kindly distributed with each codefile.

Currently browsing [appwrap.zip] (6,324 bytes) - [test.cpp] - (3,327 bytes)

    ///////////////////////////////////////////////////////////////////////////////////////////
    // Just a little test program.
    ///////////////////////////////////////////////////////////////////////////////////////////
    //  Disclaimer: This tiny test program is actually infused with the almighty engrams of
    //  Christopher Dudley. If left in it's current sorry state, I can guarantee that this
    //  code will make your cat walk funny, format your harddisk, and turn that gray thing
    //  resting in your skull to tapioca pudding. Sucks to be you, I claim no responsibility.
    ///////////////////////////////////////////////////////////////////////////////////////////
    #define WIN32_LEAN_AND_MEAN                         // You mean it's only 800kg now?
    #include <windows.h>                                // Tremble, my friends, tremble...
    #include <math.h>                                   // Really unnecessary, but cool...
    #include "Application.hpp"                          // Durr.
    #pragma intrinsic (sin, cos, atan2, sqrt)           // Who mentioned this again? Props!

    ///////////////////////////////////////////////////////////////////////////////////////////
    // This is the utterly useless test class.
    ///////////////////////////////////////////////////////////////////////////////////////////
    class TestKlasse : public Application
    {
    public:
      /////////////////////////////////////////////////////////////////////////////////////////
      // Construction, duh...
      /////////////////////////////////////////////////////////////////////////////////////////
      First( char* name ) : Application(name)
      {
      }

      /////////////////////////////////////////////////////////////////////////////////////////
      // INITIALIZATION
      /////////////////////////////////////////////////////////////////////////////////////////
      void Initialize( void )
      {
      }

      /////////////////////////////////////////////////////////////////////////////////////////
      // FINALIZATION
      /////////////////////////////////////////////////////////////////////////////////////////
      void Finalize( void )
      {
      }

      /////////////////////////////////////////////////////////////////////////////////////////
      // RENDERING
      /////////////////////////////////////////////////////////////////////////////////////////
      void Render( void )
      {
      }

      /////////////////////////////////////////////////////////////////////////////////////////
      // Control logic goes in here.
      /////////////////////////////////////////////////////////////////////////////////////////
      void Update( int msec )
      {
      }

    public:
      /////////////////////////////////////////////////////////////////////////////////////////
      // No data members.
      /////////////////////////////////////////////////////////////////////////////////////////
    };

    ///////////////////////////////////////////////////////////////////////////////////////////
    // Entrypoint, or what passes for one under Windows.
    ///////////////////////////////////////////////////////////////////////////////////////////
    int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrev, LPSTR lpCmdLine, int Cmd)
    {
      TestKlasse* app = new TestKlasse("We are Hugh, resistance is futile.");
      app->Create( 640, 480, false );
      app->Run();
      delete app;
      return(0);
    }

Currently browsing [appwrap.zip] (6,324 bytes) - [Application.cpp] - (17,875 bytes)

    ///////////////////////////////////////////////////////////////////////////////////////////
    //  Disclaimer: This wrapper library is actually infused with the almighty engrams of
    //  Christopher Dudley. If left in it's current sorry state, I can guarantee that this
    //  code will make your cat walk funny, format your harddisk, and turn that gray thing
    //  resting in your skull to tapioca pudding. Sucks to be you, I claim no responsibility.
    ///////////////////////////////////////////////////////////////////////////////////////////
    #include            "Application.hpp"

    LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

    ///////////////////////////////////////////////////////////////////////////////////////////
    // METHOD: Constructor( void )
    //
    // DESCRIPTION:
    //  This is where the magic begins! The constructor nulls out all of the pertinent stuff,
    //  stores the requested application caption, and basically gets the GDK ready to run. But
    //  while the constructor readies things, it does not create a window or otherwise prepare
    //  any rendering. Calling Run() before Create() is a very unwise idea!
    //
    // INVAR:
    //  appname         Will be used for the window caption.
    //
    // OUTVAR:
    //  None.
    //
    // THROWS:
    //  None.
    //
    // NOTES:
    //  None.
    ///////////////////////////////////////////////////////////////////////////////////////////
    Application::Application( char* appname )
    {
      /////////////////////////////////////////////////////////////////////////////////////////
      // Null out the various Windows-related handles and members, so we can't accidentally
      // start this thing going while they are still undefined. :)
      /////////////////////////////////////////////////////////////////////////////////////////
      m_hWnd      = 0;
      m_hInstance = 0;
      ZeroMemory(&m_wClass, sizeof(WNDCLASS));

      /////////////////////////////////////////////////////////////////////////////////////////
      // Null out the internal status indicators, effectively resetting the GDK.
      /////////////////////////////////////////////////////////////////////////////////////////
      m_pName       = 0;
      m_iFrames     = 0;
      m_iWidth      = 0;
      m_iHeight     = 0;
      m_iBPP        = 0;
      m_bFullscreen = false;
      m_bReady      = false;
      m_bRunning    = false;

      /////////////////////////////////////////////////////////////////////////////////////////
      // Store our cool new name.
      /////////////////////////////////////////////////////////////////////////////////////////
      m_pName = appname;
    }

    ///////////////////////////////////////////////////////////////////////////////////////////
    // METHOD: Destructor( void )
    //
    // DESCRIPTION:
    //  Well, here is where it all ends, folks. When the game is finished running, we will call
    //  the user defined cleanup routine so they can shutdown.
    //
    // INVAR:
    //  None.
    //
    // OUTVAR:
    //  None.
    //
    // THROWS:
    //  None.
    //
    // NOTES:
    //  None.
    ///////////////////////////////////////////////////////////////////////////////////////////
    Application::~Application( void )
    {
      /////////////////////////////////////////////////////////////////////////////////////////
      // Null out the internal status indicators, effectively resetting the GDK.
      /////////////////////////////////////////////////////////////////////////////////////////
      m_pName       = 0;
      m_iFrames     = 0;
      m_iWidth      = 0;
      m_iHeight     = 0;
      m_iBPP        = 0;
      m_bFullscreen = false;
      m_bReady      = false;
      m_bRunning    = false;

      /////////////////////////////////////////////////////////////////////////////////////////
      // Call the UD finalization method, in case they have things to do there.
      /////////////////////////////////////////////////////////////////////////////////////////
      Finalize();
    }

    ///////////////////////////////////////////////////////////////////////////////////////////
    // METHOD: Halt( void )
    //
    // DESCRIPTION:
    //  Breaks out of the main processing loop by inserting a destroy message into the message
    //  queue. This gives the application a chance to clean up after itself before exiting.
    //
    // INVAR:
    //  None.
    //
    // OUTVAR:
    //  None.
    //
    // THROWS:
    //  None.
    //
    // NOTES:
    //  None.
    ///////////////////////////////////////////////////////////////////////////////////////////
    void Application::Halt( void )
    {
      PostQuitMessage(0);
    }

    ///////////////////////////////////////////////////////////////////////////////////////////
    // METHOD: Error( const char* msg )
    //
    // DESCRIPTION:
    //  If something goes wrong somewhere, this method can be called to report it to the user.
    //
    // INVAR:
    //  msg             A null terminated string which will be displayed for the user.
    //
    // OUTVAR:
    //  None.
    //
    // THROWS:
    //  None.
    //
    // NOTES:
    //  Error() does not call Halt(), so errors may be recovered from.
    ///////////////////////////////////////////////////////////////////////////////////////////
    void Application::Error( const char* msg )
    {
      /////////////////////////////////////////////////////////////////////////////////////////
      // Throw up a little messagebox, so someone can know what's going on...wish I did.
      /////////////////////////////////////////////////////////////////////////////////////////
      MessageBox(m_hWnd, msg, m_pName, MB_OK);
    }

    ///////////////////////////////////////////////////////////////////////////////////////////
    // METHOD: Run( void )
    //
    // DESCRIPTION:
    //  This method, once called, will enter a near-infinite loop, wherein it will process
    //  inicoming messages, farm out rendering and logic ticks, update whatever other info
    //  may be updating, and generally manage application execution from this point on.
    //
    // INVAR:
    //  None.
    //
    // OUTVAR:
    //  None.
    //
    // THROWS:
    //  None.
    //
    // NOTES:
    //  Error() does not call Halt(), so errors may be recovered from.
    ///////////////////////////////////////////////////////////////////////////////////////////
    void Application::Run( void )
    {
      /////////////////////////////////////////////////////////////////////////////////////////
      // We use a special message structure in the event pump, and here it is!
      /////////////////////////////////////////////////////////////////////////////////////////
      MSG msg;

      /////////////////////////////////////////////////////////////////////////////////////////
      // If something died earlier on, the ready flag will be unset, and we don't want to
      // try pumping events through.
      /////////////////////////////////////////////////////////////////////////////////////////
      if( m_bReady )
      {
        m_bRunning = true;
      }

      /////////////////////////////////////////////////////////////////////////////////////////
      // With everything ready to go, we can call the user defined initialization method to let
      // it load resources, prepare things to render, etc, etc...
      /////////////////////////////////////////////////////////////////////////////////////////
      Initialize();

      /////////////////////////////////////////////////////////////////////////////////////////
      // Start the message loop! When we are finished pumping along messages, call the UD
      // methods to let them do...whatever.
      /////////////////////////////////////////////////////////////////////////////////////////
      while( m_bRunning )
      {
        if( GetMessage(&msg, NULL, 0, 0) != 0 )         // See if it's time to die yet...
        {
          TranslateMessage(&msg);
          DispatchMessage (&msg);
        }
        else
        {
          m_bRunning = false;
        }

        ///////////////////////////////////////////////////////////////////////////////////////
        // Now that we are done processing any messages, let's let the user update things!
        ///////////////////////////////////////////////////////////////////////////////////////
        Update();
        Render();

        ///////////////////////////////////////////////////////////////////////////////////////
        // For this thing to work best in both windowed, and fullscreen modes, we will want to
        // send in a WM_PAINT message to Windows, invalidating our window rect.
        ///////////////////////////////////////////////////////////////////////////////////////
        RedrawWindow(m_hWnd, NULL, NULL, RDW_INTERNALPAINT);
      }

      /////////////////////////////////////////////////////////////////////////////////////////
      // Something has killed the message loop, which means either we crashed (unlikely) or
      // for some unknown reason the user asked to quit. Kids these days, so unappreciative.
      /////////////////////////////////////////////////////////////////////////////////////////
      Halt();
    }

    ///////////////////////////////////////////////////////////////////////////////////////////
    // METHOD: Create( int w, int h, bool fs )
    //
    // DESCRIPTION:
    //  Called soon after the construction of the Application, Create() accepts a requested
    //  metric to configure the screen. Of note is the omission of a pixel-format descriptor.
    //  This is intentional, because the GDK will automatically choose the most appropriate bit
    //  depth for the application. The GDK may run either in a window, or fullscreen.
    //
    // INVAR:
    //  w               Width of the active viewport.
    //  h               Height of the active viewport.
    //  fs              True for fullscreen application, false to render into a window.
    //
    // OUTVAR:
    //  None.
    //
    // THROWS:
    //  None.
    //
    // NOTES:
    //  None.
    ///////////////////////////////////////////////////////////////////////////////////////////
    void Application::Create( int w, int h, bool fs )
    {
      /////////////////////////////////////////////////////////////////////////////////////////
      // The first thing on the list to make a window is setting up a "window class." This is
      // basically just a structure filled with option flags and settings.
      /////////////////////////////////////////////////////////////////////////////////////////
      m_wClass.style          = CS_HREDRAW|CS_VREDRAW;    // Redraw window contents on resize.
      m_wClass.lpfnWndProc    = WndProc;                  // Function that handles messages.
      m_wClass.cbClsExtra     = 0;                        // Extra space in window class.
      m_wClass.cbWndExtra     = sizeof(Application*);     // Extra space attached to window.
      m_wClass.hInstance      = GetModuleHandle(0);       // Instance handle to this application.
      m_wClass.hIcon          = 0;                        // Icon to use.
      m_wClass.hCursor        = 0;                        // Cursor to use.
      m_wClass.hbrBackground  = (HBRUSH)(COLOR_WINDOW+3); // Default background color.
      m_wClass.lpszMenuName   = 0;                        // Default main menu.
      m_wClass.lpszClassName  = m_pName;                  // Name of our shiny new window class.
      RegisterClass(&m_wClass);                           // Register the window class.

      /////////////////////////////////////////////////////////////////////////////////////////
      // With the window class good and happy, we can move on to creating the actual window
      // and ferreting away the handle. Once again, there are many properties to set before
      // the thing is ready to be shown.
      /////////////////////////////////////////////////////////////////////////////////////////
      m_hWnd = CreateWindow(  m_pName,                    // Name of a registered window class.
                              m_pName,                    // Caption on the window title.
                              WS_OVERLAPPEDWINDOW,        // Style of the window decorations.
                              CW_USEDEFAULT, CW_USEDEFAULT,// Window starting position hint.
                              w, h,                       // Width and height of content pane.
                              NULL,                       // Handle to the parent window.
                              NULL,                       // Handle to a menu resource.
                              GetModuleHandle(0),         // Handle to this program instance.
                              NULL);                      // Extra parameters.

      /////////////////////////////////////////////////////////////////////////////////////////
      // Check to make sure our window is valid. If so, we can go ahead and set ready to true,
      // since it will be okay now to invoke Run().
      /////////////////////////////////////////////////////////////////////////////////////////
      if(m_hWnd != NULL)
      {
        ///////////////////////////////////////////////////////////////////////////////////////
        // Now that our window is validated and okay, we want to push a pointer to an instance
        // of Application, so that the window procedure can extract it and send us messages.
        ///////////////////////////////////////////////////////////////////////////////////////
        SetWindowLong(m_hWnd, GWL_USERDATA, (LONG)this);

        ///////////////////////////////////////////////////////////////////////////////////////
        // Map the window onto the screen and let it have an initial WM_PAINT message, and then
        // we can go on about our business happily.
        ///////////////////////////////////////////////////////////////////////////////////////
        ShowWindow(m_hWnd, 1);
        UpdateWindow(m_hWnd);
        if(fs)                                          // Hide the cursor in fullscreen modes.
        {
          ShowCursor(FALSE);
        }
        m_bReady = true;
      }

      /////////////////////////////////////////////////////////////////////////////////////////
      // Stow the screen information for later use.
      /////////////////////////////////////////////////////////////////////////////////////////
      m_iWidth      = w;
      m_iHeight     = h;
      m_bFullscreen = fs;
      m_hInstance   = GetModuleHandle(0);
    }

    ///////////////////////////////////////////////////////////////////////////////////////////
    // FUNCTION: WindowHandler( UINT msg, WPARAM wParam, LPARAM lParam )
    //
    // DESCRIPTION:
    //  Sometimes, you just want to get at the Window's messages. In that case, overloading
    //  this method lets you do just that!
    //
    // INVAR:
    //  msg             The message we are receiving from Windows.
    //  wParam          Word-sized parameter.
    //  lParam          Long-sized parameter.
    //
    // OUTVAR:
    //  None.
    //
    // THROWS:
    //  None.
    //
    // NOTES:
    //  Must be paired with Begin().
    ///////////////////////////////////////////////////////////////////////////////////////////
    LRESULT Application::WindowHandler( UINT msg, WPARAM wParam, LPARAM lParam )
    {
      return(DefWindowProc(m_hWnd, msg, wParam, lParam));
    }

    ///////////////////////////////////////////////////////////////////////////////////////////
    // FUNCTION: WndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
    //
    // DESCRIPTION:
    //  The dreaded window procedure! Made frighteningly simple by just extracting a pointer to
    //  the application object which created the window, and passing the pertinent message data
    //  off to it.
    //
    // INVAR:
    //  hWnd            Handle to the window from which this message came.
    //  msg             The message we are receiving from Windows.
    //  wParam          Word-sized parameter.
    //  lParam          Long-sized parameter.
    //
    // OUTVAR:
    //  None.
    //
    // THROWS:
    //  None.
    //
    // NOTES:
    //  Must be paired with Begin().
    ///////////////////////////////////////////////////////////////////////////////////////////
    LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
      /////////////////////////////////////////////////////////////////////////////////////////
      // First thing we need to do is extract the pointer to our application object from the
      // extra space in the window structure.
      /////////////////////////////////////////////////////////////////////////////////////////
      Application* app = (Application*)GetWindowLong(hWnd, GWL_USERDATA);

      /////////////////////////////////////////////////////////////////////////////////////////
      // This window procedure will only handle a couple of the more important messages, the
      // rest it will leave up to the user window procedure.
      /////////////////////////////////////////////////////////////////////////////////////////
      switch( msg )
      {
      /////////////////////////////////////////////////////////////////////////////////////////
      // Program kill command.
      /////////////////////////////////////////////////////////////////////////////////////////
    	case WM_CLOSE:
        PostQuitMessage(0);
        return(0);
        break;
      /////////////////////////////////////////////////////////////////////////////////////////
      // Program kill command.
      /////////////////////////////////////////////////////////////////////////////////////////
      case WM_DESTROY:
        PostQuitMessage(0);
        return(0);
        break;
      }

      /////////////////////////////////////////////////////////////////////////////////////////
    	// With the most vital messages handled for us, we are ready to call the UD window
      // procedure. It's a simple enough task, just make sure the call to GetWindowLong() did
      // what it was supposed to do, and let it go.
    	/////////////////////////////////////////////////////////////////////////////////////////
    	if( app != NULL )
    	{
    		///////////////////////////////////////////////////////////////////////
    		//	If the user handled messages and did not return 0, let's return.
    		///////////////////////////////////////////////////////////////////////
        return(app->WindowHandler(msg, wParam, lParam));
    	}

    	///////////////////////////////////////////////////////////////////////////
    	//	Otherwise, we will let Windows take care of it.
    	///////////////////////////////////////////////////////////////////////////
      return( DefWindowProc( hWnd, msg, wParam, lParam ) );
    }

Currently browsing [appwrap.zip] (6,324 bytes) - [Application.hpp] - (3,768 bytes)

    ///////////////////////////////////////////////////////////////////////////////////////////
    //  Disclaimer: This wrapper library is actually infused with the almighty engrams of
    //  Christopher Dudley. If left in it's current sorry state, I can guarantee that this
    //  code will make your cat walk funny, format your harddisk, and turn that gray thing
    //  resting in your skull to tapioca pudding. Sucks to be you, I claim no responsibility.
    ///////////////////////////////////////////////////////////////////////////////////////////
    #ifndef             MY_APPLICATION_H                // Include guard required by law...
    #define             MY_APPLICATION_H
    #define             WIN32_LEAN_AND_MEAN             // I wish...
    #include            <windows.h>

    class Application
    {
    public:
      /////////////////////////////////////////////////////////////////////////////////////////
      // Construction and destruction, no surprises here.
      /////////////////////////////////////////////////////////////////////////////////////////
      Application( char* appname );
      ~Application();

      /////////////////////////////////////////////////////////////////////////////////////////
      // Big 'ol control methods.
      /////////////////////////////////////////////////////////////////////////////////////////
      void            Run  ( void );
      void            Halt ( void );
      void            Error( const char* msg );

      /////////////////////////////////////////////////////////////////////////////////////////
      // Handle window creation given a target width, height, and fullscreen/windowed flag.
      /////////////////////////////////////////////////////////////////////////////////////////
      void            Create( int h, int w, bool fs );

      /////////////////////////////////////////////////////////////////////////////////////////
      // These are for the user to override and implement for himself.
      /////////////////////////////////////////////////////////////////////////////////////////
      virtual void    Initialize( void )    {}          // Stubby...
      virtual void    Finalize  ( void )    {}          // Stubby...
      virtual void    Render    ( void )    {}          // Stubby...
      virtual void    Update    ( void )    {}          // Stubby...
      virtual LRESULT WindowHandler( UINT msg, WPARAM wParam, LPARAM lParam );

      ///////////////////////////////////////////////////////////////////////////////////////
      // Accessor methods for internal goodies.
      ///////////////////////////////////////////////////////////////////////////////////////
      HWND            hWnd     ( void )   {return(m_hWnd);     }
      HINSTANCE       hInstance( void )   {return(m_hInstance);}

    protected:
      ///////////////////////////////////////////////////////////////////////////////////////
      // These members hold the handles that we need to talk to Windows properly.
      ///////////////////////////////////////////////////////////////////////////////////////
      HWND            m_hWnd;
      HINSTANCE       m_hInstance;
      MSG             m_hMsg;
      WNDCLASS        m_wClass;

      ///////////////////////////////////////////////////////////////////////////////////////
      // Status indicators and counters for the GDK. These keep track of the intrinsic state
      // of the GDK at all times, and can be used for statistical purposes.
      ///////////////////////////////////////////////////////////////////////////////////////
      char *          m_pName;
      unsigned long   m_iFrames;
      int             m_iWidth;
      int             m_iHeight;
      int             m_iBPP;
      bool            m_bFullscreen;
      bool            m_bReady;
      bool            m_bRunning;
    };

    #endif                                              // End almighty include-guard.

The zip file viewer built into the Developer Toolbox made use of the zlib library, as well as the zlibdll source additions.

Copyright 1999-2008 (C) FLIPCODE.COM and/or the original content author(s). All rights reserved.
Please read our Terms, Conditions, and Privacy information.