My Own C++Framework (MFC-like), i can create child window, but cant create edit box when WM_CREATE

0

I built my own c++framwork that is similar with MFC, I can create child window when WM_CREATE message was received, but I can not deal with Edit Box, I got the idea from Ivan Shcherbakov's post, I post a WM_CREATEMYWINDOW message after call OnCreate, and Create something in OnCreateMyWindow function when the message was received, and, of course, it works. but I want to know why and how to fix this problem.

my CWnd is CMyWindow, I created a CBT-hook to hold the HWND and CMyWindow* of new window, then add to a HWND-WINDOW map as WindowManager, the message loop callback function MyWndProc, got the CMyWindow* from WindowManger by hWnd parameter, then call CMyWindow's message functions OnCreate, OnSize, OnMove ..., etc. it just like the CWnd class.

CMyWindow seems work well, it can hold all messages and do something to response, but it can not create a edit box when WM_CREATE, it's so wired, because it create a new window with any style when WM_CREATE.

I built it with vs2010, win7.

the WindowManager (HWND-WINDOW map) in MyWindow.h

#define WM_CREATEMYWINDOW WM_USER + 123

//the CWindowManager is a map of HWND-CMyWindow
class CMyWindow;
//define the HWND-CMyWindow map
typedef map <HWND, CMyWindow*> CWindowMap;
typedef pair<HWND, CMyWindow*> WindowPair;
typedef map <HWND, CMyWindow*>::iterator WndIterator;
typedef pair<WndIterator, bool> IterBool;

class CWindowManager : private CWindowMap
{
private:
    CWindowManager(void);
    ~CWindowManager(void);
public:
    bool Add(CMyWindow* pwnd);  //add a window to map
    bool Remove(HWND hwnd);     //remove a window by hwnd
    void Clear();               //remove all items
    CMyWindow* Find(HWND hwnd); //find the window by hwnd

public:
    //get CWindowManager instance as singleton pattern
    static CWindowManager * GetInstance();
};

CMyWindow class is similar with CWnd of MFC

class CMyWindow
{
public:
    CMyWindow(void);
    ~CMyWindow(void);

    inline HWND GetSafeHwnd();  //get HWND
    //Create a window
    bool Create( HINSTANCE hInstance,
                TCHAR * szWindowName,
                       DWORD dwStyle,
                          RECT& rect,
                     HWND hParentWnd,
                         HMENU hMenu,
                     LPVOID lpParam);
    //add the window to WindowManager
    void Attach(HWND hwnd);
    //remove the window from WindowManager
    void Dettach();

    //process the WM_CREATE message
    virtual int OnCreate(LPCREATESTRUCT ps);
    /*
        other message function
    */
    //process the WM_CREATEMYWINDOW message
    virtual void OnCreateMyWindow();
protected:
    HWND _hwnd;
    HINSTANCE _hInst;
};

WindowManager and CBT-HOOK part in MyWindow.cpp

#include "StdAfx.h"
#include "LockEx.h"
#include "MyWindow.h"

//window class name
static TCHAR * _MY_WINDOW_CLASS_NAME_ = L"MYWINDOWCLASS";

//window-proc
LRESULT CALLBACK MyWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

///an Mutex-Lock class for keep current hwnd of new window, but you can ignore it when using single thread to crate window
static CLockEx   gLockInitWnd;
///keep current hwnd of new window
static CMyWindow * gpInitWnd = 0;
///an hook to create new window
static HHOOK ghook = 0;


///set gpInitWnd when create a new window
static void SetInitWnd(CMyWindow * pWnd)
{
    CLockEx::Scoped lock(gLockInitWnd);

    gpInitWnd = pWnd;
}

///clear gpInitWnd after new window created
static void UnsetInitWnd()
{
    CLockEx::Scoped lock(gLockInitWnd);

    gpInitWnd = 0;
}

///get the HWND of new window that is creating
static CMyWindow * GetInitPwnd()
{
    CLockEx::Scoped lock(gLockInitWnd);

    return gpInitWnd;
}

//CBT-Proc for SetWindowsHookEx
static LRESULT CALLBACK MyCBTProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    if (nCode == HCBT_CREATEWND) {
        //get the new window from gpInitWnd
        CMyWindow * pwnd = GetInitPwnd();

        if (pwnd) {
            //first time call this proc
            //add this window to WindowManager
            pwnd->Attach((HWND)wParam);
            return (0);
        } else {
            //sencond time call this proc
            CREATESTRUCT * cs = ((CBT_CREATEWND *)lParam)->lpcs;
            if (!(cs->style & WS_CHILD)) {
                //we can do something here
                return (0);
            } else {
                return (1); //error, destroy the window
                            //or, maybe, CallNextHookEx
            }
        }
    } else
        return CallNextHookEx(ghook, nCode, wParam, lParam);
}

//Create a WH_CBT Hook
static bool HookCrate()
{
   HANDLE hThread = GetCurrentThread();
   DWORD dwThreadId = GetThreadId(hThread);
   if (hThread) {
        ghook = SetWindowsHookEx(
            WH_CBT,
            MyCBTProc,    //set the CBT proc
            0,
            dwThreadId);
        if (!ghook)
            return false;
   }

   return (0);
}

//Destroy WH_CBT Hook
static void HookDestroy()
{
   if (ghook) {
        UnhookWindowsHookEx(ghook);
        ghook = 0;
   }
}

///////////////////////////////////////////////
//this is a vector for keep all CMyWindow*
CWindowManager::CWindowManager(void)
{
}

CWindowManager::~CWindowManager(void)
{
    clear();
}

//insert new window
bool CWindowManager::Add(CMyWindow* pwnd)
{
    IterBool ib = insert(WindowPair(pwnd->GetSafeHwnd(), pwnd));
    return ib.second;
}

//remove a window by hwnd
bool CWindowManager::Remove(HWND hwnd)
{
    WndIterator wi = find(hwnd);

    if (wi == end( )) {
        return false;
    } else {
        erase(wi);
        return true;
    }
}

//find a window by hwnd
CMyWindow* CWindowManager::Find(HWND hwnd)
{
    WndIterator wi = find(hwnd);

    if (wi == end( )) {
        return (0);
    } else {
        return wi->second;
    }
}

//remove all items
void CWindowManager::Clear()
{
    clear();
}

//get instance as singleton pattern.
CWindowManager * CWindowManager::GetInstance()
{
    static CWindowManager wm;
    return &wm;
}

register window class, set the WndProc to MyWIndowProc

ATOM RegisteWindowClass(HINSTANCE hInstance, TCHAR * szClassName)
{
    WNDCLASSEX wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style      = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = MyWindowProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = 0;
    wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW + 1);
    wcex.lpszMenuName   = 0;
    wcex.lpszClassName  = szClassName;
    wcex.hIconSm        = 0;    
    return RegisterClassEx(&wcex);
}

CMyWindow class, please pay attention on my trouble at WM_CREATE and WM_CREATEMYWINDOW message

CMyWindow::CMyWindow(void)
    : _hwnd(0)
    , _hInst(0)
{
}


CMyWindow::~CMyWindow(void)
{
}

inline HWND CMyWindow::GetSafeHwnd()
{
    return _hwnd;
}

//Craete a window
bool CMyWindow::Create( HINSTANCE hInstance,
                        TCHAR * szWindowName,
                        DWORD dwStyle,
                        RECT& rect,
                        HWND hParentWnd,
                        HMENU hMenu,
                        LPVOID lpParam)
{
    //get safe instance
    HINSTANCE hInst = hInstance;
    if (!hInstance)
        if (!hParentWnd)
            return false;
        else
            hInst = (HINSTANCE) GetWindowLong(hParentWnd, GWL_HINSTANCE);
    if (!hInst)
        return false;

    //register window class
    if (!RegisteWindowClass(hInst, _MY_WINDOW_CLASS_NAME_)) {
        DWORD dwErr = GetLastError();
        if (dwErr != ERROR_CLASS_ALREADY_EXISTS) //0x00000582
            return false;
    }

    //claim i am creating 
    SetInitWnd(this);
    //create CBT hook, then this window will add to WindowManager
    HookCrate();
    //create window
    HWND hwnd = CreateWindow(
                    _MY_WINDOW_CLASS_NAME_,
                    szWindowName,
                    dwStyle,
                    rect.left, rect.right, rect.right - rect.left, rect.bottom - rect.top,
                    hParentWnd,
                    hMenu,
                    hInstance,
                    lpParam);
    //destroy CBT hook
    HookDestroy();

    if (!hwnd)
        return false;

    _hwnd = hwnd;
    _hInst = hInst;

    //show window
    ShowWindow(_hwnd, SW_SHOW);
    UpdateWindow(_hwnd);
    return true;    
}

//add the this window to WindowManager
void CMyWindow::Attach(HWND hwnd)
{
    _hwnd = hwnd;
    CWindowManager::GetInstance()->Add(this);

    UnsetInitWnd();
}

//remove the this window to WindowManager
void CMyWindow::Dettach()
{
    CWindowManager::GetInstance()->Remove(_hwnd);
    _hwnd = 0;
}



int CMyWindow::OnCreate(LPCREATESTRUCT ps)
{
    return (0);
}

void CMyWindow::OnCreateMyWindow()
{
}

//the WndProc callback function
LRESULT CALLBACK MyWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    //Get the CMyWindow instance from WindowManager by hWnd
    CMyWindow * pwnd = CWindowManager::GetInstance()->Find(hWnd);
    //can not find thi window in WindowManager
    if (!pwnd) return DefWindowProc(hWnd, message, wParam, lParam);

    switch (message)
    {
    case WM_CREATE:
        {
            //perform the OnCreate function, just like MFC's OnCreate
            int r = pwnd->OnCreate(reinterpret_cast<LPCREATESTRUCT>(lParam));
            if (r)  //some error occurred, will destory the window
                return (r);
            //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            //I can not create any edit box in OnCreate function,
            //I must do it leater, when the window was created and
            //WM_CREATEMYWINDOW was received
            ::PostMessage(hWnd, WM_CREATEMYWINDOW, 0, 0);
        }
        break;
    /*
    case WM_.....
        other message
    case WM_.....
    */
    case WM_DESTROY:
        ::PostQuitMessage(0);
        break;
    case WM_CREATEMYWINDOW:
        //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        //I can not create any edit box in OnCreate function,
        //I must do it when WM_CREATEMYWINDOW was received
        pwnd->OnCreateMyWindow();
        break;
    }

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

Now, inherit the class CMyWindow to create 2 windows, first window will create the second one that with a edit box

class CMyWnd2 : public CMyWindow
{
public:
    CMyWnd2(void) : _hCmdEdit(0) {}
    ~CMyWnd2(void){}

protected:
    HWND _hCmdEdit;

    //create edit box
    virtual void OnCreateMyWindow();
};


class CMyWnd1 : public CMyWindow
{
public:
    CMyWnd1(void) {}
    ~CMyWnd1(void){}

protected:
    virtual int OnCreate(LPCREATESTRUCT ps);

    //create window2
    virtual void OnCreateMyWindow();

    CMyWnd2 _wnd2;
};

int CMyWnd1::OnCreate(LPCREATESTRUCT /*ps*/)
{
    //Can create window2, but can not crate window2's edit-boxs
    return (0);
}

//create window2 with edit box
void CMyWnd1::OnCreateMyWindow()
{
    RECT rect = {0, 0, 400, 300};

    _wnd2.Create(this->_hInst,
        0,
        WS_VISIBLE | WS_POPUP,
        rect,
        _hwnd,
        0,
        0);
}


//create edit box
void CMyWnd2::OnCreateMyWindow()
{
    RECT rect;
    GetClientRect(_hwnd, &rect);
    _hCmdEdit = CreateWindowEx(
                            WS_EX_STATICEDGE,
                            L"EDIT",
                            NULL,
                            WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL, 
                            0,
                            rect.bottom - 80,
                            rect.right - rect.left,
                            80,
                            _hwnd,
                            (HMENU) 100,
                            (HINSTANCE) GetWindowLong(_hwnd, GWL_HINSTANCE),
                            NULL);
}

Finally, WinMain function create a instance of CMyWnd1

CMyWnd1 mainwnd;
RECT rect;
rect.left = rect.top = 0, rect.right = 500, rect.bottom = 400;
mainwnd.Create(hInstance,
    L"MyWindow",
    WS_OVERLAPPEDWINDOW,
    rect,
    0,
    0,
    0);

while (GetMessage(&msg, NULL, 0, 0))
{
    if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}

Here must have something I missed, any one can help me fix this problem?

c++
frameworks
mfc
hook
asked on Stack Overflow Aug 28, 2012 by Sean • edited May 23, 2017 by Community

2 Answers

1

Please note that WM_CREATE message is sent before CreateWindow() returns. This means window handle member _hwnd (note: identifiers starting with _ and __ shall not be used, they are reserved for compiler extensions) is not yet initialized and contains 0. Therefore subsequent CreateWindow() call fails because you must pass valid window handle to create child window (that has WS_CHILD style). But it is allowed for popup windows (WS_POPUP style). That is very probably the reason why Wnd2 is created, but edit box is not.

answered on Stack Overflow Aug 28, 2012 by Rost
0

i got it, a stupid mistake occurred in CBThook callback function, i refused the creation request for a window with WS_CHILD style when this function was called by second time. it only need return a zero!! please see the error in comments. now it works! thanks Rost's hint.

static LRESULT CALLBACK MyCBTProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    if (nCode == HCBT_CREATEWND) {
        //get the new window from gpInitWnd
        CMyWindow * pwnd = GetInitPwnd();

        if (pwnd) {
            //first time call this proc
            //add this window to WindowManager
            pwnd->Attach((HWND)wParam);
            return (0);
        } else {
            //sencond time call this proc
            return (0);

            ////
            /// below was my stupid code, 
            ////

            /*CREATESTRUCT * cs = ((CBT_CREATEWND *)lParam)->lpcs;
            if (!(cs->style & WS_CHILD)) {
                //we can do something here
                return (0);
            } else {
                return (1); //error, destroy the window
                            //or, maybe, CallNextHookEx
            }*/
        }
    } else
        return CallNextHookEx(ghook, nCode, wParam, lParam);
}
answered on Stack Overflow Aug 29, 2012 by Sean • edited May 23, 2017 by Community

User contributions licensed under CC BY-SA 3.0