Solved

A 32 / 64 Bit Window Wrapper, Efficient and Thread Safe - Using class member function callbacks

Posted on 2003-12-05
6
527 Views
Last Modified: 2008-02-01
This is not a question, I just wanted to dump my code here for others to see and/or make use of. Any feedback and/or suggestions will be much appreciated.
I have attempted to put together a window wrapper that will provide an easy replacement for RegisterClassEx and CreateWindowEx that uses a standard WNDPROC function, however, this function is placed in the object of your choice. i.e. You create an object (class) and use the CreateXWindow function instead of RegisterClassEx and CreateWindowEx and specify a member function as the WNDPROC. I have attempted to make it thread safe, also I do not utilise the GWL_USERDATA area as almost every other wrapper does, this means it is free for you to use as you wish (as it should be!). Instead the pointer to the object is stored as extra window bytes. This should not interfere with other things (such as certain dialogs) that use the extra window bytes are as the pointer is placed at the end and the size of the extra bytes area is the size you specify in the call + the size of one long pointer.

To use this just copy and paste the code into a file named XWindow.h and include it in your project. I could not make a lib/dll or place the code in a seperate .cpp file as it makes use of STL class templates. So...one single .h file it is!

To use this all you need do is include the header file and then call CreateXWindow from your object instead of using RegisterClassEx and CreateWindowEx.

Parameter Info:
CreateXWindow(T* ptrObj, LRESULT (T::*PtrFn)(HWND, UINT, WPARAM, LPARAM),   LPXWNDINFO lpXWndInfo)

T* ptrObj
is a pointer to an object of the type in which the callback function resides

LRESULT (T::*PtrFn)(HWND, UINT, WPARAM, LPARAM)
is basically a WNDPROC function. Create a member function in the object/class that returns a LRESULT and takes (HWND, UINT, WPARAM, LPARAM) as the parameters, exactly the same as a normal WNDPROC function.

LPXWNDINFO lpXWndInfo
is a long pointer to a XWNDINFO structure. This is declared also in the code provided. This header combines the elements required for a WNDCLASSEX structure and the parameters passed to CreateWindowEx. It adds only one new attribute which is the dwFlags attribute. This attribute has one of three states:

XWI_REGISTER_CLASS specifies that only a new class is being registered
XWI_CREATE_XWINDOW specifies that only a new window is being created
XWI_REG_AND_CREATE specifies that both are being created

If you specify XWI_REG_AND_CREATE all members of the structure must be filled.
Otherwise the structure consists of two parts;

General Settings:
      UINT      cbSize;
      HINSTANCE   hInstance;
      LPCTSTR     lpszClassName;

These must be filled regardless of which flags are set.

Specific Settings:
If you specified XWI_REGISTER_CLASS then
    UINT        uiClassStyle;  
    int         cbClsExtra;
    int         cbWndExtra;
    HICON       hIcon;
    HCURSOR     hCursor;
    HBRUSH      hbrBackground;
    LPCTSTR     lpszMenuName;
    HICON       hIconSm;
must be filled as well as the general settings.

If you secified XWI_CREATE_XWINDOW then
    LPCTSTR lpWindowName;
    DWORD dwExStyle;
    DWORD dwStyle;
    int xPosition;
    int yPosition;
    int nWidth;
    int nHeight;
    HWND hWndParent;
    HMENU hMenu;
    LPVOID lpParam;
must be filled as well as the general settings.

The function returns either NULL or a valid window handle for the newly created handle. If you specified XWI_REGISTER_CLASS then the return value will be NULL but you can check for success by calling GetLastError() and comparing the value against XW_NO_ERROR.

If the function fails then GetLastError() will return one of the following values:
XW_ERR_CLASS_EXISTS
XW_ERR_CLASS_NOT_FOUND
XW_ERR_REGISTER_CLASS
XW_ERR_CREATE_WINDOW
XW_ERR_INVALID_PARAM
XW_ERR_MEMORY_ALLOCATION
XW_ERR_UNSPECIFIED

If you wish to access the XWindow_T object directly you will need to use something like:

DWORD dwOffset = GetClassLongPtr(hWnd,GCL_CBWNDEXTRA) - (sizeof(LONG_PTR));
XWindow_T<T> FAR*  lpXWnd = (XWindow_T<T> FAR*)((LONG_PTR)GetWindowLongPtr(hWnd, dwOffset));

You will need to replace the value T in <T> for the appropriate object type. You should not need to access this under normal cirumstances as it is meant to be transparent in it's use.

Once you have a valid HWND returned you can continue to use the window exactly the same as any other you created with CreateWindowEx.

Example use:

#include "XWindow.h"

class myClass{
    myClass();

    BOOL CreateOurWindow()
    {
      LPXWNDINFO lpXWndInfo = new XWNDINFO;

      lpXWndInfo->dwFlags            = XWI_REG_AND_CREATE;
      lpXWndInfo->hInstance      = hInstance;
      lpXWndInfo->lpszClassName      = _T("MainWClass");

      lpXWndInfo->cbSize            = sizeof(lpXWndInfo);
      lpXWndInfo->cbClsExtra      = 0;
      lpXWndInfo->cbWndExtra      = 0;
      lpXWndInfo->hbrBackground      = (HBRUSH)COLOR_WINDOW;
      lpXWndInfo->hCursor            = LoadCursor(NULL, IDC_ARROW);
      lpXWndInfo->hIcon            = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_MAIN));
      lpXWndInfo->hIconSm      = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_MAIN));
      
      lpXWndInfo->lpszMenuName      = NULL;
      lpXWndInfo->uiClassStyle      = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
      lpXWndInfo->dwExStyle      = WS_EX_TOOLWINDOW;
      lpXWndInfo->lpszClassName      = _T("XWindow");
      lpXWndInfo->lpWindowName      = _T("XWinTest");
      lpXWndInfo->dwStyle      = WS_OVERLAPPED|WS_SYSMENU|WS_TABSTOP|WS_CLIPCHILDREN|WS_CLIPSIBLINGS;
      lpXWndInfo->xPosition      = CW_USEDEFAULT;
      lpXWndInfo->yPosition      = CW_USEDEFAULT;
      lpXWndInfo->nWidth            = 440;
      lpXWndInfo->nHeight            = 68;
      lpXWndInfo->hWndParent      = NULL;
      lpXWndInfo->hMenu            = NULL;
      lpXWndInfo->lpParam            = NULL;
       
      hWnd = CreateXWindow(this,&myClass::WndProc, lpXWndInfo);
      ShowWindow(hWnd,1);

               // Now just implement a MSG loop as you would with any other window
    }

    LRESULT WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
      switch (uMsg)
                {
                case WM_CREATE:
                        {
                        // ... Your code goes here
                        }
                        break;

                default:
                return DefWindowProc(hWnd, uMsg, wParam, lParam);
                }
    {
   

    ~myClass();
};



Code output below:



//////////////////////////////////////////////////////////////////////
// XWindow Class
//
// by Gareth Paterson 01/12/03
//
// The XWindow class is used as a wrapper for the RegisterClassEx and
// the CreateWindowEx functions. It is designed specifically to allow
// the use of class member functions for the window callback function
// used to process messages/events from the window, a feature that is
// not supported by normal use of RegisterClassEx and CreateWindowEx.
// This is done through use of STL class templates and a pointer map.
// It is designed to be multi/thread safe through the use of critical
// sections. Only one thread can set or retrieve a pointer in the map
// at any one time. The pointer is placed in in the map directly
// before CreateWindowEx is called and is retrieved and stored in the
// window itself before the first message is processed by the window.
// This means that theoretically it is impossible to have our pointer
// overwritten by our own thread or by nested windows created in the
// initial window creation events such as WM_NCCREATE or WM_CREATE.
// The benefit of this method is that the pointer is stored in extra
// window memory as opposed to GWL_USERDATA, consequently this space
// is still free for use in other parts of the program.
// Although not ideal all the code for this has been placed in this
// header file, this is because we are using templates which makes it
// difficult to offload the code into a seperate file.
//
// Required header files:
//
//            windows.h
//            map.h
//
//////////////////////////////////////////////////////////////////////
#if !defined(_XWindow_H_)
#define _XWindow_H_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#pragma warning(disable : 4786 4291) // These warnings clutter up the output window


// windows.h should be included by the program elsewhere, not here. Alowing it
// to be included here means that options such as WIN32_LEAN_AND_MEAN may have
// not been defined and could result in extra unnecessary code/overhead.
// Relying on this include also leads to sloppy programming.
#ifndef _WINDOWS_
      #include <windows.h>
#endif

// map.h is not so important, the user may choose to include this elsewhere
// should they wish. It will make little odds to the program.
#ifndef _MAP_
      #include <map>                        // Include map header if not already included
#endif


//////////////////////////////////////////////////////////////////////
// XWNDINFO flag settings.
//
// These values are set for the dwFlags value of XWNDINFO structure
// XWI_REGISTER_CLASS specifies that a new class is being registered
// XWI_CREATE_XWINDOW specifies that a new window is being created
// XWI_REG_AND_CREATE specifies that both are being created
//
// XWI_REG_AND_CREATE is the same as XWI_REGISTER_CLASS|XWI_CREATE_XWINDOW
//
//////////////////////////////////////////////////////////////////////
#define XWI_REGISTER_CLASS                  0x00000001L
#define XWI_CREATE_XWINDOW                  0x00000002L
#define XWI_REG_AND_CREATE                  0x00000003L


//////////////////////////////////////////////////////////////////////
// XWindow Errors
//
// XW_NO_ERROR                        Indicates success. i.e. No error was found
// XW_ERR_CLASS_EXISTS            Attempt to register a class that already exists
// XW_ERR_CLASS_NOT_FOUND      Attempt to use a class that does not exist
// XW_ERR_REGISTER_CLASS      An error occured when registering the class
// XW_ERR_CREATE_WINDOW            An error occured when creating the window
// XW_ERR_INVALID_PARAM            An invalid parameter has been passed or used
// XW_ERR_UNSPECIFIED            You tell me!
//////////////////////////////////////////////////////////////////////
#define XW_NO_ERROR                              0x00000000L
#define XW_ERR_CLASS_EXISTS                  0x00000001L
#define XW_ERR_CLASS_NOT_FOUND            0x00000002L
#define XW_ERR_REGISTER_CLASS            0x00000004L
#define XW_ERR_CREATE_WINDOW            0x00000008L
#define XW_ERR_INVALID_PARAM            0x00000010L
#define XW_ERR_MEMORY_ALLOCATION      0x00000020L
#define XW_ERR_UNSPECIFIED                  0x00000040L


//////////////////////////////////////////////////////////////////////
// XWNDINFO structure.
//
// This is used to replace and combine the WNDCLASSEX structure with
// the parameters passed to the CreateWindowEx function so that only
// one window structure needs to be created/passed to the CreateXWindow
// structure. It is used regardless of whether we are creating a new
// window class, a new window instance of an existing class or both.
// The dwFlags member is responsible for determining which of these
// is being created. The general settings must be set regardless of
// which is flag is set. If XWI_REGISTER_CLASS is specified then all
// the General settings must be set. If XWI_CREATE_XWINDOW is set then
// all the window settings must be set and the class name must be a
// valid registered window class. If XWI_REGISTER_CLASS|XWI_CREATE_XWINDOW
// or XWI_REG_AND_CREATE is specified then all values must be set.
//
//////////////////////////////////////////////////////////////////////
typedef struct _XWinInfo
{
      // General XWNDINFO settings
      DWORD            dwFlags;
      UINT            cbSize;
      HINSTANCE   hInstance;
      LPCTSTR     lpszClassName;

      // Window class settings
    UINT        uiClassStyle;  
    int         cbClsExtra;
    int         cbWndExtra;
    HICON       hIcon;
    HCURSOR     hCursor;
    HBRUSH      hbrBackground;
    LPCTSTR     lpszMenuName;
    HICON       hIconSm;

      // Window settings
      LPCTSTR lpWindowName;
      DWORD dwExStyle;
    DWORD dwStyle;
    int xPosition;
    int yPosition;
    int nWidth;
    int nHeight;
    HWND hWndParent;
    HMENU hMenu;
    LPVOID lpParam;
}XWNDINFO, FAR *LPXWNDINFO;


//////////////////////////////////////////////////////////////////////
// CreateXWindow template function
//
// This function acts as a wrapper for the XWindow class. Although the
// window object could be created manually this provides a more elegant
// approach much more in line with the standard Win32 methods and
// function calls.
//
//////////////////////////////////////////////////////////////////////
template<class T>
HWND CreateXWindow(T* ptrObj, LRESULT (T::*PtrFn)(HWND, UINT, WPARAM, LPARAM),
                           LPXWNDINFO lpXWndInfo)
{
      // Check our flags are in valid range
      if((lpXWndInfo->dwFlags > 0L) && (lpXWndInfo->dwFlags < 4L))
      {
            XWindow_T<T> FAR *lpXWndT = new(std::nothrow) XWindow_T<T>(ptrObj,PtrFn);
            if(lpXWndT) // Make sure we have a valid XWindow_T<T>
                  return lpXWndT->Create_T(lpXWndInfo);
            else
            {
                  // Error allocating memory for the XWindow_T
                  SetLastError(XW_ERR_MEMORY_ALLOCATION);
                  return NULL; // Return a null handle
            }
            // No need to error much check here, this is carried out by Create_T
      }

      // If dwFlags is invalid
      SetLastError(XW_ERR_INVALID_PARAM);
      return NULL; // Return a null handle
}


//////////////////////////////////////////////////////////////////////
// XWindow base class
//
// This is used as it is not possible to typedef a template class
// without specifyng a type and as we do not want to specifically
// set the type (and lose the generic nature of the templates) we
// must do this instead. It allows us to create our pointer map
// without using templates which makes life a little simpler too.
//
//////////////////////////////////////////////////////////////////////
typedef class XWindow
{
public:
      // Default constructor
      XWindow(){}
      // Default destructor
      virtual ~XWindow(){}      
} XWINDOW, FAR *LPXWINDOW;


//////////////////////////////////////////////////////////////////////
// XWindow_T template class
//
// This is the main part of our XWindow it's only constructor requires
// that we pass it a pointer to the object in which the window function
// resides and the address of the window function itself. This info
// is then stored using member variables and the Create_T function is
// then called. The Create_T function will either create a new window
// class or use an existing one depending on the info passed in the
// XWNDINFO structure.
//
//////////////////////////////////////////////////////////////////////
template<class T>
class XWindow_T : public XWindow
{
public:
      // Typedef our WNDPROC clone function for easier manipulation and use
      typedef LRESULT (T::*PtrMemFnt)(HWND, UINT, WPARAM, LPARAM);

      // Constructor, initialises the member variables
      XWindow_T(T* ptrObj, PtrMemFnt ptrFn)
            : m_ptrObject(ptrObj), m_ptrMemFnt(ptrFn), m_bAlwaysDelete(TRUE) {}

      // Returns state of m_bAlwaysDelete flag
      BOOL DeleteAlways() { return m_bAlwaysDelete; }

      // Sets and returns new state of m_bAlwaysDelete flag
      BOOL DeleteAlways(BOOL bTrue)
      { return m_bAlwaysDelete = bTrue; }

      // The main function on the XWindow_T class. This registers a new window class if
      // XWI_REGISTER_CLASS or XWI_REG_AND_CREATE are specified and creates a window of
      // the specific class if XWI_CREATE_XWINDOW or XWI_REG_AND_CREATE is specified.
      HWND Create_T(LPXWNDINFO lpXWnd)
      {
            // ***Reminder***
            // XWI_REGISTER_CLASS specifies that a new class is being registered
            // XWI_CREATE_XWINDOW specifies that a new window is being created
            // XWI_REG_AND_CREATE specifies that both are being created
      

            //////////////////////////////////////////////////////////////////////
            // Window Class (Registering/Retrieving)
            //////////////////////////////////////////////////////////////////////
            // Check if class exists, if not check if we should register a new one

            if(lpXWnd->dwFlags & XWI_REGISTER_CLASS)
            {            
                  // Used to get or register the window class information
                  LPWNDCLASSEX lpWCX = new(std::nothrow) WNDCLASSEX;
                  if(!lpWCX)
                  {
                        SetLastError(XW_ERR_MEMORY_ALLOCATION);
                        return NULL;
                  }

                  if(!GetClassInfoEx(lpXWnd->hInstance,lpXWnd->lpszClassName,lpWCX))
                  {
                        // Create a new window class
                        lpWCX->cbSize = sizeof(WNDCLASSEX);                                    // size of structure
                        lpWCX->style = lpXWnd->uiClassStyle;                              // class style
                        lpWCX->lpfnWndProc = XWindow_T<T>::stSetPointer_T;            // callback function
                        lpWCX->cbClsExtra = lpXWnd->cbClsExtra;                              // extra class memory
                        lpWCX->cbWndExtra = lpXWnd->cbWndExtra+sizeof(LONG_PTR);// extra window memory
                        lpWCX->hInstance = lpXWnd->hInstance;                              // handle to instance
                        lpWCX->hIcon = lpXWnd->hIcon;                                          // app. icon
                        lpWCX->hIconSm = lpXWnd->hIconSm;                                    // small class icon
                        lpWCX->hCursor = lpXWnd->hCursor;                                    // cursor
                        lpWCX->hbrBackground = lpXWnd->hbrBackground;                  // background brush
                        lpWCX->lpszMenuName = lpXWnd->lpszMenuName;                        // name of menu resource
                        lpWCX->lpszClassName = lpXWnd->lpszClassName;                  // name of window class

                        // Register the window class.
                        if(!RegisterClassEx(lpWCX))
                        {
                              if(lpWCX) // delete our WNDCLASSEX object
                              {
                                    delete(lpWCX);
                                    lpWCX = NULL;
                              }

                              SetLastError(XW_ERR_REGISTER_CLASS); // Set our error (Error registering class)
                              return NULL; // Return a null handle
                        }
                  }
                  else // Something has not been set right we need to return an error
                  {
                        if(lpWCX) // delete our WNDCLASSEX object
                        {
                              delete(lpWCX);
                              lpWCX = NULL;
                        }

                        // If this is true then GetClassInfoEx found this window class already in existence
                        if (lpXWnd->dwFlags & XWI_REGISTER_CLASS)
                        {
                              SetLastError(XW_ERR_CLASS_EXISTS); // Set our error (Class already exists)
                              return NULL; // Return a null handle
                        }
                        else // If not then the class does not exist and has not been created either
                        {
                              SetLastError(XW_ERR_CLASS_NOT_FOUND); // Set our error (Class not found)
                              return NULL; // Return a null handle
                        }
                  }

                  if(lpWCX) // delete our WNDCLASSEX object
                  {
                        delete(lpWCX);
                        lpWCX = NULL;
                  }
            }

            // If we are not creating the window then return here with success
            if(!(lpXWnd->dwFlags & XWI_CREATE_XWINDOW))
            {
                  // If we get here we can assume success and set the error to XW_NO_ERROR. This is
                  // important because if we register a class only the return will be null so this is
                  // the only method of error checking when dwFlags is set to XWI_REGISTER_CLASS
                  SetLastError(XW_NO_ERROR);
                  return NULL;
            }
            
            //////////////////////////////////////////////////////////////////////
            // Window Creation
            //////////////////////////////////////////////////////////////////////
            // Add the pointer to this XWindow object to the map
            g_PtrMap.Add(this);

            // And now create our window
            HWND hWnd = CreateWindowEx(
                              lpXWnd->dwExStyle,
                              lpXWnd->lpszClassName,
                              lpXWnd->lpWindowName,
                              lpXWnd->dwStyle,
                              lpXWnd->xPosition,
                              lpXWnd->yPosition,
                              lpXWnd->nWidth,
                              lpXWnd->nHeight,
                              lpXWnd->hWndParent,
                              lpXWnd->hMenu,
                              lpXWnd->hInstance,
                              lpXWnd->lpParam);

            // If we have a valid handle then we have successfuly created our window
            if(hWnd)
            {
                  SetLastError(XW_NO_ERROR); // Specify that no error was found
                  return hWnd; // Return our handle to the newly created window
            }
            else // If we dont have a valid handle then something has gone wrong
            {
                  SetLastError(XW_ERR_CREATE_WINDOW); // Specify that there was an error
                  return NULL; // Return a null handle
            }
      }

      // Static message handler used in WNDCLASSEX structure. This is called the first time the
      // window is created and sets the window user data property with our pointer to the
      // XWindow_T object. It then resets the static callback function to our real message router
      // and calls that function to process this first message also. This is how we set the
      // object pointer before any messages are processed.
      static LRESULT CALLBACK stSetPointer_T(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
      {
            XWindow_T<T> FAR* lpXWnd = (XWindow_T<T> FAR*) g_PtrMap.Extract();
            if(lpXWnd) // Only proceed if we retrieved a valid XWindow pointer
            {
                  // MSDN says the valid range is up to the size of cbWndExtra minus the size of one
                  // integer value. If we try implement this we get a access violation so I say stuff
                  // it. We place our pointer at the end so that the preceding space is available for
                  // other applications that specify a cbWndExtra value. This is used for certain
                  // dialog procedures amongst other things. This method means we also leave the
                  // GWL_USERDATA blank for use elsewhere in the program.
                  DWORD dwOffset = GetClassLongPtr(hWnd,GCL_CBWNDEXTRA) - (sizeof(LONG_PTR));

                  // This warning complains about possible loss of data converting a LONG_PTR to
                  // a LONG value. We can ignore this because a 32 bit system will indeed use a
                  // LONG pointer. This warning should not occur if compiled on a 64 bit system.
                  #pragma warning(disable : 4244)
                  SetWindowLongPtr(hWnd, dwOffset, (LONG_PTR) lpXWnd);
                  SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR) XWindow_T<T>::stMsgRouter_T);
                  #pragma warning(default : 4244)

                  return stMsgRouter_T(hWnd, uMsg, wParam, lParam);
            }
            
            // Return a failure message as we could not retrieve our object ID from the map.
            // There seems to be some debate as to which message is sent first to a window.
            // It should be WM_GETMINMAXINFO followed by WM_NCCREATE for CreateWindowEx and
            // WM_CREATE for CreateWindow (according to MSDN). Just to be safe we handle all
            // of them and return the appropriate failure message
            switch(uMsg)
            {
                  case WM_GETMINMAXINFO:
                        return 1L; // Success is 0L so all other values indicate failure

                  case WM_NCCREATE:
                        return FALSE; // Success is TRUE, failure is FALSE

                  case WM_CREATE:
                        return 1L; // 0L indicates success, 1L indicates failure
            }

            // We should never get here, but just incase return a generic value.
            return 0L;
      }

      // Our real message handler which routes the message to the appropriate objects window
      // handler by retrieving the XWindow_T object pointer stored in the window user data.
      // If the window object could not be found it is passed to the default window procedure.
      // This should never happen unless a normal window is created using CreateWindowEx and
      // given this function as its callback without specifying the object in GWL_USERDATA or
      // if this has been overwritten elsewhere in the program.
      static LRESULT CALLBACK stMsgRouter_T(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
      {
            // Set our offset in the extra window info. MSDN specifies that we should deduct the
            // size of one integer from the end value. This does not work! So we deduct the size
            // of our one long to put us at the beginning of our long pointer value.
            DWORD dwOffset = GetClassLongPtr(hWnd,GCL_CBWNDEXTRA) - (sizeof(LONG_PTR));

            // Retrieve our pointer
            XWindow_T<T> FAR*  lpXWnd = (XWindow_T<T> FAR*)((LONG_PTR)GetWindowLongPtr(hWnd, dwOffset));

            if (lpXWnd) // If we found our window object return its window procedure
            {
                  // If the message is not WM_NCDESTROY then just pass it and return
                  if (uMsg != WM_NCDESTROY)
                        return lpXWnd->CallMemberPtr(hWnd, uMsg, wParam, lParam);
                  else // However, if the message is WM_NCDESTROY we must also clean up the object
                  {
                        // We still process the WM_NCDESTROY and check it returns 0L (success)
                        LRESULT lRes = lpXWnd->CallMemberPtr(hWnd, uMsg, wParam, lParam);
                        if(lRes==0L || lpXWnd->DeleteAlways())
                              delete(lpXWnd); // Then delete our window object thus not leaking memory
                        return lRes; // Pass back our LRESULT.
                        // *** Note the window object is only deleted if WM_NCDESTROY is successful
                        // or the m_bAlwaysDelete flag is set (default). If the user has chosen to
                        // disable this option (possibly because they use a customised WM_NCDESTROY
                        // then the object will need to be deleted another way. ***
                  }
            }      
            else // If we did not find it let the default window handler deal with it
                  return DefWindowProc(hWnd, uMsg, wParam, lParam);
      }

      // Default destructor
      virtual ~XWindow_T() {}

protected:
      T*                        m_ptrObject; // The object that the callback function resides in
      PtrMemFnt            m_ptrMemFnt; // The address of the callback function
      
      // This flag determines whether we delete the object even if WM_NCDESTROY fails.
      BOOL                  m_bAlwaysDelete;

      // CallMemberPtr returns the result of the defined window function in our object
      virtual LRESULT CallMemberPtr(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
      { return (m_ptrObject->*m_ptrMemFnt)(hWnd, uMsg, wParam, lParam); }
};



//////////////////////////////////////////////////////////////////////
// The XWindow pointer map class
//
// This class is declared static and is instanced once globaly as
// g_PtrMap. We only need one instance of this for any/all XWindows
// we create. It stores the XWindow object pointer using the current
// thread ID as the key. It uses critical sections to ensure only one
// thread is ever reading or writing to the map at any one point in
// time and keeps the lock time to a minimum by only locking when a
// value is being set or retrieved from the map.
//
//////////////////////////////////////////////////////////////////////
static class XWinPtrMap
{
public:

      // Initialise our critical section object
      XWinPtrMap() { InitializeCriticalSection(&m_cs); }


      // Add a XWindow -> Thread ID map entry
      BOOL Add(LPXWINDOW lpXWnd)
      {
            EnterCriticalSection(&m_cs); // Lock while we modify map
            // Add our value to the map using the thread ID as the key
            try
            {
                  m_map[GetCurrentThreadId()] = lpXWnd;
            }
            catch(std::bad_alloc) // m_map could not allocate memory
            {
                  LeaveCriticalSection(&m_cs); // Unlock before exiting
                  return FALSE; // Indicate failure to the calling function
            }

            LeaveCriticalSection(&m_cs); // Unlock now that we have finished
            return TRUE;
      }


      // Retrieve the XWindow associated with the current thread ID
      LPXWINDOW Extract()
      {
            EnterCriticalSection(&m_cs); // Lock while we read map
            // Retrieve our value from the map using the thread ID
            LPXWINDOW lpXWnd = m_map[GetCurrentThreadId()];
            LeaveCriticalSection(&m_cs); // Unlock now that we have finished
            return lpXWnd; // Return the value found
      }

      // Uninitialise (delete) our critical section object
      ~XWinPtrMap() { DeleteCriticalSection(&m_cs); }

private:
      std::map<DWORD, LPXWINDOW> m_map; // Map a LPXWINDOW object to a DWORD Thread ID
      CRITICAL_SECTION m_cs; // Our critical section object used to create a lock

} g_PtrMap; // Our single static instance of the pointer map.


#pragma warning(default: 4786 4291) // Re-enable the warnings.

#endif // !defined(_XWindow_H_)
0
Comment
Question by:GPaterson
  • 4
6 Comments
 
LVL 1

Author Comment

by:GPaterson
ID: 9886226
Sorry for putting this up 3 times. EE site was being very slooow and I clicked the submit button a couple of extra times.

Anyway, on a side note. Thanks to JKR who provided assistance while I was developing this.
0
 
LVL 1

Author Comment

by:GPaterson
ID: 9886261
Correction: In the example above the CreateXWindow function should say:

CreateXWindow(this,&myClass::WndProc, lpXWndInfo);

not

CreateXWindow(this,&ScreenResX::WndProc, lpXWndInfo);

this was an error due to cutting and pasting from some other code.
0
 
LVL 1

Author Comment

by:GPaterson
ID: 10703421
Ignore the last comment. I edited the question.
0
 
LVL 1

Author Comment

by:GPaterson
ID: 11138474
It's not really the kind of thing people need to put any comment to, it's just there as a reference. Must it be deleted? Is there not a way to close it without deleting it?
0
 

Accepted Solution

by:
modulo earned 0 total points
ID: 11171303
PAQed, with points refunded (20)

modulo
Community Support Moderator
0

Featured Post

Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

Join & Write a Comment

Errors will happen. It is a fact of life for the programmer. How and when errors are detected have a great impact on quality and cost of a product. It is better to detect errors at compile time, when possible and practical. Errors that make their wa…
Article by: SunnyDark
This article's goal is to present you with an easy to use XML wrapper for C++ and also present some interesting techniques that you might use with MS C++. The reason I built this class is to ease the pain of using XML files with C++, since there is…
The viewer will learn how to user default arguments when defining functions. This method of defining functions will be contrasted with the non-default-argument of defining functions.
The viewer will be introduced to the technique of using vectors in C++. The video will cover how to define a vector, store values in the vector and retrieve data from the values stored in the vector.

747 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now