[comp.windows.ms.programmer] Associating window handle with C++ object

ronb@burklabs (Ron Burk ) (04/16/91)

Suppose you are creating a C++ class which corresponds to a Windows 3.0
window.  Obviously, you will want to associate a Windows 3.0 window
handle with the C++ window object.  I would think the most natural way
to do this would be:

a) Allocate enough "extra" bytes to hold a pointer to "this" when you
   register your window class.

b) Using a single window function for all windows.

c) In your single window function, extract the "this" pointer and pass
   the window message on to the correct C++ window object.

Unfortunately, CreateWindow() will send messages to the window before it
returns the window handle, so the scheme just described doesn't quite
work.  In the "whello.cpp" that comes with Borland C++, they assume that
any "extra" window bytes will be initialized to zero.  When their Window
constructor calls CreateWindow(), it passes "this" in the last
parameter.  In their single window function, they first extract the
"this" pointer from the "extra" window bytes and if it is zero and the
message is WM_PAINT, they get the "this" pointer from
lParam->lpCreateParams and store it in the window "extra" bytes. 

How do other C++ classes associate the window handle with the C++ window
object? Is it safe to assume that WM_PAINT is the first message received
by any window created by CreateWindow()?

wallis@sieras.enet.dec.com (Barry L. Wallis) (04/18/91)

In article <0J7H11w163w@burklabs>, ronb@burklabs (Ron Burk       ) writes...
>Suppose you are creating a C++ class which corresponds to a Windows 3.0
>window.  Obviously, you will want to associate a Windows 3.0 window
>handle with the C++ window object.  I would think the most natural way
>to do this would be:
> 
>a) Allocate enough "extra" bytes to hold a pointer to "this" when you
>   register your window class.
> 
>b) Using a single window function for all windows.
> 
>c) In your single window function, extract the "this" pointer and pass
>   the window message on to the correct C++ window object.
> 
>Unfortunately, CreateWindow() will send messages to the window before it
>returns the window handle, so the scheme just described doesn't quite
>work.  In the "whello.cpp" that comes with Borland C++, they assume that
>any "extra" window bytes will be initialized to zero.  When their Window
>constructor calls CreateWindow(), it passes "this" in the last
>parameter.  In their single window function, they first extract the
>"this" pointer from the "extra" window bytes and if it is zero and the
>message is WM_PAINT, they get the "this" pointer from
>lParam->lpCreateParams and store it in the window "extra" bytes. 
> 
>How do other C++ classes associate the window handle with the C++ window
>object? Is it safe to assume that WM_PAINT is the first message received
>by any window created by CreateWindow()?

Being a novice Windows programmer (working my way through the Petzold book) and
having the BC++ examples and the Borland Languages Express (or some such name)
to work from I am cobbling together my own class library. I currently assume
that WM_CREATE is going to be the first Windows message I care about (can't do
much until you have created the window) and grab the pointer from
lpCreateParams there. I also have gotten rid of the switch statements from hell
in my code by encapsulating that functionality into a base class which does all
the messy stuff. I define virtual functions for all the WM_* messages, VK_*
messages, and SB_* messages. Now all I have to do is derive a class from Window
and define the routines I need to reimplement (at least wmPaint) and I have a
usable class without any distracting ugliness. I don't have many of the WM_*
calls implemented yet (I'm only in chapter 3 of Petzold), but, I have all the
VK_* and SB_* stuff done.

BTW, this code also supports multiple Windows sharing a common application. All
you do is create a new object and voila, another window goes up.

If anyone is interested, I can mail you the class code (it is quite large and
broken up over several files). However, don't expect it soon as I am on the
road next week.

---
Barry L. Wallis			USENET: wallis@labc.dec.com
Database Consultant		Prodigy (don't laugh): DNMX41A
U.S. DECtp Resource Center	DECUServe: EISNER::WALLIS (not on the net yet)
Los Angeles, CA			"No one voted for me, I represent myself"
---

davel@booboo.SanDiego.NCR.COM (David Lord) (04/19/91)

In article <0J7H11w163w@burklabs> ronb@burklabs (Ron Burk       ) writes:
>
>Unfortunately, CreateWindow() will send messages to the window before it
>returns the window handle, so the scheme just described doesn't quite
>work.  In the "whello.cpp" that comes with Borland C++, they assume that
>any "extra" window bytes will be initialized to zero.  When their Window
>constructor calls CreateWindow(), it passes "this" in the last
>parameter.  In their single window function, they first extract the
>"this" pointer from the "extra" window bytes and if it is zero and the
>message is WM_PAINT, they get the "this" pointer from
>lParam->lpCreateParams and store it in the window "extra" bytes. 
>
>How do other C++ classes associate the window handle with the C++ window
>object? Is it safe to assume that WM_PAINT is the first message received
>by any window created by CreateWindow()?

I've played with this problem quite a bit and here's some of the things
I found:

	I didn't like the tecnique used in whello.cpp. Seems to me their
	use of classes serves only to make the program more complicated
	and doesn't give coresponding benifits (reusibility, etc.)

	Ultimately I used a separate window proc for each window. One
	reason is that I didn't want to stray too far from what I would
	be seeing in every example in the reference books. The results
	also seemed much clearer (to me) than what was done in whello.cpp.

	So far I have classes:

		classWindow
		classWChild : classWindow
		classWDlgModal : classWindow
		classWDlgModless : classWindow
		classScrollBar
		classScrollBarCaptioned : classScrollBar
		   (this one scrolls a second window in sync with the one
		   containing the scroll bar.)

	There are a lot of messages that get sent to your window before
	CreateWindow returns. I tried setting the handle in WM_CREATE
	which seemed like the obvious place to me but I still had problems
	with some of the non-client messages that come before WM_CREATE.
	As far as I can tell, the best thing is to use the hWnd that
	gets passed in as a parameter any time you call DefWinProc (or
	whatever it's called) I think there were other messages that I
	was handling that also came befor WM_CREATE. I suppose another
	solution is to start out with your handle set to some invalid
	value and set it on the first message received, regardless of
	what message it is.

	I'd be interested in hearing any other approaches.

--
Dave.Lord@SanDiego.NCR.COM

robertk@lotatg.lotus.com (Robert Krajewski) (04/25/91)

In article <1991Apr18.182959.11263@SanDiego.NCR.COM> davel@booboo.SanDiego.NCR.COM (David Lord) writes:

	   I didn't like the tecnique used in whello.cpp. Seems to me their
	   use of classes serves only to make the program more complicated
	   and doesn't give coresponding benifits (reusibility, etc.)

	   Ultimately I used a separate window proc for each window.

Sorry, but this doesn't follow. The whole reason for using C++ is to
STOP using manual forms of inheritance, like Windows WNDPROCs, and
start using a unified paradigm in the language. The most compelling
reason for minimizing the proliferation of WNDPROCs is that every new window
class must be manually initialized with RegisterClass. That is
bookkeepping that is error-prone.

Instead of using extra bytes for storing a pointer in the extra window
bytes, I use a more flexible (albeit slightly less efficient) scheme.
By using properties instead of extra bytes, you don't depend on how
many extra window bytes any associated Windows window uses, which
means that you can use this association scheme for your own windows
and dialogs (which have DLGWNDEXTRA extra bytes). Since properties can
only hold a WORD's worth of information, the property value will
either have to be a NEAR pointer (if you are using medium model) or an
index into a table, each of whose elements is a pointer to the C++
window.

Assuming medium model (the work for table maintenance complicates
matters), I'd get something like this (just for illustrative
purposes):

Caveats: I've never used Borland, but this still will work with
Zortech; no special provisions for MDI, CS_HREDRAW, CS_VREDRAW, class
icons.

class Window {
	friend LONG FAR PASCAL WindowDispatcherProc(HWND, WORD, WORD, LONG);
	public:
	Window();
	// Constructor

	HWND Realize(<CreateWindow args>);
	// Make a real MSW window. Returns what CreateWindow returns

	HWND GetHandle() const { return fHandle; }

	virtual LONG HandleMessage(WORD wMsg, WORD wParam, LONG lParam);
	// Handle the message, following WNDPROC protoco.

	static Window * WindowFromHandle(HWND hWnd);

	private:
	WORD fHandle; // Real window handle
};

/* --- Implementation --- */
static const char kWindowProp[] = "Window"; // Property tag
static const char kWindowClass[] = "Window_CLASS"; // Class tag

<< need to register kWindowClass with WindowDispatcherProc >>

Window::Window() : fHandle(0) { }

Window * Window::WindowFromHandle(HWND hWnd)
{
	WORD prop = GetProp(hWnd, kWindowProp);
	return (Window *)prop;
}

HWND Window::Realize(<args>)
{
	return CreateWindow(kWindowClass, ... lParam is (LONG)this);
}

LONG Window::HandleMessage(WORD wMsg, WORD wParam, LONG lParam)
{
	return DefWindowProc(GetHandle(), wMsg, wParam, lParam);
}

LONG FAR PASCAL WindowDispatcherProc(HWND hWnd, WORD wMsg,
	WORD wParam, LONG lParam)
{
	Window * w;

	switch (wMsg) {
	case WM_NCCREATE: // just let it go
	return DefWindowProc(hWnd, wMsg, wParam, lParam);
	break;

	case WM_CREATE: // do association
	w = (Window *)((LPCREATESTRUCT)lParam)->lpCreateParams;
	SetProp(hWnd, kWindowProp, (HANDLE)w); // pointer is property
	w->fHandle = hWnd;
	// Give class a chance to handle WM_CREATE
	return w->HandleMessage(wMsg, wParam, lParam); 
	break;

	default: // Still possible that window wasn't associated
	w = WindowFromHandle(hWNd);
	if (w)
	   return w->HandleMessage(wMsg, wParam, lParam);
	else
	   return DefWindowProc(hWnd, wParam, lParam);
	break;
}
}