Sign in to follow this  
ajm113

Win32 Child Window Event Question (C++)

Recommended Posts

Hello, is there a way when I create a child window I can tell it to use a different "lpfnWndProc" then it's default className's? I'm making a editor and I want to use child window that have their own events with out having to go through the trouble making a dozen class registers. I create my child windows like so:
	    if (!(Layers = CreateWindowEx(WS_EX_TOOLWINDOW, 
    "OpenGL2", "Layers",
    WS_POPUP | WS_SYSMENU | WS_THICKFRAME | WS_CAPTION,
    0, CenterScreenPosY, 200, 100,
    hWnd, NULL, hInst, NULL))) {

 //Print a error on the screen..
		return false;							// Return FALSE
  }



Thanks I appreciate any help or questions like always. :)

Share this post


Link to post
Share on other sites
You've got a couple options. The first, as mentioned before, is to use SetWindowLongPtr along with GWLP_WNDPROC. The other is to create a superclass of an existing class (see MSDN on this).

Quote:
MSDN - SetWindowLongPtr
Calling SetWindowLongPtr with the GWLP_WNDPROC index creates a subclass of the window class used to create the window. An application can subclass a system class, but should not subclass a window class created by another process. The SetWindowLongPtr function creates the window subclass by changing the window procedure associated with a particular window class, causing the system to call the new window procedure instead of the previous one. An application must pass any messages not processed by the new window procedure to the previous window procedure by calling CallWindowProc. This allows the application to create a chain of window procedures.


In both versions, you need to store the original WndProc returned by Set/Get WindowLongPtr and make it available to your custom version, so you can call it with CallWindowProc. If you don't, the window might not behave correctly. Note, this is a completely different WindowProcedure than the normal one you write. In most cases, you will want to call the orginal first from your new custom one, then handle any specific behavior you want; but it is up to you where you call it in relation to your custom code.

When I did something similar, I did the superclass option, so I could store the old window proc in the new superclass using SetClassLongPtr as well as making it available through my custom C++ wrapper class as well.

[edit]
More info about what you want to do - MSDN - About Windows Procedure

Share this post


Link to post
Share on other sites
Ok, thanks. Although there is a bit of a problem still when I try to call call a function thats in the class. Here is how I set up the creation of the Child Window and yes this is on topic in a way since I think a diffrent message then should is being passed into "AttributeProc" then should when I use the "SetWindowLongPtr" function.


if (!(Attribute = CreateWindowEx(WS_EX_TOOLWINDOW,
"Class2", "Attributes",
WS_POPUP | WS_SYSMENU | WS_THICKFRAME | WS_CAPTION,
0, 0, 200, CenterY,
hWnd, NULL, hInst, NULL))) {

return false; // Return FALSE

}
SetWindowLongPtr(Attribute, GWLP_WNDPROC, (LONG)AttributeProc);






Now in AttributeProc


LRESULT CALLBACK Core::AttributeProc(HWND bhWnd, UINT message, WPARAM wParam, LPARAM lParam) {

static Core* pWnd = NULL;
static bool bProcessed = false;

switch (message) {
case WM_NCCREATE: {
int val = 0;

SetWindowLong(bhWnd, GWL_USERDATA, (long) (LPCREATESTRUCT(lParam)->lpCreateParams));
break;
}
default: {
pWnd = (Core*) GetWindowLong(bhWnd, GWL_USERDATA);
if(NULL != pWnd) {
bProcessed = pWnd->MessageHandlerAttri(message, wParam, lParam);
}

}
}

return DefWindowProc(bhWnd, message, wParam, lParam);

} //Window Proc





I'm trying to get the "pWnd->MessageHandlerAttri(message, wParam, lParam);" line to get called, but it won't call that line like it should. I'm calling that function because AttributeProc is a static in the core class and I can't have access to local variables in "Core" unless I call a non-static message handler.

Share this post


Link to post
Share on other sites
[edit]
Reread last post and realized you basically did what I originally wrote.

Try making the Window Procedure a friend to your class. This will give you access to your class instance variables from within the Window Procedure.

Share this post


Link to post
Share on other sites
Woops sorry I must have posted just after you did, sry about that!

I'm a bit confused on your first post.. I tried playing with different methods, but it seems I can't get anything going. I'm not trying to ask to spoon feed me here, but a pausado code? I'm not quite familiar where I'm at now.

Share this post


Link to post
Share on other sites
No problem. I've recently been doing something very similar to what it sounds like your trying to do, so I know it can be a little frustrating. I'll try and make some code below that I think it trying to do what you want.

PS This is untested. Some of this is ripped or modified from my code and cobbled together from memory.



LRESULT WindowProcedure(HWND in_WindowHandle, UINT in_Message, WPARAM in_wParam, LPARAM in_lParam)
{
switch(in_Message)
{
case WM_CREATE:
{
/* Convert the lParam into something more usable */
CREATESTRUCT* _CreateStruct(reinterpret_cast<CREATESTRUCT*>(in_lParam));
/* Get the class pointer from the structure */
Control* const _Caller(_CreateStruct->lpCreateParams);
return 0;
}

default:
{
return DefWindowProc(in_WindowHandle, in_Message, in_wParam, in_lParam);
}
}
}

LRESULT ChildProcedure(HWND in_WindowHandle, UINT in_Message, WPARAM in_wParam, LPARAM in_lParam)
{
/* This can be a doosy, if you aren't paying attention */
#pragma warning(push) /* Disable C4312 warning */
#pragma warning(disable:4312) /* Disable C4312 warning */
Control* const _CallerPointer((in_WindowHandle != 0) ? reinterpret_cast<Control*>(GetWindowLongPtr(in_WindowHandle, GWLP_USERDATA)) : 0);
#pragma warning(pop) /* Renable C4312 warning */

if(_CallerPointer == 0)
{
/*
* No pointer currently stored with the handle. This
* is likely some kind of create message and should
* be handled accordingly. This will only likely happen
* if you do the superclassing.
*/

return 0;
}

/*
* This will call the original window procedure for the class.
* You can change it around so you can call your code, but at
* some point you probably need to call this for each of the
* messages. This does assume it has access to the protected
* member. One safer way around this would be to make a public
* function that returns the old WNDPROC.
*/

LRESULT _OldResult(CallWindowProc(_CallerPointer->m_OldProc, in_WindowHandle, in_Message, in_wParam, in_lParam));

return _OldResult;
}

class Control
{
public:
/*
* I don't think the syntax is right here, in my code
* I store my procedures as statics in a class and make
* that class a friend.
*/

friend WindowProcedure;
friend ChildProcedure;

void CreateHandle()
{
m_WindowHandle = CreateWindowEx(..., this);
if(m_WindowHandle == 0)
{
const DWORD Error(GetLastError());
/* Do something */
throw std::exception("CreateWindowEx() failed.");
}

/* Save the pointer */
SetLastError(0);
#pragma warning(push) /* Disable C4244 warning */
#pragma warning(disable:4244) /* Disable C4244 warning */
if(SetWindowLongPtr(m_WindowHandle, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this)) == 0)
#pragma warning(pop) /* Renable C4244 warning */
{
const DWORD Error(GetLastError());
if(Error != 0)
{
/* A real error */
throw std::exception("SetWindowLongPtr() failed.");
}
}

/* Save the new procedure and the old one */
SetLastError(0);
#pragma warning(push) /* Disable C4244 warning */
#pragma warning(disable:4244) /* Disable C4244 warning */
m_OldProc = SetWindowLongPtr(m_WindowHandle, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(this));
#pragma warning(pop) /* Renable C4244 warning */
if(m_OldProc == 0)
{
const DWORD Error(GetLastError());
if(Error != 0)
{
/* A real error */
throw std::exception("SetWindowLongPtr() failed.");
}
}
}

protected:
HWND m_WindowHandle;
WNDPROC m_OldProc; /* For simplicity's sake, as if you do it right, you can share the OldProc between similar Control types, like with a static, and subclasses of each idnvidual type */
};




The pragmas there are to supress an warning reported when doing conversions involving LONG_PTR and Set/Get-WindowLongPtr. The code is legal (as per MSDN), but the SDK has a little glitch that causes it to cast strange enough to trigger the warning.

Share this post


Link to post
Share on other sites
I still can't get the message handler in my class I want to hold to work...

Are you sure I should be looking at how I register my window or there is something I'm forgetting? Because every time I try to use SetWindowLong on a second window it returns 0 in the event handler. So I'm thinking there is something wrong with the HWND being passed in.


void Core::Create(HINSTANCE _hInst)
{
ClassNames.push_back("AttributeWnd");

InitDockWindow(ClassNames[0],(WNDPROC) AttributeProc, NULL, (HBRUSH)(WHITE_BRUSH), _hInst);


if (!(Attribute = CreateWindowEx(WS_EX_TOOLWINDOW,
ClassNames[0], "Attributes",
WS_POPUP | WS_SYSMENU | WS_THICKFRAME | WS_CAPTION,
0, 0, 200, CenterY,
hWnd, NULL, _hInst, NULL))) {

//error!
return false; // Return FALSE

}

}//Create application function

bool Core::InitDockWindow(char* ClassName, WNDPROC Proc, LPCSTR Menu, HBRUSH bBackground, HINSTANCE hInstance)
{

WNDCLASSEX wc;
ZeroMemory(&wc, sizeof(wc));

//Window class for the main application parent window
wc.cbSize = sizeof(wc);
wc.style = 0;
wc.lpfnWndProc = Proc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = 0;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = bBackground;
wc.lpszMenuName = Menu;
wc.lpszClassName = ClassName;
wc.hIconSm = 0;

if(RegisterClassEx(&wc) == 0){
return false;
}else{
return true;
}
}


LRESULT CALLBACK Core::AttributeProc(HWND bhWnd, UINT message, WPARAM wParam, LPARAM lParam) {
static Core* pWnd = NULL;
static bool bProcessed = false;

switch (message) {

case WM_NCCREATE: {
int val = 0;

val = SetWindowLong(bhWnd, GWL_USERDATA, (long) (LPCREATESTRUCT(lParam)->lpCreateParams));
if(val == 0)
{
printf("Didn't work...");
}
break;
}
default: {
pWnd = (Core*) GetWindowLong(bhWnd, GWL_USERDATA);
if(NULL != pWnd) {
bProcessed = pWnd->MessageHandler(message, wParam, lParam);
}

}
}

if(!bProcessed)
{
return DefWindowProc(bhWnd, message, wParam, lParam);
}

return 0;

} //Window Proc








[Edited by - ajm113 on December 24, 2009 10:16:14 PM]

Share this post


Link to post
Share on other sites
Wow, I feel like a idiot! I totally forgot to add "(void*)this" at the end of the argument on CreateWindowEx! :S

That's why nothing was working! I figured I would figure this out by the end of this day!

Thank you guys anyways for your help! :D

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