Sign in to follow this  
santonel

Problems in C++ (WIN32 API)

Recommended Posts

santonel    106
Hey, I've been trying to write a set of wrapper classes for the win32 API to make my programs easier to write (I know there are things like MFC but I don't want to learn it right now). Im not too good at OOP so im having a bit of trouble wrapping my head around it. What I have so far is:
class wccwindow  
{
private:
	WNDCLASSEX wnd;			//the actual window class
	LRESULT CALLBACK WinProc(HWND hwnd,UINT msg,WPARAM wparam,LPARAM lparam);
public:
	//constructors/destructors
	wccwindow();			//constructor
	virtual ~wccwindow();	//destructor

	//vars
	DWORD exstyle;			//extended styles
	int x,y;				//coords of the window
	HWND parent;			//parent window
	HWND menu;				//menu
	HWND window;			//handle to this window;

	//functions
	void init(LPCTSTR windowname,LPCTSTR windowtitle,DWORD windowstyle,int xlen,int ylen,HINSTANCE instance);  //initialize window
	
};
///////////////////////////////////////////////////////////////////
wccwindow::wccwindow()
{
wnd.cbSize = sizeof(WNDCLASSEX);
wnd.style = CS_VREDRAW | CS_HREDRAW | CS_OWNDC;
wnd.cbClsExtra = 0;
wnd.cbWndExtra = 0;
wnd.hIcon = LoadIcon(NULL,IDI_APPLICATION);
wnd.hCursor = LoadCursor(NULL,IDC_ARROW);
wnd.hbrBackground = (HBRUSH)GRAY_BRUSH;
wnd.lpszMenuName = NULL;
wnd.hIconSm = LoadIcon(NULL,IDI_APPLICATION);
wnd.lpfnWndProc = WinProc;
x = CW_USEDEFAULT;
y = CW_USEDEFAULT;
exstyle = NULL;
parent = NULL;
menu = NULL;
}

wccwindow::~wccwindow()
{

}
///////////////////////////////////////////////////
// Functions
///////////////////////////////////////////////////
void wccwindow::init(LPCTSTR windowname,LPCTSTR windowtitle,DWORD windowstyle,int xlen,int ylen,HINSTANCE instance)
{
wnd.hInstance = instance;
wnd.lpszClassName = windowname;
RegisterClassEx(&wnd);
window = CreateWindowEx(exstyle,windowname,windowtitle,windowstyle,x,y,xlen,ylen,parent,(HMENU)menu,instance,NULL);
}

//LRESULT CALLBACK wccwindow::WinProc(HWND hwnd,UINT msg,WPARAM wparam,LPARAM lparam)
//{
//return(0);
//}
The problem I get is with this line: wnd.lpfnWndProc = WinProc; My compiler (VC++ 6) gives me an error: error C2440: '=' : cannot convert from 'long (__stdcall wccwindow::*)(struct HWND__ *,unsigned int,unsigned int,long)' to 'long (__stdcall *)(struct HWND__ *,unsigned int,unsigned int,long)' There is no context in which this conversion is possible Can anybody offer some advice? [Edited by - santonel on October 26, 2005 5:32:01 PM]

Share this post


Link to post
Share on other sites
The Rug    628
IIRC you can't use a member function for a callback. Pointers to member functions are handled differntly to pointers to functions. Check out this arcticle, it might help clear things up, and this may help you overcome the problem.

Share this post


Link to post
Share on other sites
Jesse Chounard    394
It's because class functions have a hidden parameter, which is called the "this" pointer. This makes the function in your class different from a standard WindowProc function, and that's why the compiler is complaining.

Share this post


Link to post
Share on other sites
SiCrane    11839
You can't assign a pointer to a non-static member function to a pointer to a normal function. The usual method to handle this is to assign a static or normal function to the windows procedure and use GetWindowLong() or GetWindowLongPtr() to a pointer from the GWL_USERDATA of the windows class. See this article for more details. When you call CreateWindowEx() pass the pointer to the class as parameter.

[Edited by - SiCrane on June 25, 2006 1:05:25 PM]

Share this post


Link to post
Share on other sites
santonel    106
Thanks for the help but like I said im not very experienced with OOP so I only partly understand those articles. Plus they seem to be about making pointers to member functions from outside the class.

The wnd.lpfnWndProc is a function pointer in a class which is within another class and the function i need it to point to is in the "outer" class. I have no idea how to go about this. Any ideas?

P.s. my brain hurts.

Share this post


Link to post
Share on other sites
santonel    106
Ok I got around that problem and realised after reading your articles (thanx for them by the way) that im going to need a static message router to route messages back to an event handler in the instance of the class that generated it.

The problem is figring out which instance sent the messages. In the article the code was this:

wnd = reinterpret_cast<Window *>(::GetWindowLong(hwnd, GWL_USERDATA));

I do not get how this works. What exatcly does GetWIndowLong return and how can it be casted into a totally unrelated type? It sort of flies in the face of everyting I learned about type casting.

Other than that is there other ways to accomplish this?

Share this post


Link to post
Share on other sites
Deyja    920
The 'window long' is just an arbitrary bit of data that the application can set when creating the window. In your case, when you create the window, you cast a pointer to that window to long and store it there. Later, you can retreive it using the handle your message handler receives and cast it back into a pointer.

Share this post


Link to post
Share on other sites
santonel    106
Quote:
Original post by Deyja
The 'window long' is just an arbitrary bit of data that the application can set when creating the window. In your case, when you create the window, you cast a pointer to that window to long and store it there. Later, you can retreive it using the handle your message handler receives and cast it back into a pointer.


Im starting to get it now. Why does this require a reinterpret_cast? I've never seen anyone use one before.

NVM the above. I get it now.

Share this post


Link to post
Share on other sites
santonel    106
OK thanx for the help everyone my program works now. Now im on to event handling. For this im trying to do it using function pointers. Basically the premise is every event gets its own function pointer.When the event comes up in the wndproc the function pointer is called. The person using the class can then point these function pointers to their own functions so that when an event is triggered the function pointer calls their custom function to handle the event. So far i've implemented this and it compiles without errors but it crashes when the function pointer is called. I created an empty static event function in my class that just returns 0 and set all my function pointers to it by default in the constructor.

Any ideas why it's crashing.

(If you want to see the code just ask.)

Share this post


Link to post
Share on other sites
santonel    106
The source code mentioned above.

The class

class wccwindow
{
private:
//internal functions
WNDCLASSEX wnd; //the actual window class
static LRESULT CALLBACK message_route(HWND hwnd,UINT msg,WPARAM wparam,LPARAM lparam);
LRESULT CALLBACK message_proc(HWND hwnd,UINT msg,WPARAM wparam,LPARAM lparam);
static int empty_event(wccwindow *sender,WPARAM wparam,LPARAM lparam);
//vars
DWORD exstyle; //extended styles
int x,y; //coords of the window
HWND parent; //parent window
HWND menu; //menu
HWND window; //handle to this window;
int xlen,ylen; //length and width of window
DWORD windowstyle; //style

public:
//constructors/destructors
wccwindow(); //constructor
virtual ~wccwindow(); //destructor

//functions
void init(LPCTSTR windowname,LPCTSTR windowtitle,HINSTANCE instance); //initialize window
//properties
void set_pos(int px,int py); //sets window position
void set_size(int sx,int sy); //sets window size
void set_exstyle(DWORD ex); //sets extened style
void set_style(DWORD style); //sets window style
void set_back_col(HBRUSH brush); //sets background color
void set_titlebar(LPCTSTR title); //sets the window title
//events
int (* on_create)(wccwindow *sender,WPARAM wparam,LPARAM lparam);
};



Part of the implementation


//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

wccwindow::wccwindow()
{
wnd.cbSize = sizeof(WNDCLASSEX);
wnd.style = CS_VREDRAW | CS_HREDRAW | CS_OWNDC;
wnd.cbClsExtra = 0;
wnd.cbWndExtra = 0;
wnd.hIcon = LoadIcon(NULL,IDI_APPLICATION);
wnd.hCursor = LoadCursor(NULL,IDC_ARROW);
wnd.hbrBackground = (HBRUSH)GRAY_BRUSH;
wnd.lpszMenuName = NULL;
wnd.hIconSm = LoadIcon(NULL,IDI_APPLICATION);
wnd.lpfnWndProc = message_route;
x = CW_USEDEFAULT;
y = CW_USEDEFAULT;
exstyle = NULL;
parent = NULL;
menu = NULL;
xlen=320;
ylen=240;
windowstyle = WS_OVERLAPPEDWINDOW | WS_VISIBLE;
on_create = empty_event;
}
//------------------------------------------------
wccwindow::~wccwindow()
{

}
///////////////////////////////////////////////////
// Functions
///////////////////////////////////////////////////
void wccwindow::init(LPCTSTR windowname,LPCTSTR windowtitle,HINSTANCE instance)
{
wnd.hInstance = instance;
wnd.lpszClassName = windowname;
SetWindowLong(window,GWL_USERDATA, reinterpret_cast&lt;long&gt;(this));
RegisterClassEx(&wnd);
window = CreateWindowEx(exstyle,windowname,windowtitle,windowstyle,x,y,xlen,ylen,parent,(HMENU)menu,instance,NULL);
}
//---------------------------------------------------
LRESULT CALLBACK wccwindow::message_route(HWND hwnd,UINT msg,WPARAM wparam,LPARAM lparam)
{
wccwindow *tmp;
tmp = reinterpret_cast&lt;wccwindow *&gt;(GetWindowLong(hwnd,GWL_USERDATA));
if( tmp-&gt;message_proc(hwnd,msg,wparam,lparam) == -1)
{
return DefWindowProc(hwnd,msg,wparam,lparam);
}
else
{
return 0;
}
}
//---------------------------------------------------
LRESULT CALLBACK wccwindow::message_proc(HWND hwnd,UINT msg,WPARAM wparam,LPARAM lparam)
{
switch(msg)
{
case WM_CREATE:
{
//this line cause the crash V
on_create(this,wparam,lparam);
return 0;
}break;

case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}break;
}
return -1;
}
//----------------------------------------------------
int wccwindow::empty_event(wccwindow *sender,WPARAM wparam,LPARAM lparam)
{
return 0;
}



Anyone know why calling the function pointer causes the crash?

[Edited by - santonel on October 25, 2005 4:44:34 PM]

Share this post


Link to post
Share on other sites
santonel    106
OK i've figured out that it isn't just the function pointer. Any attempt to call a member function from the wccwindow::message_proc crashes the program. Does the fact that the wccwindow::message_proc function itself was called by a static function?(wccwindow::message_route). And if so, how can i get around this problem?

Share this post


Link to post
Share on other sites
rmsimpson    228
Quote:
Original post by santonel
OK i've figured out that it isn't just the function pointer. Any attempt to call a member function from the wccwindow::message_proc crashes the program. Does the fact that the wccwindow::message_proc function itself was called by a static function?(wccwindow::message_route). And if so, how can i get around this problem?


You do realize you're reinventing the wheel, right? ATL/WTL has a phenomenally powerful set of windowing template classes without the MFC bloat, and has taken care of just about every scenario you might dream up. ATL provided the base window templates, and WTL is built on top of them.

You are going to have a lot of problems implementing your class, for many reasons. Here's a few problems I saw immediately:

SetWindowLong(window,GWL_USERDATA, reinterpret_cast<long>(this));
RegisterClassEx(&wnd);
window = CreateWindowEx(exstyle,windowname,windowtitle,windowstyle,x,y,xlen,ylen,parent,(HMENU)menu,instance,NULL);
}



First, you're calling SetWindowLong() on a window that doesn't exist until CreateWindowEx() is called.

Second, even if you called SetWindowLong() after creating the window, the windowproc has already received about 10-20 messages and dispatched them.

Of course, when you receive any message in your windowproc, you must call GetWindowLong() to get the pointer to wccwindow, and if it is NULL (which it MAY BE) you must call the DefWindowProc without trying to dispatch the message.

The fundamental problem you'll face is that the static windowproc doesn't have any decent way of getting a pointer to the window's wccwindow instance when the window is being created -- and there are literally dozens of messages that need dispatching during the creation process before control ever returns to you from CreateWindowEx(). There are lots of ways to do this, some of them more technical than others. Some involve extending the WNDCLASSEX struct, some involve WM_NCCREATE, but most all boil down to storing the wccwindow pointer with the window's data via SetWindowLong. ATL/WTL takes a different approach entirely, and a very cool one, but you'll hurt your head trying to figure it out -- it involves some clever inline assembly.

I'll give you credit for wanting to figure this stuff out for yourself (I wrote straight C windows for years before switching to WTL) but I just hate seeing someone hammering away with a chisel to make a wheel when there's a perfectly good Ferrari in their garage. You said you didn't want to learn MFC, which I can't blame you for ... so give WTL a try. To use the Ferarri analogy: It will take you longer to build the wheel than it will to learn how to drive a stick.

Robert

Share this post


Link to post
Share on other sites
santonel    106
Quote:
Original post by rmsimpson
Quote:
Original post by santonel
OK i've figured out that it isn't just the function pointer. Any attempt to call a member function from the wccwindow::message_proc crashes the program. Does the fact that the wccwindow::message_proc function itself was called by a static function?(wccwindow::message_route). And if so, how can i get around this problem?


You do realize you're reinventing the wheel, right? ATL/WTL has a phenomenally powerful set of windowing template classes without the MFC bloat, and has taken care of just about every scenario you might dream up. ATL provided the base window templates, and WTL is built on top of them.

You are going to have a lot of problems implementing your class, for many reasons. Here's a few problems I saw immediately:
*** Source Snippet Removed ***

First, you're calling SetWindowLong() on a window that doesn't exist until CreateWindowEx() is called.

Second, even if you called SetWindowLong() after creating the window, the windowproc has already received about 10-20 messages and dispatched them.

Of course, when you receive any message in your windowproc, you must call GetWindowLong() to get the pointer to wccwindow, and if it is NULL (which it MAY BE) you must call the DefWindowProc without trying to dispatch the message.

The fundamental problem you'll face is that the static windowproc doesn't have any decent way of getting a pointer to the window's wccwindow instance when the window is being created -- and there are literally dozens of messages that need dispatching during the creation process before control ever returns to you from CreateWindowEx(). There are lots of ways to do this, some of them more technical than others. Some involve extending the WNDCLASSEX struct, some involve WM_NCCREATE, but most all boil down to storing the wccwindow pointer with the window's data via SetWindowLong. ATL/WTL takes a different approach entirely, and a very cool one, but you'll hurt your head trying to figure it out -- it involves some clever inline assembly.

I'll give you credit for wanting to figure this stuff out for yourself (I wrote straight C windows for years before switching to WTL) but I just hate seeing someone hammering away with a chisel to make a wheel when there's a perfectly good Ferrari in their garage. You said you didn't want to learn MFC, which I can't blame you for ... so give WTL a try. To use the Ferarri analogy: It will take you longer to build the wheel than it will to learn how to drive a stick.

Robert



Yeah I know i'm re-inventing the wheel. More than anything this is a learning experience for me to sharpen my knowledge of C++ and the API. I don't care for using other's code if i dont know at least on some level how it works.

I called the set window long before the createwindowex because i figured it would catch these 10-20 messages that the createwindowex generates. Also for now since I want this to be really basic I'm not interested in most of the events generated by CreateWindowEx. Just the basic stuff like WM_MOVE or WM_SIZE and the like.

What I need to figure out is why the program crashes when I call a member function in either the message_route or message_proc functions. Anywhere else it works fine.

Share this post


Link to post
Share on other sites
rmsimpson    228
Quote:
Original post by santonel
I called the set window long before the createwindowex because i figured it would catch these 10-20 messages that the createwindowex generates. Also for now since I want this to be really basic I'm not interested in most of the events generated by CreateWindowEx. Just the basic stuff like WM_MOVE or WM_SIZE and the like.

What I need to figure out is why the program crashes when I call a member function in either the message_route or message_proc functions. Anywhere else it works fine.


In your window proc you must do several things:
1. Call GetWindowLong() to get a pointer to the window class
2. If the pointer is null, which it can be sometimes, you must call DefWindowProc() and exit (unless you receive WM_NCCREATE, in which case proceed to #4)
3. If it is not null, you cast the pointer to a wccwindow* and call the method on the pointer that you want.
4. The last parameter of CreateWindowEx() should be a pointer to the wccwindow instance. When your wndproc receives WM_NCCREATE, you must store the LPARAM value using SetWindowLong() -- this is your pointer to the class instance.

Share this post


Link to post
Share on other sites
DerekSaw    243
It is better you do SetWindowLong() under WM_NCCREATE (or WM_CREATE) of your static msg handler. Briefly:


LRESULT CALLBACK WndProc(...)
{
AWindowClass pwin = (AWindowClass*)GetWindowLong(hwnd, GWL_USER);
switch (msg)
{
case WM_CREATE: // or WM_NCCREATE
{
AWindowClass * pwin = new AWindowClass(...);
SetWindowLong(hwnd, GWL_USER, (long)pwin);
pwin->OnCreate(...);
}
return 0;

case WM_PAINT:
{
pwin->OnPaint(...);
}

//...

return 0;
}
return DefWindowProc(...);
}


Basically you just need to allocate your window class and set it as the GWL_USER data in one of the earliest msg you received from Windows after calling CreateWindowEx().

Share this post


Link to post
Share on other sites
santonel    106
Quote:
Original post by rmsimpson
Quote:
Original post by santonel
I called the set window long before the createwindowex because i figured it would catch these 10-20 messages that the createwindowex generates. Also for now since I want this to be really basic I'm not interested in most of the events generated by CreateWindowEx. Just the basic stuff like WM_MOVE or WM_SIZE and the like.

What I need to figure out is why the program crashes when I call a member function in either the message_route or message_proc functions. Anywhere else it works fine.


In your window proc you must do several things:
1. Call GetWindowLong() to get a pointer to the window class
2. If the pointer is null, which it can be sometimes, you must call DefWindowProc() and exit (unless you receive WM_NCCREATE, in which case proceed to #4)
3. If it is not null, you cast the pointer to a wccwindow* and call the method on the pointer that you want.
4. The last parameter of CreateWindowEx() should be a pointer to the wccwindow instance. When your wndproc receives WM_NCCREATE, you must store the LPARAM value using SetWindowLong() -- this is your pointer to the class instance.



Steps 1 to 3 are handled in the message_route rather than the message_proc in my implementation.

EDIT: Thanks I got it working!

[Edited by - santonel on October 27, 2005 7:47:04 PM]

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this