Window class

B

Bonj

i'm trying to create a class that will contain all the features of
instantiating a window and showing it. I've got the SetWindowLong /
GetWindowLong pair to succesfully store the 'this' pointer in the hWnd's
GWL_USERDATA, but the pointer that it's referring to always seems to point
to the base class and never the derived class. I don't know what I'm doing
wrong, this has been driving me mad for hours. Consequently, the static
WndProc handler function can never route the messages to the class's actual
wndproc (which is virtual and implemented in the derived class) - it gives
me some low-level debug error of 'pure virtual function call' .... WHY can't
I get it to see the derived class? help!....
Thanks

this is in window1.h (the base class)

#ifndef WINDOW1CLASS_DEFINED

#define WINDOW1CLASS_DEFINED

class Window1

{

public:

//variables:

HWND hWnd;

HCURSOR hcWait, hcArrow;

BOOL bWaiting;


//methods:

void Create( HINSTANCE hInst,

HICON hIcon,

HICON hIconSm,

HBRUSH hbrBackground,

LPCTSTR lpszCaption,

int nClassStyle,

int nWindowStyle);

static LRESULT CALLBACK WPHandler(

HWND hWnd,

UINT uMsg,

WPARAM wParam,

LPARAM lParam);

virtual LRESULT WndProc(HWND hWnd,

UINT uMsg,

WPARAM wParam,

LPARAM lParam) = 0;

virtual int ShowModally(int nCmdShow) = 0;



//constructor/destructor:

Window1() : hWnd(NULL) {};

virtual ~Window1() {};

Window1( HINSTANCE hInst,

HICON hIcon,

HICON hIconSm,

HBRUSH hbrBackground,

LPCTSTR lpszCaption,

int nClassStyle,

int nWindowStyle);

};





#endif



//this is in Window1.cpp

//======================================



#include "Generics.h"

#include "Window1.h"

Window1::Window1( HINSTANCE hInst,

HICON hIcon,

HICON hIconSm,

HBRUSH hbrBackground,

LPCTSTR lpszCaption,

int nClassStyle,

int nWindowStyle)

{

Create(hInst, hIcon, hIconSm, hbrBackground,

lpszCaption, nClassStyle, nWindowStyle);

}

LRESULT CALLBACK Window1::WPHandler( HWND hWnd,

UINT uMsg,

WPARAM wParam,

LPARAM lParam)

{

if(uMsg == WM_NCCREATE)

{

LPCREATESTRUCT lpcs = (LPCREATESTRUCT)lParam;

SetWindowLong(hWnd, GWL_USERDATA, (long)lpcs->lpCreateParams);

}

Window1* pThis = (Window1*)(LPVOID):):GetWindowLong(hWnd, GWL_USERDATA));

//Window1* pThis = (Window1*)(GetWindowLong(hWnd, GWL_USERDATA));

if(pThis) return pThis->WndProc(hWnd, uMsg, wParam, lParam);

else return DefWindowProc(hWnd, uMsg, wParam, lParam);

}





void Window1::Create(HINSTANCE hInst,

HICON hIcon,

HICON hIconSm,

HBRUSH hbrBackground,

LPCTSTR lpszCaption,

int nClassStyle,

int nWindowStyle)

{

bWaiting = FALSE;

WNDCLASSEX wcx;

memset(&wcx, 0, sizeof(WNDCLASSEX));

wcx.cbSize = sizeof(WNDCLASSEX);

wcx.style = nClassStyle;

wcx.lpfnWndProc = Window1::WPHandler;

wcx.hInstance = hInst;

wcx.hIcon = NULL;

wcx.hCursor = NULL;

wcx.hbrBackground = hbrBackground;

wcx.lpszMenuName = NULL;

wcx.lpszClassName = lpszCaption;

wcx.hIconSm = NULL;



hcWait = (HCURSOR)LoadImage( NULL,

IDC_ARROW,

IMAGE_CURSOR,

CW_USEDEFAULT,

CW_USEDEFAULT,

LR_DEFAULTSIZE | LR_DEFAULTCOLOR | LR_SHARED);

hcArrow = (HCURSOR)LoadImage( NULL,

IDC_WAIT,

IMAGE_CURSOR,

CW_USEDEFAULT,

CW_USEDEFAULT,

LR_DEFAULTSIZE | LR_DEFAULTCOLOR | LR_SHARED);



ATOM ClassName = RegisterClassEx(&wcx);

hWnd = CreateWindow( (LPCTSTR)ClassName,

lpszCaption,

nWindowStyle,

CW_USEDEFAULT,

CW_USEDEFAULT,

CW_USEDEFAULT,

CW_USEDEFAULT,

(HWND)NULL,

(HMENU)NULL,

(HINSTANCE)hInst,

(LPVOID)this);

}




//and the base class is defined as such (settingsdlg.h)



#include "Window1.h"

class SettingsDlg : public Window1

{

public:

//constructor/destructor:

SettingsDlg( HINSTANCE hInst,

HICON hIcon,

HICON hIconSm,

HBRUSH hbrBackground,

LPCTSTR lpszCaption,

int nClassStyle,

int nWindowStyle) :

Window1( hInst,

hIcon,

hIconSm,

hbrBackground,

lpszCaption,

nClassStyle,

nWindowStyle) {};

//SettingsDlg() {};

//~SettingsDlg() {};


LRESULT WndProc(HWND hWnd,

UINT uMsg,

WPARAM wParam,

LPARAM lParam);

int ShowModally(int nCmdShow);

};



//(settingsdlg.cpp)



#include "Generics.h"

#include "settingsdlg.h"

int SettingsDlg::ShowModally(int nCmdShow)

{

{

if(hWnd)

{

ShowWindow(hWnd, nCmdShow);

UpdateWindow(hWnd);

}

else

return -1;

}


{

MSG msg;

while(GetMessage(&msg, hWnd, 0, 0))

{

TranslateMessage(&msg);

DispatchMessage(&msg);

}

return (int)msg.wParam;

}

}







LRESULT SettingsDlg::WndProc( HWND hWnd,

UINT uMsg,

WPARAM wParam,

LPARAM lParam)

{

switch(uMsg)

{

case WM_MOUSEMOVE:

{

SetCursor((bWaiting) ? (hcWait) : (hcArrow));

return 0;

}

/*

case WM_CLOSE:

{

PostMessage(hWnd, WM_QUIT, 0, 0);

return 0;

}

*/

}

return DefWindowProc(hWnd, uMsg, wParam, lParam);

}





//and my winmain cpp file looks like this:



#include "Generics.h"

#include "SettingsDlg.h"



int WINAPI WinMain( HINSTANCE hInstance,

HINSTANCE hPrevInstance,

LPSTR lpszCmdLine,

int nCmdShow

)

{

HICON hIcon = (HICON)LoadImage( hInstance,

MAKEINTRESOURCE(IDI_TRANS),

IMAGE_ICON,

GetSystemMetrics(SM_CXICON),

GetSystemMetrics(SM_CYICON),

LR_DEFAULTCOLOR | LR_SHARED),

hIconSm = (HICON)LoadImage( hInstance,

MAKEINTRESOURCE(IDI_TRANS),

IMAGE_ICON,

GetSystemMetrics(SM_CXSMICON),

GetSystemMetrics(SM_CYSMICON),

LR_DEFAULTCOLOR | LR_SHARED);


SettingsDlg sdlg( hInstance,

hIcon,

hIconSm,

(HBRUSH)(COLOR_3DFACE + 1),

"SpaceMaze settings",

CS_OWNDC | CS_HREDRAW | CS_VREDRAW,

WS_OVERLAPPEDWINDOW);

return sdlg.ShowModally(SW_SHOWMAXIMIZED);

}







Thanks ever so much to anybody that can help me out of this !
 
V

Volker Hilsheimer

The object you create when calling the constructor is still a "Window1"
object when the Window1 constructor is executed. That's how C++ object
creation works - calling virtual functions (or functions with a "virtual"
semantic, ie. like your's) in the constructor (or the destructor) is
pointless if you expect your class to be subclassed.

But then you have a C++ class anyway. Keep a dictionary of HWND -> Window1
class, and have your window procedure use that dictionary to find the
Window1 object for a certain message.

Volker
 
B

Bonj

But it wasn't trying to call a member function of the derived class from the
constructor, it was trying to do so from WndProcHandler, which is a static
method of the base class.
It isn't really being 'subclassed' in the true sense of the word, I suppose
it is in a way but I thought subclassing involved replacing a WndProc with
another one in order to handle extra messages, this is just one central
WndProc routing the messages to the derived class instantiations.
I don't know what you mean by 'keep a dictionary'. But I've got it working
now, which is the main thing - I was just curious I guess as to why calling
the derived class member function on the actual WM_CREATE message (when the
pointer to its memory address is at this time known, albeit only just)
causes it to crash on an 'invalid v-table' or something, but calling it on
the next message when the pointer has been forgotten and retrieved again,
works fine.....
Thanks again
 
A

a.m.a

Now that we know that you are going through all that trouble because
you use Borland. I just wanted to remind you that posting questions about
windowing code done in Borland in a VC news group is probably not
the most efficient way of doing things. That said, i have never used Borland
but something tellsme that over the long years since Borland has been
around,
im sure that somebody , somewhere has written code to wrap win32 windows
and that it would make more sense to base your work on solid ground
rather then re-inventing the wheel and risk running into a world of problems
and hoping that this news group, wich isnt specialised in Borland will be
solve all those issues.

Im only suggesting all that to save you some time though.

cya
 
B

Bonj

How do you *know* that, with regards to this specific problem? Guessing
generically more like.
You're wrong in that I'm going through this trouble because I use Borland.
It compiles fine with either Borland or VC7.0.
Using the .NET 2002 compiler gave exactly the same error before I fixed the
bug, which I now have, and gave no more information as to the cause of the
error than bcc32 and td32 did.

You're right in that somebody has written code to wrap a Win32 window, the
designers of MFC for one. Which is what I may end up using, if I was writing
applications in a commercial environment. However, this particular project
is more of a hobbyist thing, so I want to design it the way I want, with
just the features I want and not the ones I don't. I want to be able to
genericise all the various portions of utility code and algorithms needed in
both my actual application, and any utility tools I write and use to aid me
in creating it, archive them for possible future use, and then specialise
them as required - as opposed to having to shoehorn my wishlist into
something like MFC. Not that MFC's bad - it's just I've got the "want it
just-so" bug with this particular project, and I want to construct all the
parts myself just to see how it's done if nothing else / in the rare case
that someone else has invent the exact wheel I'm trying to craft.

In terms of posting to these newsgroups - without sounding patronising, many
have tried to see me off before and failed (I'm still here) so I don't see
how you think you can sunshine. If you are genuinely suggesting for my own
benefit and there really is a free, high-volume, on-topic newsgroup that's
more specific to Borland development that'll work with OE, then I'll admit I
don't know about it and I apologise, and would appreciate if you would point
me to the news server name. But even then I'll be more likely to use it in
addition to this one rather than instead of.

Incidentally my reason for using Borland is that I simply don't trust
Microsoft tools any more, since reinstalling their SDK doesn't produce a
windows.h file, and many others. Reinstalling windows hasn't fixed this
problem, so there must be something up with the way the complex web of .msi
files works. Consequently, I can't trust it - and besides, they don't give
an optimizing compiler for free.

However, none of my questions are specific to Borland products in any way,
and are just as relevant to Microsoft development, and since I have GOT
Microsoft VS.NET, including VC7.0, and since my project compiles OK with
that aswell as Borland, as far as you're concerned my questions might as
well be about developing with Microsoft Visual C++ - as if you answered it
as if that was the case, I would be perfectly capable of interpreting the
result - not that any interpretation has thus far been required, nor can I
foresee any being required.

If you're not feeling too good about the fact that I'm motoring along
very-nicely-thank-you with my project using an optimizing compiler I got for
free while you spent a 4-figure sum on your MS equivalent, but were hoping
that as comuppance you'd watch me run into problems of Borland's compiler
not being able to compile Windows code half as good, then I'm sorry but your
delusions have been shattered - because it can do it just as well.

Sorry!
 
D

Daniel Bowen

Instead of calling "Create" in the constructor, you need to do it in a
separate initialization function, so that the object can be completely
constructed. Otherwise, you are falling victim to "object slicing".

Here's some links:
http://www.ezdefinition.com/eng/catalog/pages/comp/430/430.html
http://www-h.eng.cam.ac.uk/help/tpl/languages/C++/Thinking_in_C++/tic0159.html
http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=UTF-8&c2coff=1&[email protected]
http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=UTF-8&c2coff=1&[email protected]


Also, note that you might want to use ATL/WTL, or look at how this is
done there (it does something similar, but doesn't use SetWindowLong
although it could).

-Daniel
 
A

a.m.a

If you were considering MFC for a window wrapper, i would suggest
the ATL classes instead. that way you dont have to bundle MFC.dll .

ATL window classes are also open source.
classes like CWindowImpl defined in <atlwin.h>

As far as windows.h goes, Re-installing Windows has nothing to do with the
SDK.
And we still dont know where this SDK you describe comes from.
The one from Microsoft.com ?

PS: i am realy just trying to help.
 
H

Hendrik Schober

a.m.a said:
[...] That said, i have never used Borland
but something tellsme that over the long years since Borland has been
around,
im sure that somebody , somewhere has written code to wrap win32 windows

It's called VCL (aka Visual Component Lib)
and it makes creating C++ GUI apps as easy
as using VB. Unfortunately, they seem to be
dropping the C++ support for it. (It's a
Delphi lib.)
[...]
cya

Schobi

--
(e-mail address removed) is never read
I'm Schobi at suespammers dot org

"Sometimes compilers are so much more reasonable than people."
Scott Meyers
 
V

Volker Hilsheimer

Your window proc receiving the WM_CREATE message is called synchronously
from the constructor of the Window1 class (via create), which is called
from the SettingsDlg constructor.

The pointer you pass into the CreateWindow API (the "this" pointer)
references at this point an object of type Window1, which is going to be
an object of type SettingsDlg once the Window1 constructor is completed,
and the SettingsDlg constructor is entered. Not a single line of code of
that SettingsDlg constructor is executed, so the object behaves like a
Window1 object.

This is defined C++ behavior - it will happen with Borland, VC++ or gcc.
There is no way around it, other than calling an explicit create()
function to create the window after the C++ object has been completely
created (which leads to a rather awful design like in MFC), or to use a
dictionary (ie. a hash-table) to map the window handle for future window
messages to the C++ object that will then be complete (and then call a
virtual function). Pseudo-code:


LRESULT GenericWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{

Window1 *window = Dictionary.Find(hWnd);
if (!window) // unkown handle - do nothing
uMsg = WM_NULL;

switch(uMsg) {
case WM_SIZE:
window->OnSize(wParam, lParam);
break;

...

default:
return DefWindowProc(...);
}

return 0;
}

OnSize() in above code would be a virtual function of the Window1 class,
possibly reimplemented by your subclass.

Of course WM_CREATE messages cannot be handled, but since you have a
constructor in C++ you don't have to anyway :)

Cheers,
Volker
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top