Jump to content
  • Advertisement

Archived

This topic is now archived and is closed to further replies.

OberonZ

WinProcs and C++

This topic is 5825 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi.
I've run into this before as well. As far as I know the Windows API will not allow you to do this directly. However, it can be done with a little effort.

First, create the standard WindowProc function. Don't bother with filling out the body yet. Now create the window as you were doing using this WindowProc as the function pointer.
After this you can write:

SetWindowLong( handle, GWL_USERDATA,(DWORD)this)

where "handle" is the HWND of the window you just created.

Now in the standard WindowProc function you can have:


LRESULT CALLBACK WindowProc( paramaters )
{
CMapEdit* mapEdit=(CMapEdit*)GetWindowLong (hwnd, GWL_USERDATA);

mapEdit->MeWndProc( paramaters )
//This will now call your proc
}

Here's an example from my own editor:

/*Basic WindowProc*/
LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
//Get the instance of the editor and call
//Message Handler
Editor *editor = (Editor*)GetWindowLong(hwnd,GWL_USERDATA);
editor->MessageHandler(hwnd, msg, wparam, lparam);
}

//Editor Constructor
Editor::Editor(HINSTANCE Inst, int Height, int Width)
{

//Window is my own class to create a window
// Create the Window to run in
m_MainWindow = new Window(Inst, "Editor" , Width, Height, WindowProc);

...

SetWindowLong(m_MainWindow->GetHandle(), GWL_USERDATA, (DWORD)this);

}

This allows you to have acess to one instance that you create (I think). Anyways you can always look at the function SetWindowLong()
in your compilier's help.

Whoa. I hope that this comes out formatted ok. If this doesn't work let me know and I try to write a better example.

Andrew
acraig@engr.mun.ca

[This message has been edited by acraig (edited October 01, 1999).]

[This message has been edited by acraig (edited October 01, 1999).]

Share this post


Link to post
Share on other sites
Advertisement
The above code is pretty much the standard way to go about things but you missed something. Actually I'm surprised that code doesn't crash.

When you call CreateWindow or CreateWindowEx, Windows sends a fair amount of messages (I think the first one is guaranteed to be WM_CREATE) that are sent to the WindowProc BEFORE CreateWindow returns. But with your code, you are calling SetWindowLong after the call to CreateWindow (I'm assuming that's done somewhere between the new Window() and the SetWindowLong). So the WM_CREATE message will come to your window proc, the Editor variable will be NULL, and when it tries to make the call to MessageHandler it will crash.

A way to get around this is to use a temporary global/static variable to hold the Editor * and call SetWindowLong in the WM_CREATE handler. This works as long as you are never creating more than one window at the same time - if you are, you'll have to wrap window creation with a synchronization primitive like a critical section.

------------------
-vince


Share this post


Link to post
Share on other sites
Vince:

After reading your post, I am starting to wonder why it doesn't crash. It compiles and runs fine on my machine.

I guess I'll have to play around with it some more when I get some time to see exactly what's going on.


Andrew


Share this post


Link to post
Share on other sites
Just to make sure everyone understands why this is necessary, C++ puts a this pointer as the first argument to every class method you write.

This adds an argument to the function prototype, and causes it not to match up with the standard WndProc prototype.

The method you guys mention does work, but I usually declare the function as static in the class definition. The static keyword makes sure that the implied this is not your first argument to the function. It's a little prettier to do it this way in my opinion.

------------------

-Kentamanos

Share this post


Link to post
Share on other sites
acraig:

The only thing I can think is that your MessageHandler doesn't actually do anything for WM_CREATE and some of the other messages that get sent during window creation (I think WM_SIZE is another one). I.e. if you don't access any member variables, then the NULL this pointer won't matter the times those are called, and you won't get a crash.

Easiest way to see what's going on is put a breakpoing in MessageHandler and see when it gets called and what the value of "this" is.

------------------
-vince


Share this post


Link to post
Share on other sites
Thanks for the tip, Kantamanos,
I changed my code and tried to declare the winproc static and it working perfectly
Ahh, the joys of encapsulation.

Thanks again,
-OberonZ

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
You could save yourself from adding an ugly static function in your C++ by doing this the way MFC (yuck, I know) does it. When registering the window class, use DefWindowProc as your class wndproc. Then use SetWindowLong() with the GWL_WNDPROC argument. Make sure you read the docs for this! You have to do things a little differently since you're "chaining" wndprocs together.

Clay

Share this post


Link to post
Share on other sites
To answer the problem in general, I'll kind of give a top view of what I've done in the past. If some of this is wrong, let me know. It's from my memory, which is not what it used to be (too many career beers I suppose).

It looks like one of the easiest ways to do this is to pass the "this" pointer of the class to the CreateWindow function. The last parameter is a pointer to a void (LPVOID). Our class could have a Create function that calls CreateWindow with the last parameter being "this".

If we pass in "this" when we create the window, we can handle call SetWindowLong in the WM_NCCREATE. WM_NCCREATE is the place to do this (it gets called before the other messages, including WM_CREATE).

The following might show you what your WinProc could look like. We're basically getting the "this" pointer from the GetWindowLong. This function would be declared static in the class definition.

code:

LRESULT CALLBACK OurWin::WinProc(HWND hWnd, UINT uMsg, WPARAM
wParam, LPARAM lParam)
{
OurWin *pWindow = 0;
pWindow = (OurWin *)GetWindowLong(hWnd, GWL_USERDATA);

switch(uMsg)
{
case WM_NCCREATE :
// Save pointer to object
SetWindowLong(hWnd, GWL_USERDATA,
(LONG)((LPCREATESTRUCT)lParam->lpCreateParams);
OurWin *pWindow = (OurWin *)((LPCREATESTRUCT)lParam)->lpCreateParams;
pWindow->m_hWnd = hWnd;
break;
case WM_CREATE:
pWindow->OnCreate();
return 0;
case WM_CLOSE :
pWindow->OnClose();
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}


Notice that in the WM_NCCREATE, we take a look at the LPARAM, which is a pointer to a CREATESTRUCT. The lpCreateParams member of this struct is the parameter we passed originally into CreateWindow (or CreateWindowEx), which is a pointer to our object. We then call a SetWindowLong to save a "this" pointer with the window. Also notice that all of the other messages use the pointer we pull out at the beginning of the function (the GetWindowLong call).

The thing to realize here, is that we are in a static function defined in our class. If we use the "this" pointer we pull out with GetWindowLong, we can access any of our normal methods without problems (including private and protected members).

There are other ways to do this as well. If you get a chance, check out how ATL handles it. They use a declspec called "novtable". Take a look at it if you get a chance, it's pretty cool. The ATL method might not be too portable though.

To be honest, you might consider using the CWindow as your base class. Unlike MFC, ATL is pretty small and tight. I hate MFC with a passion, but ATL is pretty slick.

Like I said, let me know if you have any questions. I'm happy to send more code if you'd like. Drop me an email if you need to (just in case I miss a post to this thread).

Good luck,

------------------

-Kentamanos

[This message has been edited by Kentamanos (edited October 07, 1999).]

Share this post


Link to post
Share on other sites

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!