Click here to Skip to main content
15,881,044 members
Articles / Programming Languages / C++

Writing Win32 Apps with C++: V2 - part 1

Rate me:
Please Sign up or sign in to vote.
4.70/5 (34 votes)
20 Jun 2005CPOL14 min read 107.6K   1.2K   73  
An independent framework to handle Win32 objects inside C++ classes.
//wndw.h
//	wrapping a window
#pragma once


namespace GE_{ namespace win{

	struct windowmessage
	{
		HWND hWnd;
		UINT uMsg;
		WPARAM wParam;
		LPARAM lParam;
		LRESULT lResult;
		
		windowmessage(HWND _hWnd, UINT _uMsg, WPARAM _wParam, LPARAM _lParam)
		{hWnd = _hWnd; uMsg = _uMsg; wParam = _wParam; lParam = _lParam; lResult = 0;}

		NMHDR& nmhdr() { return *(NMHDR*)lParam; }
		WORD lParamLo() { return LOWORD(lParam); }
		WORD lParamHi() { return HIWORD(lParam); }
		WORD wParamLo() { return LOWORD(wParam); }
		WORD wParamHi() { return HIWORD(wParam); }
	};


	class message_handler:
		public stdx1::event_handler<windowmessage*>
	{
	public:
		typedef stdx1::event_source::state* nexter;
		// yuu can shortcut a call as _nexter->do_next();

        virtual bool on_message(windowmessage* _pmsg, UINT _mapID, nexter& _nexter)
		{	return false;	} 

		virtual bool on_event(windowmessage* const & pmsg, stdx1::event_source::state* pState)
		{	return on_message(pmsg, 0, pState);  }
	};


    //########   CLASS _window.   #######
	//	normalym you'll deriv ite and pass your class to stdx1::Handle
	//	i.e.:	class yourwindow: public _window
	//			{
	//				virtual bool on_event(windowmessage* const & pmsg, stdx1::event_source::state* pState);
	//			};
	//			typedef stdx1::Handle<yourwindow> YourWindow;
	class _window:
		public stdx1::HandableRefcounter<stdx1::RefcountableValue<HWND> >,
		public message_handler
	{
	protected:
		struct data:
			public stdx1::event_handler<windowmessage*>
		{
			stdx1::event_source event;
			WNDPROC prev_wndproc;
			virtual bool on_event(windowmessage* const & pmsg, stdx1::event_source::state* pState)
			{
				pmsg->lResult = CallWindowProc(prev_wndproc, pmsg->hWnd, pmsg->uMsg, pmsg->wParam, pmsg->lParam);
				return true;
			}
		};


		typedef std::map<HWND, data> datamap_type;
		static datamap_type& datamap() 
		{ 	static stdx1::Global<datamap_type>::ref g_r = stdx1::Global<datamap_type>::get(); return g_r();	}

#pragma warning(push)
#pragma warning(disable: 4244)
//NOTE review the type casts for W64 ... 
		static void reclass(HWND hWnd)
		{
			data& d = datamap()[hWnd];
			SetWindowLongPtr(hWnd, GWL_WNDPROC, (LONG_PTR)d.prev_wndproc);
			datamap().erase(hWnd);
		}
#pragma warning(pop)

		//the window procedure used to sublclass every window
		static LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
		{
			windowmessage msg(hWnd,uMsg,wParam,lParam); //remaps function parameters
			stdx1::event_source& evt = datamap()[hWnd].event;
			evt.fire(&msg); //fire to the specific HWND handles
			if(msg.uMsg == WM_NCDESTROY)
			{
				reclass(hWnd);
				if (hWnd == hMainWnd())
					PostQuitMessage(0);
			}
			return msg.lResult;
		}

#pragma warning(push)
#pragma warning(disable: 4312 4244)
//NOTE review the type casts for W64 ... 
		static void subclass(HWND hWnd)
		{
			data& d = datamap()[hWnd];
			d.prev_wndproc = (WNDPROC) SetWindowLongPtr(hWnd, GWL_WNDPROC, (LONG_PTR)_window::WindowProc);
			d.Hook(d.event); //autohook
		}
#pragma warning(pop)


		virtual void on_firstref()
		{
			//we are the firse referring hook: subclass the widow and place the default evenbt handler
			subclass(*this);
		}

		virtual void on_addref()
		{
			data& d = datamap()[*this];
			Hook(d.event);	
		}

		virtual void on_release()
		{
			if(!!*this)
			{
				data& d = datamap()[*this];
				Unhook(d.event);
			}
		}


		//creation hook mamagement
		struct createdata
		{
			HHOOK hhook;
			_window* creating;
			CRITICAL_SECTION cs;
			
			createdata() { hhook = NULL; creating = NULL; InitializeCriticalSection(&cs); }
		};
		static createdata& _createdta() { static createdata d; return d; }


		static LRESULT CALLBACK WinhookCallWndProc(
			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
				{
					_window* crt = _createdta().creating;
					HHOOK hhook = _createdta().hhook;
					STRACE(trc, 3, ("Processing creation hook %p\n", hhook))
					if(crt && !*crt) 
					{
						if(!hMainWnd()) hMainWnd() = ps.hwnd;
						crt->_set(ps.hwnd); //subclass it !
					}
					LRESULT ret = 0;
					SRETRACE(trc, ("Unhooking creation hook %p\n", hhook));
					LeaveCriticalSection(&_createdta().cs);
					if(hhook)
					{
						LRESULT ret = CallNextHookEx(hhook, nCode, wParam, lParam);
						UnhookWindowsHookEx(hhook);
					}
					return ret;
				}
			}
			return CallNextHookEx(_createdta().hhook, nCode, wParam, lParam); //default action (window procedure ... an consequent dispatching)
		}
		
	protected:

		//OVERLOAD THIS FUNCTION TO DO MESSAGE CRACKING
        virtual bool on_message(windowmessage* _pmsg, UINT _mapID, nexter& _nexter)
		{	return false;	} 

		virtual bool on_event(windowmessage* const & pmsg, stdx1::event_source::state* pState)
		{	
			bool b = on_message(pmsg, 0, pState);  
			if(pmsg->uMsg == WM_NCDESTROY && pmsg->hWnd == *this)
				_clear(); //detach the wrapper
			return b;
		}

	public:
		bool SetCreateHook()
		{
			EnterCriticalSection(&_createdta().cs);
			//only one tread here, until creation is completed
			_createdta().creating = this;
			_createdta().hhook = SetWindowsHookEx(WH_CALLWNDPROC, WinhookCallWndProc, NULL, GetCurrentThreadId());
			STRACE(trc,3,("Setted Creation hook %p\n", _createdta().hhook));
			return true;
		}

	};

	
	//############# _window specifics: ##############

	//windowholder: destroy the window if it has the last release
	class _window_holder: public _window
	{
	protected:
		virtual void on_lastrelease()
		{	PostMessage(*this, WM_CLOSE,0,0); }
	};


	//WND dynamic: for on-heap leaving hooks
	//note: uses delete_onidle: don't use it with application
	//that doesn't use win::Messageloop.
	//(or ... fire yourself the Messageloop::idle_event)
	class _window_onheap:
		public _window,
		public delete_onidle
	{
	private:
		bool bDelete;
	protected:
		_window_onheap() { bDelete = false; }

		virtual bool on_event(windowmessage* const & pmsg, stdx1::event_source::state* pState)
		{
			if(pmsg->hWnd == *this && pmsg->uMsg == WM_NCDESTROY)
			{	post_delete();	}
			return _window::on_event(pmsg, pState);
		}

	};


	//basic dialog procedure
	template<bool b_SetFocus>
	INT_PTR CALLBACK DlgProc(HWND hwnd, UINT msg, WPARAM wP, LPARAM lP)
	{
		if(msg == WM_INITDIALOG) return b_SetFocus;
		if(msg == WM_COMMAND && (wP == IDOK || wP == IDCANCEL))
		{
			EndDialog(hwnd,wP);
			return true;
		}
		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