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; } }