Upcoming Events
Unite 2010
11/10 - 11/12 @ Montréal, Canada

GDC China
12/5 - 12/7 @ Shanghai, China

Asia Game Show 2010
12/24 - 12/27  

GDC 2011
2/28 - 3/4 @ San Francisco, CA

More events...
Quick Stats
65 people currently visiting GDNet.
2406 articles in the reference section.

Help us fight cancer!
Join SETI Team GDNet!
Link to us Events 4 Gamers
Intel sponsors gamedev.net search:

Window Class Registration

Window styles (and trait classes that store styles) are a means of customizing the appearance of an individual instance of a window class. Window styles can do a great deal to change the look of a given window, but we require more control over the look of the window than window styles alone provide. Specifically, we'd like to change the background color to black. To do this we need to customize the window class itself.

So, what window class does an instance of our CDxWindowImpl belong to, and more importantly, how can we change the class styles and attributes to suit our needs? If you'll recall from the previous article we could choose a specific name for the window class used by our implementation by including the DECLARE_WND_CLASS macro in the class definition. Doing that gives us a definite name but doesn't tell us much else or give us any more control. Besides, CDxWindowImpl doesn't yet include the DECLARE_WND_CLASS macro. So what's the name of the class it's using? The answer to that last question can be found in a function called AtlModuleRegisterWndClassInfo()1 which is defined in atlwin.h. A bit of code in that function assigns a (pseudo) random name to the window class if one is not specifically provided elsewhere2. The answer to the first two questions lies in the DECLARE_WND_CLASS macro itself, but first we need to cover a structure called CWndClassInfo.

CWndClassInfo

The CWndClassInfo structure is used by the WTL libraries to store information needed to register a window class. Below are the members of CWndClassInfo1.

[listing 2.8]

WNDCLASSEX m_wc;
LPCSTR m_lpszOrigName;
WNDPROC pWndProc;
LPCSTR m_lpszCursorID;
BOOL m_bSystemCursor;
ATOM m_atom;
CHAR m_szAutoName[13];

ATOM Register(WNDPROC* p)
{
  return AtlModuleRegisterWndClassInfo(&_Module, this, p);
}

As you can see the first member of CWndClassInfo is a WNDCLASSEX structure. This is the same structure passed to the Windows API function RegisterClassEx(). The m_lpszOrigName is used to support super classing. The pWndProc is a pointer to the window procedure for the class. The m_lpszCursorID and m_bSystemCursor support the use of cursor resources. The m_atom member stores the ATOM value returned by RegisterClassEx(), and m_szAutoName stores the pseudo random class name, if used. The sole method, Register(), registers the class defined by this structure by calling AtlModuleRegisterWndClassInfo(). Super classing and using custom cursors won't be covered in this series3.

DECLARE_WND_CLASS and DECLARE_WND_CLASS_EX

The DECLARE_WND_CLASS macro does nothing more than create a function that returns an instance of a CWndClassInfo class [1]. Below is the definition of the DECLARE_WND_CLASS macro.

[listing 2.9]

#define DECLARE_WND_CLASS(WndClassName) \
static CWndClassInfo& GetWndClassInfo() \
{ \
  static CWndClassInfo wc = \
  { \
    { sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS, StartWindowProc, \
      0, 0, NULL, NULL, NULL, (HBRUSH)(COLOR_WINDOW + 1), NULL, WndClassName,  NULL }, \
      NULL, NULL, IDC_ARROW, TRUE, 0, _T("") \
  }; \
  return wc; \
}

As you can see the function created is a static one called GetWndClassInfo(). The macro is a bit of a mess to look at but the class styles (CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS) and background color (COLOR_WINDOW + 1) jump right out.

Since all the DECLARE_WND_CLASS macro does is define a static function, we could omit the macro and define the function ourselves. By doing this we'd gain complete control over every item in the WNDCLASSEX structure. Such a function might look like the one below, which changes the background color to black by using the helper function AtlGetStockBrush()4.

[listing 2.10]

static CWndClassInfo& GetWndClassInfo()
{
  static CWndClassInfo wc =
  {
    {sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS,
     StartWindowProc,
     0, 0, NULL, NULL, NULL, AtlGetStockBrush(BLACK_BRUSH), NULL,
     GetWndClassName() , NULL}, NULL, NULL, IDC_ARROW, TRUE, 0, _T("")
  };
  return wc;
}

But if all we'd like to do is change the background color and perhaps alter the class styles we may not need go though the trouble of creating our own GetWndClassInfo() function. The WTL provides another macro called DECLARE_WND_CLASS_EX which allows us to set these often customized properties. The definition of DECLARE_WND_CLASS_EX is almost identical to DECLARE_WND_CLASS except that is adds two additional parameters, style and bkgnd, which as you might expect, set the class styles and background color.

Unfortunately this macro, though worth mentioning, won't do. The value passed to bkgnd must be one of the predefined brush constants like COLOR_WINDOW, COLOR_WINDOWTEXT, etc. Using AtlGetStockBrush() here won't work, so we'll stick with defining our own GetWndClassInfo() function. This has the added advantage of giving us complete control over the class.

The GetWndClassName() Function

One of the features I'd like for CDxWindowImpl to have is the ability to detect and prevent multiple instances of itself. To do this we'll need to be able to easily change the window class name. Each project based on CDxWindowImpl will need to use a unique class name. If two projects use the same class name there's a chance Game A would 'see' an instance of Game B as an instance of itself.

We need a means of easily setting the class name, and there are a few options to choose from. We could simply require that each class that derives from CDxWindowImpl define it's own GetWndClassInfo() function. This would work well, but unless we need to change some item other than the class name it'd be nice to be able to use the GetWndClassInfo() function inherited from CDxWindowImpl. We could use #define to assign a token to a string literal and use that inside GetWndClassInfo(), but I dislike this approach. Instead we'll override a static function inherited from CWindow called GetWndClassName(). CWindow's implementation of GetWndClassName() simply returns NULL. If this function is overridden to return a non-NULL value that value will be used by the WTL library to name the window class. This approach also allows us to call GetWndClassName() whenever we need the name of the window class. We can even use it in GetWndClassInfo(), and I do for the sake of clarity. Technically this isn't necessary so long as the class name used in GetWndClassInfo() is NULL. If not, the name set in GetWndClassInfo() is used in place of the name returned by GetWndClassName().

Let's give CDxWindowImpl a definite class name and a black background color. We'll also add some code to prevent multiple instances of the same application from running on the same machine. Make the following changes to CDxWindowImpl.h:

[listing 2.11]

#ifndef __ATLWIN_H__
    #error CDxWindowImpl.h requires atlwin.h to be included first
#endif

typedef CWinTraits<WS_OVERLAPPEDWINDOW,0> CDxAppWinTraits;

template <class T, class TBase = CWindow, class TWinTraits = CDxAppWinTraits>
class CDxWindowImpl : public CWindowImpl<T,TBase,TWinTraits>
{
public:

  static CWndClassInfo& GetWndClassInfo()
  {
    static CWndClassInfo wc =
    {
      { sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS,
         StartWindowProc,0, 0, NULL, NULL, NULL,
        (AtlGetStockBrush(BLACK_BRUSH),NULL,GetWndClassName(), NULL
      },
      NULL, NULL, IDC_ARROW, TRUE, 0, _T("")
    };
    return wc;
  }

  static LPCTSTR GetWndClassName()
  {
    return _T("CDxWindowImpl");
  }

  BOOL AllowMultipleInstances()
  {
    return false;
  }


  BOOL PreviousInstanceFound(LPCTSTR lpClassName, LPCTSTR lpWindowName)
  {
    HWND hwnd = FindWindow(lpClassName,lpWindowName);

    if(hwnd)
    {
      if(!T::AllowMultipleInstances())
      {
        //  Flash the existing window
        FLASHWINFO flashinfo;

        flashinfo.cbSize = sizeof(flashinfo);
        flashinfo.hwnd = hwnd;
        flashinfo.dwFlags = FLASHW_ALL;
        flashinfo.uCount = 2;
        flashinfo.dwTimeout = 0;

        FlashWindowEx(&flashinfo);
      }
      return true;
    }

    return false;
  }

  HWND Create(HWND hWndParent, RECT& rcPos, LPCTSTR szWindowName = NULL,
                           DWORD dwStyle = 0, DWORD dwExStyle = 0,
                           UINT nID = 0, LPVOID lpCreateParam = NULL)
  {
    if(PreviousInstanceFound(GetWndClassInfo().m_wc.lpszClassName,szWindowName)
                               & !AllowMultipleInstances())
      return NULL;

    HWND hwnd = CWindowImpl<CDxAppWindow,CWindow,CDxAppWinTraits>::Create(hWndParent,
                        rcPos,szWindowName,dwStyle,dwExStyle,nID,lpCreateParam);

    return hwnd;
  }

  BEGIN_MSG_MAP(CDxAppWindow)
    MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
  END_MSG_MAP()

LRESULT OnDestroy( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled )
  {
    PostQuitMessage(0);
    bHandled = FALSE;
    return 0;
  }

};

We now have functions for detecting and preventing multiple instances and a Create() function to bring them all together. Each new project based on CDxWindowImpl now just needs to return a different string from GetWndClassName() to make the functionality work. And if you wish to allow multiple instances just change AllowMultipleInstances() to return true. Let's add a GetWndClassName() override to CDxAppWindow in main.cpp.

[listing 2.12]

//...
class CDxAppWindow : public CDxWindowImpl<CDxAppWindow>
{
public:
  static LPCTSTR GetWndClassName()
  {
    return _T("CDxAppWindow");
  }

  BEGIN_MSG_MAP(CDxAppWindow)
    CHAIN_MSG_MAP(CDxWindowImpl<CDxAppWindow>)
  END_MSG_MAP()
};
//...




Full Screen and Back


Contents
  Introduction
  Window Traits
  Window Class Registration
  Full Screen and Back

  Source code
  Printable version
  Discuss this article

The Series
  Getting Started
  Windowing