I'm passing a this pointer in my constructor initialization list for use by a base class and VC9 gives me this warning about it...

main.cpp(124) : warning C4355: 'this' : used in base member initializer list

The program runs perfectly as compiled by VC9 Pro or Code::Blocks 10.05. In Code::Blocks 10.05 using MinGW I get no warnings and a perfectly clean compile. I really think its OK but I wanted to run it by the experts here to see what others might think, and naturally, I might be missing something. If there is a problem I'd like to know about it.

Theoretically, the base address of any base class should be the same as the base address of a class from which it singly inherits. That's all I really need to ensure.

The context for what I'm doing is I put together a simple Windows Class Framework. I never use class frameworks myself, as I'm a pure Win32 Sdk coder, but I put one together for myself nonetheless to play with. Here is the code for the whole program. You can try it out and get the warning if you care to. Oh, I did do a search here on this problem first and found this...

http://www.daniweb.com/software-development/cpp/threads/100917

where MattEvans and Ancient Dragon had an exchange over this issue. But it doesn't look like they really resolved it. MattEvans rewrote his program just to get rid of the warning. Here is my code...

#ifndef UNICODE                     //This program creates a window upon which are displayed
   #define UNICODE                  //continuously updated mouse coordinates, left button   
#endif                              //click positions, window sizing information, and key
#ifndef _UNICODE                    //presses (concatenated together for simple text display). 
   #define _UNICODE
#endif
#include <windows.h>
#include <tchar.h>
#include <string>
#ifdef UNICODE
   #define String std::wstring
#else
   #define String std::string
#endif
#include <cstdio>


struct ProgramData                  //This object will be used to persist mouse coordinate,
{                                   //client window dimensions, and left mouse click coordinates
 short int xMouse;                  //across invocations of the Window Procedure so that the
 short int yMouse;                  //data can be displayed on the window during WM_PAINT
 short int xSize;                   //messages.  An instance of this object will be allocated
 short int ySize;                   //dynamically in WM_CREATE, and the pointer to it stored
 short int xButton;                 //as instance data in the window's WNDCLASSEX::cbWndExtra
 short int yButton;                 //bytes.
};


class CWinClass                     //Just a wrapper for registering WNDCLASSEX object
{
 public:
 CWinClass(WNDPROC fnWndProc, LPCTSTR szClassName, int cbClsExtra,  int cbWndExtra, HBRUSH hBackColor, HINSTANCE hInst)
 {
  wc.lpszClassName = szClassName;
  wc.lpfnWndProc   = fnWndProc;
  wc.style         = 0;
  wc.cbClsExtra    = cbClsExtra;
  wc.cbWndExtra    = cbWndExtra;
  wc.hInstance     = hInst;
  wc.hIcon         = 0;
  wc.hIconSm       = 0;
  wc.hCursor       = LoadCursor (0, IDC_ARROW);
  wc.hbrBackground = hBackColor;
  wc.lpszMenuName  = 0;
  wc.cbSize        = sizeof(WNDCLASSEX);
  RegisterClassEx(&wc);
 }

 private:
 WNDCLASSEX wc;
};


class CWindow               //CWindow Fully Initialized Constructor
{                           //This is a base class
 public:
 CWindow
 (
  int iShow, 
  LPCTSTR szClassName, 
  LPCTSTR szCaption, 
  DWORD dwStyle, 
  int x, 
  int y, 
  int iWidth, 
  int iHeight, 
  HWND hParent, 
  HMENU hMenu, 
  HINSTANCE hIns, 
  void* lpCreateParams
 )
 {
  this->m_hWnd=           //Win Api CreateWindowEx() call
  CreateWindowEx
  (
   0,
   szClassName,
   szCaption,
   dwStyle,
   x,
   y,
   iWidth,
   iHeight,
   hParent,
   (HMENU)hMenu,
   hIns,
   lpCreateParams
  );
  this->m_hInst=hIns;
  ShowWindow(this->m_hWnd,iShow);
  UpdateWindow(this->m_hWnd);
 }

 WPARAM Run(void)        //Wrapper for message pump
 {
  MSG msg;

  while(GetMessage(&msg,NULL,0,0))
  {
     TranslateMessage(&msg);
     DispatchMessage(&msg);
  }

  return msg.wParam;
 }

 HWND Window(void)
 {
  return this->m_hWnd;
 }

 virtual ~CWindow(void) {}

 protected:
 HINSTANCE   m_hInst;
 HWND        m_hWnd;
};


class CFrame : public CWindow         //CFrame : Inherits from CWindow
{
 public:
 CFrame
 (
  int iShow,
  LPCTSTR szClassName,
  LPCTSTR szCaption,
  DWORD dwStyle,
  int x,
  int y,
  int nWidth,
  int nHeight,
  HWND hParent,
  HMENU hMenu,
  HINSTANCE hIns,                          //Why is passing 'this' bad as last 
  void* lpCreateParams                     //param of initialization list below ...  -V-
 ):CWindow(iShow,szClassName,szCaption,dwStyle,x,y,nWidth,nHeight,hParent,hMenu,hIns,this)
 {  //1>.\main.cpp(124) : warning C4355: 'this' : used in base member initializer list
  //SetWindowLong(this->m_hWnd,0,(long)this);  //Could be done this way and just put
  this->m_nCmdShow=iShow;                      //WM_CREATE code here
 }


 static long OnCreate(HWND hWnd, LPARAM lParam)        //If I pass 'this' into base class 
 {                                                     //constructor it will be passed into
  CREATESTRUCT* pCreateStruct=(CREATESTRUCT*)lParam;   //CreateWindowEx() call as a CREATEPARAMS   
  CFrame* pObj=(CFrame*)pCreateStruct->lpCreateParams; //and be able to be retrieved here in   
  SetWindowLong(hWnd,0,(long)pObj);                    //WM_CREATE handler.  I'd prefer to do
  String* pStr;                                        //it this way, but I could change it.  I
  pStr=new String;                                     //don't know why VC++ 9 doesn't like it.
  SetWindowLong(hWnd,4,(long)pStr);
  ProgramData* pPD=(ProgramData*)GlobalAlloc(GPTR,sizeof(ProgramData));
  SetWindowLong(hWnd,8,(long)pPD);
  return 0L;
 }


 static long OnSize(HWND hWnd, LPARAM lParam)
 {
  ProgramData* pPD=(ProgramData*)GetWindowLong(hWnd,8);
  pPD->xSize=LOWORD(lParam);
  pPD->ySize=HIWORD(lParam);
  InvalidateRect(hWnd,NULL,TRUE);
  return 0L;
 }


 static long OnMouseMove(HWND hWnd, LPARAM lParam)
 {
  ProgramData* pPD=(ProgramData*)GetWindowLong(hWnd,8);
  pPD->xMouse=LOWORD(lParam);
  pPD->yMouse=HIWORD(lParam);
  InvalidateRect(hWnd,NULL,TRUE);
  return 0L;
 }


 static long OnChar(HWND hWnd, WPARAM wParam)
 {
  String* pStr;
  pStr=(String*)GetWindowLong(hWnd,4);
  *pStr+=+wParam;
  InvalidateRect(hWnd,NULL,FALSE);
  return 0;
 }


 static long OnLButtonDown(HWND hWnd, LPARAM lParam)
 {
  ProgramData* pPD=(ProgramData*)GetWindowLong(hWnd,8);
  pPD->xButton=LOWORD(lParam);
  pPD->yButton=HIWORD(lParam);
  InvalidateRect(hWnd,NULL,FALSE);
  return 0;
 }


 static long OnPaint(HWND hWnd)
 {
  PAINTSTRUCT ps;
  String s1;
  String s2;
  String* pStr;
  TCHAR szBuffer[16];
  HDC hDC;
  hDC=BeginPaint(hWnd,&ps);
  ProgramData* pPD=(ProgramData*)GetWindowLong(hWnd,8);
  s1=_T("xMouse=");
  _stprintf(szBuffer,_T("%d"),pPD->xMouse);
  s2=szBuffer;
  s1+=s2+_T("     yMouse=");
  _stprintf(szBuffer,_T("%d"),pPD->yMouse);
  s2=szBuffer;
  s1+=s2;
  TextOut(hDC,0,0,s1.c_str(),s1.length());
  if(pPD->xButton||pPD->yButton)
  {
     s1=_T("xButton=");
     _stprintf(szBuffer,_T("%d"),pPD->xButton);
     s2=szBuffer;
     s1+=s2+_T("    yButton=");
     _stprintf(szBuffer,_T("%d"),pPD->yButton);
     s2=szBuffer;
     s1+=s2;
     TextOut
     (
      hDC,
      pPD->xButton+12,
      pPD->yButton,
      s1.c_str(),
      s1.length()
     );
     pPD->xButton=0, pPD->yButton=0;
  }
  s1=_T("Width=");
  _stprintf(szBuffer,_T("%d"),pPD->xSize);
  s2=szBuffer;
  s1+=s2+_T("    Height=");
  _stprintf(szBuffer,_T("%d"),pPD->ySize);
  s2=szBuffer;
  s1+=s2;
  TextOut(hDC,0,20,s1.c_str(),s1.length());
  pStr=(String*)GetWindowLong(hWnd,4);
  TextOut(hDC,0,40,pStr->c_str(),pStr->length());
  EndPaint(hWnd,&ps);
  return 0L;
 }


 static long OnDestroy(HWND hWnd)
 {
  String* pStr;
  pStr=(String*)GetWindowLong(hWnd,4);
  delete pStr;
  ProgramData* pPD=(ProgramData*)GetWindowLong(hWnd,8);
  GlobalFree(pPD);
  PostQuitMessage(0);
  return 0L;
 }

 virtual ~CFrame(void){}

 protected:
 int             m_nCmdShow;
};


LRESULT CALLBACK FrameWndProc(HWND hWnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
 switch(msg)
 {
  case WM_CREATE:
    return CFrame::OnCreate(hWnd,lParam);
  case WM_SIZE:
    return CFrame::OnSize(hWnd,lParam);
  case WM_MOUSEMOVE:
    return CFrame::OnMouseMove(hWnd,lParam);
  case WM_CHAR:
    return CFrame::OnChar(hWnd,wParam);
  case WM_LBUTTONDOWN:
    return CFrame::OnLButtonDown(hWnd,lParam);
  case WM_PAINT:
    return CFrame::OnPaint(hWnd);
  case WM_DESTROY:
    return CFrame::OnDestroy(hWnd);
 }

 return (DefWindowProc(hWnd, msg, wParam, lParam));
}


int WINAPI WinMain(HINSTANCE hIns, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
{
 CWinClass WinClass(FrameWndProc, _T("Form2"), 0, 12, (HBRUSH)GetStockObject(WHITE_BRUSH), hIns);
 CFrame App(iShow,_T("Form2"),_T("Various Inputs"),WS_OVERLAPPEDWINDOW,200,500,295,180,0,(HMENU)0,hIns,0);
 return App.Run();
}

Recommended Answers

All 4 Replies

>>The program runs perfectly

Your program is going to run correctly. The thing about using the "this" pointer in the initialization list is not a matter that the this pointer's value (the address of the object) is not correct, it is correct. The problem is that, at that exact point in time, the this pointer points to an incomplete object (meaning some data members have not yet been initialized). So, the danger is that if the this pointer is used (dereferenced to access a data or function member) then that operation could be dangerous and execute erroneously. But, if all you do is store that pointer (in a data member of the base class or by passing it over to a windows API function as the "userdata" pointer) and guarantee that it will not actually be used until all the constructors have finished (derived and base), then your program will execute just fine and is well-formed. So, in your code, since you only call the Run() function once the CFrame object has been created, your program will and should run correctly.

>>In Code::Blocks 10.05 using MinGW I get no warnings and a perfectly clean compile.

This is probably because of a warning-level issue, but I do not remember if GCC will give such a warning, this is a bit of a fringe case.

>>If there is a problem I'd like to know about it.

There is no technical correctness problem, i.e., your program does and should work. But, there is a design problem here.

>>Theoretically, the base address of any base class should be the same as the base address of a class from which it singly inherits. That's all I really need to ensure.

Although this statement is true in practice, I don't find that it is an assumption / restriction that you should have to make. If you design to avoid having to pass down that this pointer you will also eliminate that problem.

>>Here is my code...

Your design is very awkward to say the least, I would have several improvements to suggest, but for the main issue here (passing the this to the base-class constructor), I think you simply chose an inflexible design and passing the this pointer is just a symptom of that bad design. Maybe I can suggest an alternate design that makes a bit more sense and uses polymorphism of the CWindow objects to better handle the message dispatching. Consider this architecture:

#ifndef UNICODE                     //This program creates a window upon which are displayed
   #define UNICODE                  //continuously updated mouse coordinates, left button   
#endif                              //click positions, window sizing information, and key
#ifndef _UNICODE                    //presses (concatenated together for simple text display). 
   #define _UNICODE
#endif
#include <windows.h>
#include <tchar.h>
#include <string>
#ifdef UNICODE
   #define String std::wstring
#else
   #define String std::string
#endif
#include <cstdio>

// NOTICE: 
#include <map>


struct ProgramData                  //This object will be used to persist mouse coordinate,
{                                   //client window dimensions, and left mouse click coordinates
 short int xMouse;                  //across invocations of the Window Procedure so that the
 short int yMouse;                  //data can be displayed on the window during WM_PAINT
 short int xSize;                   //messages.  An instance of this object will be allocated
 short int ySize;                   //dynamically in WM_CREATE, and the pointer to it stored
 short int xButton;                 //as instance data in the window's WNDCLASSEX::cbWndExtra
 short int yButton;                 //bytes.
};


class CWinClass                     //Just a wrapper for registering WNDCLASSEX object
{
 public:
 CWinClass(WNDPROC fnWndProc, LPCTSTR szClassName, int cbClsExtra,  int cbWndExtra, HBRUSH hBackColor, HINSTANCE hInst)
 {
  wc.lpszClassName = szClassName;
  wc.lpfnWndProc   = fnWndProc;
  wc.style         = 0;
  wc.cbClsExtra    = cbClsExtra;
  wc.cbWndExtra    = cbWndExtra;
  wc.hInstance     = hInst;
  wc.hIcon         = 0;
  wc.hIconSm       = 0;
  wc.hCursor       = LoadCursor (0, IDC_ARROW);
  wc.hbrBackground = hBackColor;
  wc.lpszMenuName  = 0;
  wc.cbSize        = sizeof(WNDCLASSEX);
  RegisterClassEx(&wc);
 }

 private:
 WNDCLASSEX wc;
};


class CWindow               //CWindow Fully Initialized Constructor
{                           //This is a base class
 private:
  CWindow(const CWindow&);
  CWindow& operator=(const CWindow&); //make this class a non-copyable class.

 public:
 CWindow
 (
  int iShow, 
  LPCTSTR szClassName, 
  LPCTSTR szCaption, 
  DWORD dwStyle, 
  int x, 
  int y, 
  int iWidth, 
  int iHeight, 
  HWND hParent, 
  HMENU hMenu, 
  HINSTANCE hIns, 
  void* lpCreateParams
 )
 {
  this->m_hWnd =           //Win Api CreateWindowEx() call
  CreateWindowEx
  (
   0,
   szClassName,
   szCaption,
   dwStyle,
   x,
   y,
   iWidth,
   iHeight,
   hParent,
   (HMENU)hMenu,
   hIns,
   lpCreateParams
  );
  this->m_hInst=hIns;

  // NOTICE:
  RegisterCWindow(this->m_hWnd, this); //associate the window handle with 'this' CWindow object.

  ShowWindow(this->m_hWnd,iShow);
  UpdateWindow(this->m_hWnd);
 }

 WPARAM Run(void)        //Wrapper for message pump
 {
  MSG msg;

  while(GetMessage(&msg,NULL,0,0))
  {
     TranslateMessage(&msg);
     DispatchMessage(&msg);
  }

  return msg.wParam;
 }

 virtual long OnCreate(LPARAM lParam) = 0;

 virtual long OnSize(LPARAM lParam) = 0;

 virtual long OnMouseMove(LPARAM lParam) = 0;

 virtual long OnChar(WPARAM wParam) = 0;

 virtual long OnLButtonDown(LPARAM lParam) = 0;

 virtual long OnPaint() = 0;

 virtual long OnDestroy() = 0;


 HWND Window(void)
 {
  return this->m_hWnd;
 }

 virtual ~CWindow(void) {
  // NOTICE:
  UnregisterCWindow(m_hWnd);
 }

 protected:
 HINSTANCE   m_hInst;
 HWND        m_hWnd;

// NOTICE:
 static std::map< HWND, CWindow* > m_hWnd_CWnd_map;

 static CWindow* GetCWindowFromHWnd( HWND a_hWnd ) {
  std::map< HWND, CWindow* >::iterator it = m_hWnd_CWnd_map.find( a_hWnd );
  if( it != m_hWnd_CWnd_map.end() )
   return it->second;
  else
   return NULL;
 };

 static void RegisterCWindow( HWND a_hWnd, CWindow* a_CWnd ) {
  m_hWnd_CWnd_map[a_hWnd] = a_CWnd;
 };

 static void UnregisterCWindow( HWND a_hWnd ) {
  std::map< HWND, CWindow* >::iterator it = m_hWnd_CWnd_map.find( a_hWnd );
  if( it != m_hWnd_CWnd_map.end() )
   m_hWnd_CWnd_map.erase(it);
 };


public:

 static LRESULT CALLBACK CWindowProc(HWND hWnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
 {
  CWindow* ptr = GetCWindowFromHWnd(hWnd);
  if( ptr == NULL )
    return (DefWindowProc(hWnd, msg, wParam, lParam));

  switch(msg)
  {
   case WM_CREATE:
     return ptr->OnCreate(lParam);
   case WM_SIZE:
     return ptr->OnSize(lParam);
   case WM_MOUSEMOVE:
     return ptr->OnMouseMove(lParam);
   case WM_CHAR:
     return ptr->OnChar(wParam);
   case WM_LBUTTONDOWN:
     return ptr->OnLButtonDown(lParam);
   case WM_PAINT:
     return ptr->OnPaint();
   case WM_DESTROY:
     return ptr->OnDestroy();
  }

  return (DefWindowProc(hWnd, msg, wParam, lParam));
 }

};


class CFrame : public CWindow         //CFrame : Inherits from CWindow
{
 public:
 CFrame
 (
  int iShow,
  LPCTSTR szClassName,
  LPCTSTR szCaption,
  DWORD dwStyle,
  int x,
  int y,
  int nWidth,
  int nHeight,
  HWND hParent,
  HMENU hMenu,
  HINSTANCE hIns,  
  void* lpCreateParams  
 ) : CWindow(iShow,szClassName,szCaption,dwStyle,x,y,nWidth,nHeight,hParent,hMenu,hIns,NULL)
 {  
  //SetWindowLong(this->m_hWnd,0,(long)this);  //Could be done this way and just put
  this->m_nCmdShow=iShow;                      //WM_CREATE code here

  m_PD = (ProgramData*) GlobalAlloc(GPTR,sizeof(ProgramData));
 }


 virtual long OnCreate(LPARAM lParam) 
 { 
  //NOTICE: None of the code below is needed anymore:

  //CREATESTRUCT* pCreateStruct=(CREATESTRUCT*)lParam;    
  //CFrame* pObj=(CFrame*)pCreateStruct->lpCreateParams;   
  
  //SetWindowLong(m_hWnd,0,(long)this); 
  //String* pStr;  
  //pStr=new String;
  //SetWindowLong(m_hWnd,4,(long)pStr);
  //ProgramData* pPD=(ProgramData*)GlobalAlloc(GPTR,sizeof(ProgramData));
  //SetWindowLong(m_hWnd,8,(long)pPD);
  return 0L;
 }


 virtual long OnSize(LPARAM lParam)
 {
  m_PD->xSize=LOWORD(lParam);
  m_PD->ySize=HIWORD(lParam);
  InvalidateRect(m_hWnd,NULL,TRUE);
  return 0L;
 }


 virtual long OnMouseMove(LPARAM lParam)
 {
  m_PD->xMouse=LOWORD(lParam);
  m_PD->yMouse=HIWORD(lParam);
  InvalidateRect(m_hWnd,NULL,TRUE);
  return 0L;
 }


 virtual long OnChar(WPARAM wParam)
 {
  m_Str += +wParam;
  InvalidateRect(m_hWnd,NULL,FALSE);
  return 0;
 }


 virtual long OnLButtonDown(LPARAM lParam)
 {
  m_PD->xButton=LOWORD(lParam);
  m_PD->yButton=HIWORD(lParam);
  InvalidateRect(m_hWnd,NULL,FALSE);
  return 0;
 }


 virtual long OnPaint()
 {
  PAINTSTRUCT ps;
  String s1;
  String s2;
  TCHAR szBuffer[16];
  HDC hDC;
  hDC = BeginPaint(m_hWnd,&ps);
  s1 = _T("xMouse=");
  _stprintf(szBuffer,_T("%d"),m_PD->xMouse);
  s2 = szBuffer;
  s1 += s2 + _T("     yMouse=");
  _stprintf(szBuffer,_T("%d"),m_PD->yMouse);
  s2 = szBuffer;
  s1 += s2;
  TextOut(hDC,0,0,s1.c_str(),s1.length());
  if(m_PD->xButton || m_PD->yButton)
  {
     s1 = _T("xButton=");
     _stprintf(szBuffer,_T("%d"),m_PD->xButton);
     s2 = szBuffer;
     s1 += s2 + _T("    yButton=");
     _stprintf(szBuffer,_T("%d"),m_PD->yButton);
     s2 = szBuffer;
     s1 += s2;
     TextOut
     (
      hDC,
      m_PD->xButton+12,
      m_PD->yButton,
      s1.c_str(),
      s1.length()
     );
     m_PD->xButton=0, m_PD->yButton=0;
  }
  s1 = _T("Width=");
  _stprintf(szBuffer,_T("%d"),m_PD->xSize);
  s2 = szBuffer;
  s1 += s2 + _T("    Height=");
  _stprintf(szBuffer,_T("%d"),m_PD->ySize);
  s2 = szBuffer;
  s1 += s2;
  TextOut(hDC,0,20,s1.c_str(),s1.length());
  TextOut(hDC,0,40,m_Str.c_str(),m_Str.length());
  EndPaint(m_hWnd,&ps);
  return 0L;
 }


 virtual long OnDestroy()
 {
  PostQuitMessage(0);
  return 0L;
 }

 virtual ~CFrame(void) { 
  GlobalFree(m_PD);
 }

 protected:
 int             m_nCmdShow;
 ProgramData*    m_PD;       //Why does this would need to be a pointer, I don't know.
 String          m_Str;
};



int WINAPI WinMain(HINSTANCE hIns, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
{
 CWinClass WinClass(CWindow::CWindowProc, _T("Form2"), 0, 12, (HBRUSH)GetStockObject(WHITE_BRUSH), hIns);
 CFrame App(iShow,_T("Form2"),_T("Various Inputs"),WS_OVERLAPPEDWINDOW,200,500,295,180,0,(HMENU)0,hIns,0);
 return App.Run();
}

Amongst other things, I don't like your use of defines for type identifers, either use a typedef or a use templates. I also don't like to see people use names with leading underscore characters, especially since it is forbidden by the standard in many situations. _T is pretty horrible. Also, you have to provide a copy-constructor (or disable it, like I did) for your classes.

I really appreciate your taking the time to examine my rather difficult and long post Mike! I'm going to study what you just provided!

Fred

Say, Mike, I'm getting a bunch of linker errors on that in CodeBlocks related to your inclusion of <map>. I'm not very familiar with STL, and was wondering what I need to link against to get it to work. Here are the errors I'm getting...

||=== FrmWrk34, Release ===|
C:\Code\CodeBlks\ClassFrameworks\FrmWrk34\.objs\main.o:main.cpp|| undefined reference to `CWindow::m_hWnd_CWnd_map'|
C:\Code\CodeBlks\ClassFrameworks\FrmWrk34\.objs\main.o:main.cpp|| undefined reference to `CWindow::m_hWnd_CWnd_map'|
C:\Code\CodeBlks\ClassFrameworks\FrmWrk34\.objs\main.o:main.cpp|| undefined reference to `CWindow::m_hWnd_CWnd_map'|
C:\Code\CodeBlks\ClassFrameworks\FrmWrk34\.objs\main.o:main.cpp|| undefined reference to `CWindow::m_hWnd_CWnd_map'|
C:\Code\CodeBlks\ClassFrameworks\FrmWrk34\.objs\main.o:main.cpp|| undefined reference to `CWindow::m_hWnd_CWnd_map'|
C:\Code\CodeBlks\ClassFrameworks\FrmWrk34\.objs\main.o:main.cpp|| more undefined references to `CWindow::m_hWnd_CWnd_map' follow|
||=== Build finished: 6 errors, 0 warnings ===|

At present I'm only linking against gdi32, user32 and kernel32. You know, maybe I ought to try with VC9 and not bother you. Maybe they link by default with more libs.

You just have to add this after your declaration of the CWindow class:

std::map< HWND, CWindow* > CWindow::m_hWnd_CWnd_map;

I just forgot to put that in the code I posted.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.