Click here to Skip to main content
15,886,724 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
About
I am trying to make an alternative to the MFC, using the Win32 API. Which is I am going to wrap it into classes. My first approach was not succeeded.

The Whole Code
C++
#include <Windows.h>
#include <tchar.h>

/** BASE WINDOW CLASS | AS A TEMPLATE **/
class CWnd
{
public:
	virtual void Init(HINSTANCE hInstance)	//Registers, Creates, Shows the Window
	{
		HWND         hwnd ;
		WNDCLASS     wndclass ;

		wndclass.style         = 0 ;
		wndclass.lpfnWndProc   = WndProc ;
		wndclass.cbClsExtra    = 0 ;
		wndclass.cbWndExtra    = 0 ;
		wndclass.hInstance     = hInstance ;
		wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
		wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
		wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
		wndclass.lpszMenuName  = NULL ;
		wndclass.lpszClassName = L"CWndMy32" ;

		RegisterClass (&wndclass);
     
		hwnd = CreateWindow (L"CWndMy32", TEXT ("The Hello Program"), 
                                      WS_OVERLAPPEDWINDOW,
			              CW_USEDEFAULT,CW_USEDEFAULT,
                                      CW_USEDEFAULT,CW_USEDEFAULT,
				     NULL,NULL,hInstance,
                                      (LPVOID)this) ; 
    //attach a pointer of the 
        //  class (Cwnd *) to the			                                            //window using the last parameter
                     // of CreateWindow

		ShowWindow (hwnd, SW_SHOWNORMAL) ;

		UpdateWindow (hwnd) ;
	}

protected:
	HINSTANCE hInst;
	HWND m_hwnd;
private:
	//Window Procedure for all of the windows (derived classes like the one below) created by this TEMPLATE CLASS
	static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
	{
		CWnd * _this = (CWnd *) GetWindowLong(hwnd, GWL_USERDATA);
		if(msg == WM_NCCREATE)
		{
			_this = new CWnd;
			_this->hInst = GetModuleHandle(0);
			_this->m_hwnd = hwnd;
			SetWindowLong(hwnd, GWL_USERDATA, (LONG) _this);
			return TRUE;
		}

		return _this->VirtualProc(hwnd, msg, wParam, lParam);
	}

	//Virtual Procedure Function to override in derived classes
	virtual LRESULT VirtualProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM        
        lParam)
	{
		return DefWindowProc(hwnd, msg, wParam, lParam);
	}
};

/** A CLASS FOR USING THE TEMPLATE CLASS | WHICH IS CWnd **/
class CMyWnd : public CWnd
{
private:
	//This virtual function Overides the 'VirtualProc' function of the 'CWnd' Class
	LRESULT VirtualProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
	{
		if(msg == WM_LBUTTONDOWN)
			MessageBox(0, 0, 0, 0);	//To Test if it words, if so, an empty message box will appear when the window is clicked
		return DefWindowProc(hwnd, msg, wParam, lParam);
	}
};

/** Windows Entry **/
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int nCmdShow)
{
	CMyWnd wnd;				//Object of the Template Used Class | CMyWnd : public CWnd
	wnd.Init(hInstance);

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

	return msg.wParam;
}


What I Did
1. Created a Base Class Called 'CWnd' (also I call it the TEMPLATE CLASS)
2. In CWnd::Init ;Registered, Created and Showed the window.
(In the CreateWindow call, I Attached a pointer of the CWnd to
the window, So that every derived class of CWnd will have different data,
but a same static window procedure )
3. CWnd::WndProc is the Static window procedure.
4. CWnd::VirtualProc is the Virtual window procedure which is overridden in derived
classes.
5. Created a Derived class using TEMPLATE CLASS, called 'CMyWnd'.
6. CMyWnd::VirtualProc is the function that overrode CWnd::VirtualProc.

The Problem
Code can be compiled with no errors, and the window is created nicely. But the problem is that, CWnd::VirtualProc virtual function is not correctly overrode with
CMyWnd::VirtualProc .

Any Help ?? Please ?
Posted

It looks like your WM_NCCREATE handler is creating a new instance of the parent class, which is used for all subsequent messages.
 
Share this answer
 
Comments
pasztorpisti 20-Dec-12 14:41pm    
+5, hey, caught the same bug! its also leaking... :-)
Your problem is the following line in your static CWnd::WndProc():
C++
_this = new CWnd;

See the corrected code below... And there are some other minor issues too.
To avoid such bugs make the CWnd class abstract. This way creating CWnd directly causes compile error. You can do this for example by creating an empty constructor for it with protected visibility!

Something that seems strange to me is that your virtual function is private in the base class, you should put it to at least protected. In the derived class its a good practice to mark the overridden method with virtual even if its not required because its a good documentation for people who read the code, and visual C++ has a very nice 'override' keyword that you can write after the function declaration (in place, or after the 'const' keyword) and it causes the compilation to fail if the function doesn't actually override a base class method (for example because someone has changed the base class method signature during refactor...). So the override keyword in msvc is a nice way to test for your problem as well!

A few other notes regarding your code:
The hooking via WM_NCCREATE is very nice, it is the correct way to do the hooking despite the fact that I see it rarely. In your implementation WM_NCCREATE isn't passed to the virtual wndproc but that is an implementation detail, WM_NCCREATE is rarely needed inside the class, usually WM_CREATE is enough. If you consider going to 64 bit then you should use SetWindowLongPtr with GWLP_USERDATA instead of SetWindowLong+GWL_USERDATA. The Virtual wndproc usually contains a big switch on the window message and I usually like writing it this way:
C++
//-------- In the derived class:
    struct SWndMsg
    {
        HWND hwnd;     // hwnd is optional because the m_hwnd member is always available
        UINT msg;
        WPARAM wParam;
        LPARAM lParam;
    }

    static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
        if(msg == WM_NCCREATE)
        {
            CWnd * _this = (CWnd*)((CREATESTRUCT*)lParam)->lpCreateParams; // !!!!!!!!!!! your bug
            _this->hInst = GetModuleHandle(0);
            _this->m_hwnd = hwnd;
            SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)_this);
        }

        CWnd * _this = (CWnd *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
        SWndMsg m;
        m.hwnd = hwnd;
        m.msg = msg;
        m.wParam = wParam;
        m.lParam = lParam;
        return _this->VirtualProc(m);
    }

    // A method in the base class that you can use with the SWndMsg struct
    // instead of DefWindowProc()
    LRESULT Default(SWndMsg& msg)
    {
        return DefWindowProc(msg.hwnd, msg.msg, msg.wParam, msg.lParam);
    }

//-------- In the derived class:
    // Here comes the 'override' keyword...
    virtual LRESULT VirtualProc(SWndMsg& msg) override
    {
        switch (msg)
        {
        case WM_LBUTTONDOWN:
            // Its easier to split the code into multiple functions if you
            // pass just 1 parameter instead of 4 and it looks nicer...
            return OnWMLButtonDown(msg);
        case WM_COMMAND:
            return OnWMCommand(msg);
        ...
        default:
            return Default(msg);
        }
    }

    void OnWMLButtonDown(SWndMsg& msg)
    {
        if (blahblah)
        {
            ...
        }
        else
        {
            // The Default() method can come handy in a lot of message specific methods...
            return Default(msg);
        }
    }

The code might contain bugs as I wrote it in the browser and havent compiled it!
 
Share this answer
 
v2
Just take the Tutorial below ! Nice One !
http://www.infernodevelopment.com/c-win32-api-simple-gui-wrapper[^]
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900