Hi all,
Ok, before I start I want to say that I have asked this question in other places and have been told that I am misunderstanding window subclassing, but I am convinced that what I am saying is correct, anyway, I know this forum and respect most of you guys here so I know that I can find the truth here.
Anyway, lets set the senario, say I have written some wrapper classes for some win32 common controls. Each control needs to be self sufficient so that it can handle it events automatically without needing the programmer tp pass information to it from the parent window. So in a program there are three common controls, lets say a status bar, tool bar and tree view. As Each control is created it subclasses the parent procedure so it can automatically handle notification events. So lets say we create the status bar, it subclasses the parent and sets its own procedure in place of the original one, then we create the tool bar, it also subclasses the parent, but instead of replacing the original procedure it actually replaces the staus bars procedure, because the status bar already replaced the original one, so now we have a chain or window procedures, the tool bars procedure is called by windows, it does what it needs to do then it passes the messages onto the status bar procedure which does what it needs to do and then the status bar passes messages onto the original procedure. Now say we create the tree view control, it subclasses the parent, but actually it replaces the tool bars procedure, so the chain now starts with the tree view, goes to the tool bar, then to the status bar and finally back to the original procedure.
Ok, so everything is going good, no problems.
But now lets say the program, being dynamic in nature, no longer needs the status bar, so the status bar is destroyed, but before it is destroyed the wrapper classes destructor puts what it believes to be the original window procedure (and in this example it actually is the original) back where it got it from, but the problem is that the tool bar and tree view are behind it in the chain, so the status bar will restore the orignal procedure effectively cutting out the tool bar and tree view controls.
To make sure I am clear I will give a code example
// Create the main window
parentwnd = CreateWindow(....);
// Instantiate and Create the Status Window
CStatusBar status;
status.Create(parentwnd, ....);
// Inside of this create method we do this
OldWndProc = (WNDPROC)SetWindowLong(parentwnd, GWL_WNDPROC, (LONG)statusproc)
// This remembers the original proc
// Instantiate and Create the Tool bar Window
CToolBar tool;
tool.Create(parentwnd, ....);
// Inside of this create method we do this
OldWndProc = (WNDPROC)SetWindowLong(parentwnd, GWL_WNDPROC, (LONG)toolproc)
// This remembers the statusproc
// Instantiate and Create the Tree view Window
CTreeView tree;
tree.Create(parentwnd, ....);
// Inside of this create method we do this
OldWndProc = (WNDPROC)SetWindowLong(parentwnd, GWL_WNDPROC, (LONG)treeproc)
// This remembers the toolproc
So the wndproc chain goes like this
originalproc
we create the status window, chain goes like this
statusproc
originalproc
we create the tool window, chain goes like this
toolproc
statusproc
originalproc
we create the tree window, chain goes like this
treeproc
toolproc
statusproc
originalproc
Now if I destroy the status window it does this
SetWindowLong(parentwnd, GWL_WNDPROC, (long)OldWndProc) // which is originalproc
remember the staus window remembered originalproc
after this the chain goes like this
originalproc
we have effectively cut out the tool and tree windows.
So my question to you is, how can I solve this using good programming practice??
your help is appreciated,
thankyou.
Win32 Subclassing
Re:Win32 Subclassing
not destroying the current window procedure chain should be a simple matter of something lke this --
OldWndProc = (WNDPROC) GetWindowLongPtr(hwndParent, GWLP_WNDPROC);
SetWindowLongPtr(hwndParent, GWLP_WNDPROC, (LONG_PTR) StatusWndProc);
// when the window is destroyed
if (StatusWndProc == (WNDPROC) GetWindowLongPtr(hwndParent, GWLP_WNDPROC)
{
SetWindowLongPtr(hwndParent, GWLP_WNDPROC, OldWndProc);
}
The problem actually comes with the window that replaces the status bar's window procedure. It has no way of knowing that the status bar has been destroyed, and it continues to call the status bar's window procedure, and when it is destroyed it wants to put a destroyed window's window procedure into the wndproc chain.
You could try registering a new window message and sending it to the parent when a child that has subclassed it is destroyed (since all the various window procedures would get the message), but that might not always work if you have more than one of the same type of control (such as two tree views, edit boxes, etc.). Maybe you could pass the HWND of the control as one of the message parameters and have each window examine it before removing a dead window's procedure from the call chain. How about a pointer to a structure that contains the child's HWND, a pointer to the (being destroyed) child's window procedure, and the wndproc pointer that the child was saving?
case WM_DESTROY:
DESTROYSTRUCT ds;
ds.hwnd = this->hwnd;
ds.pfnInvalidWndProc = StatusWndProc;
ds.pfnValidWndProc = OldWndProc;
SendMesssage(......, (LPARAM) &ds));
I don't know...windows child control don't lend themselves to wrapping, so you've got to try some crazy crap to get it to work.
MFC uses a procedure that they call message reflection. Rather than children subclassing their parents, the parent sends unprocessed notification messages back to the control that sent them.
OldWndProc = (WNDPROC) GetWindowLongPtr(hwndParent, GWLP_WNDPROC);
SetWindowLongPtr(hwndParent, GWLP_WNDPROC, (LONG_PTR) StatusWndProc);
// when the window is destroyed
if (StatusWndProc == (WNDPROC) GetWindowLongPtr(hwndParent, GWLP_WNDPROC)
{
SetWindowLongPtr(hwndParent, GWLP_WNDPROC, OldWndProc);
}
The problem actually comes with the window that replaces the status bar's window procedure. It has no way of knowing that the status bar has been destroyed, and it continues to call the status bar's window procedure, and when it is destroyed it wants to put a destroyed window's window procedure into the wndproc chain.
You could try registering a new window message and sending it to the parent when a child that has subclassed it is destroyed (since all the various window procedures would get the message), but that might not always work if you have more than one of the same type of control (such as two tree views, edit boxes, etc.). Maybe you could pass the HWND of the control as one of the message parameters and have each window examine it before removing a dead window's procedure from the call chain. How about a pointer to a structure that contains the child's HWND, a pointer to the (being destroyed) child's window procedure, and the wndproc pointer that the child was saving?
case WM_DESTROY:
DESTROYSTRUCT ds;
ds.hwnd = this->hwnd;
ds.pfnInvalidWndProc = StatusWndProc;
ds.pfnValidWndProc = OldWndProc;
SendMesssage(......, (LPARAM) &ds));
I don't know...windows child control don't lend themselves to wrapping, so you've got to try some crazy crap to get it to work.
MFC uses a procedure that they call message reflection. Rather than children subclassing their parents, the parent sends unprocessed notification messages back to the control that sent them.
Re:Win32 Subclassing
of course, that method of not destroying the call chain shown above would only work if there were just one type of each control, as mentioned.
(and all the C-style casts should be reinterpret_casts, and I forgot to cast the last OldWndProc to a LONG_PTR).
(and all the C-style casts should be reinterpret_casts, and I forgot to cast the last OldWndProc to a LONG_PTR).
Re:Win32 Subclassing
one other thing is that you would have to make sure that messages intended for the parent do not get processed by the window procedure for the child, you wouldn't want a WM_KEYDOWN message intended for your parent window to be processed by an edit control's window procedure as if it were intended for the edit control.
I've never actually tried to do anything like this, so I'm not 100% sure of all the little things to look out for or if it will even work properly. Have you actually tried it out in a program?
I've never actually tried to do anything like this, so I'm not 100% sure of all the little things to look out for or if it will even work properly. Have you actually tried it out in a program?
Re:Win32 Subclassing
i've only ever subclassed in vb so cant comment on the win32 methodology of it...
-- Stu --
Re:Win32 Subclassing
Hi again,
Thanks for your replies. All windows that go through the window procedure have a class wrapper (all inherit from a common ancestor), and a pointer to an instance of that wrapper is saved as a window property and then the window handle is used to get the instance for that window which has a method designed for handling messages sent to it. So messages never get mixed up, the only problem that I have is getting my classes to handle WM_CREATE messages because this message is sent before CreateWindow() returns, so my windows dont have a pointer to the window stored yet so the procedure just lets windows do its default handling.
Does anyone know where I can get some information detailing how MFC works. This might give me some ideas which can be used as a basis for my own solution.
Thanks again for your help.
Thanks for your replies. All windows that go through the window procedure have a class wrapper (all inherit from a common ancestor), and a pointer to an instance of that wrapper is saved as a window property and then the window handle is used to get the instance for that window which has a method designed for handling messages sent to it. So messages never get mixed up, the only problem that I have is getting my classes to handle WM_CREATE messages because this message is sent before CreateWindow() returns, so my windows dont have a pointer to the window stored yet so the procedure just lets windows do its default handling.
Does anyone know where I can get some information detailing how MFC works. This might give me some ideas which can be used as a basis for my own solution.
Thanks again for your help.
Re:Win32 Subclassing
The MFC source code is provided with Visual C++. Another source of information is Jeff Prosise's "Programming Windows with MFC, Second Edition".
Incidentally, WM_CREATE is simply a problematic message (same with WM_NCCREATE, etc.) when you're talking about wrapping controls inside classes. MFC control classes don't even provide a way to handle the message. There may be a way to do it with a hook procedure (maybe), but it's probably more trouble than it's worth. You could just as well provide a virtual function called InitControlWindow or something like that in the base class for all Windows controls, then call it immediately after subclassing the window. I think that's what I did when I wrote my own wrapper classes for the controls.
Incidentally, WM_CREATE is simply a problematic message (same with WM_NCCREATE, etc.) when you're talking about wrapping controls inside classes. MFC control classes don't even provide a way to handle the message. There may be a way to do it with a hook procedure (maybe), but it's probably more trouble than it's worth. You could just as well provide a virtual function called InitControlWindow or something like that in the base class for all Windows controls, then call it immediately after subclassing the window. I think that's what I did when I wrote my own wrapper classes for the controls.
Re:Win32 Subclassing
Hi,
Thanks, I will check the source code out.
P.S I registered, I am the Same Person that Started this Thread.
Thanks, I will check the source code out.
P.S I registered, I am the Same Person that Started this Thread.