Click here to Skip to main content
15,886,578 members
Articles / Programming Languages / C++

Writing Win32 Apps with C++ only classes (part 3)

Rate me:
Please Sign up or sign in to vote.
4.76/5 (14 votes)
20 Jun 2004CPOL26 min read 68.9K   979   56  
C++ classes and wrappers to write W32 apps without MFC, ATL or other (part 3).
//wnd.cpp
#include "stdafx.h"
#include "Wnd.h"
#include "MsgLoop.h"
#include "WindowLongPtr.h"

namespace GE_{ namespace NWin{

	///////
	// SWndClassExReg

	// SWndClassEX constructors:
	// initialize WNDCLASSEX with parameters and register the class

	GE_INLINE SWndClassExReg::SWndClassExReg(
		bool bAutoUnregister,
		HINSTANCE hInst,
		LPCTSTR clsName,
		LPCTSTR resID, 
		int nBkgSysColorIdx, 
		WNDPROC pfnDefWndProc, 
		DWORD dwStyle 
		)
	{
		WNDCLASSEX wc;
		wc.cbSize = sizeof(WNDCLASSEX);
		wc.style = dwStyle;
		wc.lpfnWndProc = pfnDefWndProc;
		wc.cbClsExtra = wc.cbWndExtra = 0;
		wc.hInstance = hInst;
		wc.hIcon = (HICON)LoadImage(hInst, resID, IMAGE_ICON, 0,0, LR_DEFAULTSIZE);
		wc.hIconSm = (HICON)LoadImage(hInst, resID, IMAGE_ICON, 16,16, LR_DEFAULTCOLOR);
		wc.hCursor = LoadCursor(hInst, resID); if(!wc.hCursor) wc.hCursor = LoadCursor(NULL, IDC_ARROW);
		wc.hbrBackground = (nBkgSysColorIdx==-1)? NULL: GetSysColorBrush(nBkgSysColorIdx);
		wc.lpszMenuName = NULL;
		wc.lpszClassName = clsName;

		SetOwnership(bAutoUnregister);
		STRACE(trc,3,("Registering class %s\n", clsName));
		Attach(RegisterClassEx(&wc));
		_hInst = hInst;
	}

	GE_INLINE SWndClassExReg::SWndClassExReg(
		bool bAutoUnregister,
		HINSTANCE hInst,
		LPCTSTR clsName,			//the name for the class (will autogenerate)
		HICON HIcon,	//IDI_APPLICATION
		HCURSOR HCursor, //IDC_CURSOR
		int nBkgSysColorIdx,	// the background brush index: -1 means no backround
		WNDPROC pfnDefWndProc,// the window procedure to subclass: DefWndProc , DefDialogProc etc... 
		DWORD dwStyle //class style
		)
	{
		WNDCLASSEX wc;
		wc.cbSize = sizeof(WNDCLASSEX);
		wc.style = dwStyle;
		wc.lpfnWndProc = pfnDefWndProc;
		wc.cbClsExtra = wc.cbWndExtra = 0;
		wc.hInstance = hInst;
		wc.hIcon = (HIcon==(HICON)-1)? LoadIcon(NULL, IDI_APPLICATION): HIcon;
		wc.hIconSm = NULL;
		wc.hCursor = (HCursor==(HCURSOR)-1)? LoadCursor(NULL, IDC_ARROW): HCursor;
		wc.hbrBackground = (nBkgSysColorIdx==-1)? NULL: GetSysColorBrush(nBkgSysColorIdx);
		wc.lpszMenuName = NULL;
		wc.lpszClassName = clsName;

		SetOwnership(bAutoUnregister);
		Attach(RegisterClassEx(&wc));
		_hInst = hInst;
		STRACE(trc,3,("Registering class %s with atom %p\n", clsName, _hnd));
	}

	GE_INLINE void SWndClassExReg::Destroy(ATOM sh)
	{
		STRACE(trc,3,("Unregistering windowclass with atom %p\n", sh));
		UnregisterClass((LPCTSTR)sh, _hInst);
	} 	

		
	///////////////////////////////////////////////////////////
	//// EWnd
	//

	namespace {

		GE_INLINE LRESULT SubWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
		{
			XWndProcParams x;
			x.hWnd = hWnd; x.uMsg = uMsg; x.wParam = wParam; x.lParam = lParam; x.lResult = 0;
			x.pDispatcher = NULL; x.bHandled = false;
			STRACE(trc,0,("Dispatching Msg %p to HWND %p\n", uMsg, hWnd));
			EWnd::iterator i,last;
			EWnd::get_range(i,last,hWnd);
			EWnd::Act(&x, i, last);
			return x.lResult;
		}

	}

	GE_INLINE void EWnd::OnFirstAttach(TRefCount* pRC)
	{
		pRC->_superWndProc = (WNDPROC) SetWindowLongPtr(_hnd, GWLP_WNDPROC, (LONG_PTR)SubWndProc);
	}

	GE_INLINE void EWnd::OnLastRelease(TRefCount* pRC)
	{
		SetWindowLongPtr(_hnd, GWLP_WNDPROC, (LONG_PTR)pRC->_superWndProc);
	}


	GE_INLINE bool EWnd::Default()
	{
		bool bRet = TChainedAction::Default();  //iterate calling OnAction
		TChainedAction::XData& d = *(_pdata().Data()); //fetch the data
		XWndProcParams& z = **d._pA;
		XRefChainWnd* pRC = Tmap::Find(Key(z.hWnd));
		if(!bRet && pRC) //not yet processed by any valid wrapper
		{
			bRet = true; //just call the original windowprocedure
			z.lResult = CallWindowProc(pRC->_superWndProc,z.hWnd,z.uMsg,z.wParam,z.lParam);
		}
		if(z.uMsg == WM_NCDESTROY && pRC)  //special case: destroying the wrapped window
		{
			pRC->setValid(false);	//wrapper no more valid
			try{	//if the we are the mainwindow of the main loop (if any ...)
				if(SMsgLoop::GetGlobal().HWnd() == z.hWnd)
					PostQuitMessage(0);	//quit the loop (and the application ... probably !)
			}
			catch(...) {} //if here, no global messageloop exist
		}
		if(!d._nRecursions && (!pRC || !pRC->isValid())) //last return from a no more existent HWND
			Tmap::Unmap(Key(z.hWnd));	//clear the stored data
		return bRet;
	};

	//owner called "destroy" policy
	GE_INLINE void EWnd::Destroy(HWND& hWnd)
	{
		if(hWnd && IsWindow(hWnd))
		{
			STRACE(trc,3,("Destroying Window ...\n"));
			DestroyWindow(hWnd);
		}
	}

	//EChainedAction implementation
	GE_INLINE bool EWnd::OnAction(const LPWndProcParams& a)
	{
		bool bHasOwn = 0!=_pRC->owners();

		//go through the message map (if any)
		NUtil::STmpAssign<IMessageDispatch*> _olddispatcher(a->pDispatcher,this);
		if(!a->bHandled)
			a->bHandled = DispatchWindowMessage(0,*a);

		if(!bHasOwn && a->uMsg == WM_NCDESTROY && HasAutodelete()) //if we are an autodelete wrapper (on heap)
			delete this;

		return a->bHandled;
	}


	namespace {

		//when creating a window before entering a loop, we need to dispatch also creation messages.
		// this hook is installed and remove by the following "CreateWnd" before and after calling the W32 API
		// it attaches to the discovered HWND the wrapper that caled "CreateWnd"
		
		GE_ONCE EWnd* g_pCreating = NULL;
		GE_ONCE HHOOK g_hHook = NULL;
		
		GE_INLINE LRESULT CALLBACK CallWndProc(
			int nCode,      // hook code
			WPARAM wParam,  // current-process flag
			LPARAM lParam   // message data
			)
		{
			if(nCode == HC_ACTION)
			{
				CWPSTRUCT& ps = *(CWPSTRUCT*)lParam;
				if(ps.message == WM_NCCREATE) //if a window is in creation
				{
					if(g_pCreating)
						g_pCreating->Attach(ps.hwnd); //subclass it !
					g_pCreating = NULL;

					LRESULT ret = CallNextHookEx(g_hHook, nCode, wParam, lParam);
					UnhookWindowsHookEx(g_hHook);
					g_hHook = NULL;
					STRACE(trc, 3, ("Unhooked creation hook\n"))
					return ret;
				}
			}
			return CallNextHookEx(g_hHook, nCode, wParam, lParam); //default action (window procedure ... an consequent dispatching)
		}


	}
	
	GE_INLINE HWND EWnd::CreateWnd(
		HINSTANCE hInst,
		LPCTSTR clsName,
		LPCTSTR wndName,
		DWORD dwStyle,
		LPCRECT lprcPlace,
		HWND hwndParent,
		HMENU hMenu,
		LPVOID lpParams,
		bool bAutodelete,
		bool bOwner
		)
	{	return CreateWndEx(hInst,clsName,wndName, dwStyle, 0, lprcPlace,hwndParent,hMenu,lpParams,bAutodelete,bOwner);	}

	GE_INLINE HWND EWnd::CreateWndEx(
			HINSTANCE hInst,
			LPCTSTR clsName,
			LPCTSTR wndName,
			DWORD dwStyle,
			DWORD dwExStyle,
			LPCRECT lprcPlace,
			HWND hwndParent,
			HMENU hMenu,
			LPVOID lpParams,
			bool bAutodelete,
			bool bOwner
			)
	{
		Autodelete(bAutodelete);
		SetOwnership(bOwner);

		if(!hInst) hInst = HInstance();
		else (*(HINSTANCE*)&HInstance()) = hInst;

		int x,y,w,h;
		x = lprcPlace->left; y = lprcPlace->top;
		w = lprcPlace->right-x; h = lprcPlace->bottom-y;

		//before creating the window, we must hook the wndproc, in order to sublass it
		//otherwise no creation message can go to the message map.
		g_pCreating = this;
		
		STRACE(trc,3,("Creating Window ...\n"));
		
		bAutodelete = HasAutodelete(); Autodelete(false);
		g_hHook = SetWindowsHookEx(WH_CALLWNDPROC, CallWndProc, NULL, GetCurrentThreadId());
		HWND hWnd = CreateWindowEx(dwExStyle, clsName,wndName,dwStyle,x,y,w,h,hwndParent, hMenu, hInst, lpParams);
		Autodelete(bAutodelete);
		
		if(!hWnd)
		{
			if(HasAutodelete())
				delete this;
			return NULL;
		}
		
		ASSERT(hWnd == Value()); 

		return hWnd;    //it worked !
	}

	namespace {
		
		GE_INLINE INT_PTR CALLBACK NWinDlgProc(
		HWND hwndDlg,  // handle to dialog box
		UINT uMsg,     // message
		WPARAM wParam, // first message parameter
		LPARAM lParam  // second message parameter
		)
		{
			if(uMsg == WM_COMMAND && 
				LOWORD(wParam) >= IDOK && 
				LOWORD(wParam) <= IDCLOSE )
			{
				EndDialog(hwndDlg, LOWORD(wParam));
				return true;
			}
			return false;
		}
	}

	GE_INLINE INT_PTR EWnd::CreateModalDialog(
		HINSTANCE hInst,
		LPCTSTR nIDDlgTemplate,
		HWND hParent,
		LPARAM lParam,
		bool bAutodelete,
		bool bOwner
		)
	{
		Autodelete(bAutodelete);
		SetOwnership(bOwner);

		if(!hInst) hInst = HInstance();
		else (*(HINSTANCE*)&HInstance()) = hInst;

		g_pCreating = this;
		STRACE(trc,3,("Creating Dialog ...\n"));
		Autodelete(false); //dosn't make sense, autodelete = true for modal dialogs  ...
		g_hHook = SetWindowsHookEx(WH_CALLWNDPROC, CallWndProc, NULL, GetCurrentThreadId());
		return DialogBoxParam(hInst, nIDDlgTemplate, hParent, NWinDlgProc, lParam);
	}


	GE_INLINE LRESULT EWnd::ForwardNotifications(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		LRESULT lResult = 0;

		switch(uMsg)
		{
			case WM_COMMAND:
			case WM_NOTIFY:
			case WM_PARENTNOTIFY:
			case WM_DRAWITEM:
			case WM_MEASUREITEM:
			case WM_COMPAREITEM:
			case WM_DELETEITEM:
			case WM_VKEYTOITEM:
			case WM_CHARTOITEM:
			case WM_HSCROLL:
			case WM_VSCROLL:
			case WM_CTLCOLORBTN:
			case WM_CTLCOLORDLG:
			case WM_CTLCOLOREDIT:
			case WM_CTLCOLORLISTBOX:
			case WM_CTLCOLORMSGBOX:
			case WM_CTLCOLORSCROLLBAR:
			case WM_CTLCOLORSTATIC:
				lResult = SendMessage(GetParent(Value()), uMsg, wParam, lParam);
				break;
			default:
				bHandled = FALSE;
		}

		return lResult;
	}


	GE_INLINE LRESULT EWnd::ReflectNotifications(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		HWND hWndChild = NULL;

		switch(uMsg)
		{
		case WM_COMMAND:
			if(lParam != NULL)	// not from a menu
				hWndChild = (HWND)lParam;
			break;
		case WM_NOTIFY:
			hWndChild = ((LPNMHDR)lParam)->hwndFrom;
			break;
		case WM_PARENTNOTIFY:
			switch(LOWORD(wParam))
			{
			case WM_CREATE:
			case WM_DESTROY:
				hWndChild = (HWND)lParam;
				break;
			default:
				hWndChild = GetDlgItem(Value(), HIWORD(wParam));
				break;
			}
			break;
		case WM_DRAWITEM:
			if(wParam)	// not from a menu
				hWndChild = ((LPDRAWITEMSTRUCT)lParam)->hwndItem;
			break;
		case WM_MEASUREITEM:
			if(wParam)	// not from a menu
				hWndChild = GetDlgItem(Value(), ((LPMEASUREITEMSTRUCT)lParam)->CtlID);
			break;
		case WM_COMPAREITEM:
			if(wParam)	// not from a menu
				hWndChild = GetDlgItem(Value(), ((LPCOMPAREITEMSTRUCT)lParam)->CtlID);
			break;
		case WM_DELETEITEM:
			if(wParam)	// not from a menu
				hWndChild = GetDlgItem(Value(), ((LPDELETEITEMSTRUCT)lParam)->CtlID);
			break;
		case WM_VKEYTOITEM:
		case WM_CHARTOITEM:
		case WM_HSCROLL:
		case WM_VSCROLL:
			hWndChild = (HWND)lParam;
			break;
		case WM_CTLCOLORBTN:
		case WM_CTLCOLORDLG:
		case WM_CTLCOLOREDIT:
		case WM_CTLCOLORLISTBOX:
		case WM_CTLCOLORMSGBOX:
		case WM_CTLCOLORSCROLLBAR:
		case WM_CTLCOLORSTATIC:
			hWndChild = (HWND)lParam;
			break;
		default:
			break;
		}

		if(hWndChild == NULL)
		{
			bHandled = FALSE;
			return 1;
		}

		ASSERT(IsWindow(hWndChild));
		return SendMessage(hWndChild, OCM__BASE + uMsg, wParam, lParam);
	}

	GE_INLINE BOOL EWnd::DefaultReflectionHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult)
	{
		switch(uMsg)
		{
		case OCM_COMMAND:
		case OCM_NOTIFY:
		case OCM_PARENTNOTIFY:
		case OCM_DRAWITEM:
		case OCM_MEASUREITEM:
		case OCM_COMPAREITEM:
		case OCM_DELETEITEM:
		case OCM_VKEYTOITEM:
		case OCM_CHARTOITEM:
		case OCM_HSCROLL:
		case OCM_VSCROLL:
		case OCM_CTLCOLORBTN:
		case OCM_CTLCOLORDLG:
		case OCM_CTLCOLOREDIT:
		case OCM_CTLCOLORLISTBOX:
		case OCM_CTLCOLORMSGBOX:
		case OCM_CTLCOLORSCROLLBAR:
		case OCM_CTLCOLORSTATIC:
			lResult = ::DefWindowProc(hWnd, uMsg - OCM__BASE, wParam, lParam);
			return TRUE;
		default:
			break;
		}
		return FALSE;
	}

}}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

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


Written By
Architect
Italy Italy
Born and living in Milan (Italy), I'm an engineer in electronics actually working in the ICT department of an important oil/gas & energy company as responsible for planning and engineering of ICT infrastructures.
Interested in programming since the '70s, today I still define architectures for the ICT, deploying dedicated specific client application for engineering purposes, working with C++, MFC, STL, and recently also C# and D.

Comments and Discussions