Derived combo box controls in forms - MessageBox.Show breaks the control's message routing

C

Clive Dixon

This is a follow on from a previous posting about this subject some time ago
by one of my colleagues. I have made some progress in understanding the
problem and post the results here in the hope of finding some answers.

We have a control class derived from System.Windows.Forms.ComboBox that
overrides WndProc. These controls are used in forms within components which
are used by an MFC application as COM components. The forms are displayed
within application toolbars. The problem is that whenever a .NET component
calls MessageBox.Show, the combo boxes break. The sequence of events appears
to be as follows:

1) Setting certain properties of child combo box controls in the form's
InitializeComponent will cause the child control window to be created there
and then, before the parent form window itself has been created. When such
child windows are first created, they are temporarily attached as child
windows to a window of type System.Windows.Forms.Application.ParkingWindow
called "WindowsFormsParkingWindow", which is itself owned by the desktop
window. This parking window is a singleton window which is apparently
created by .NET as and when required.

2) The parent form window is created either a) near the end of
InitializeComponent by a System.Windows.Forms.AxHost.EndInit call resulting
from a ISupportInitialize.EndInit call on an ActiveX control in the form or
b) as a result of System.Windows.Forms.Control.ActiveXImpl.InPlaceActivate.
This form window is also created as a child of the parking window.

3) The control windows are subsequently reassigned by .NET as children of
the form window and thus grandchildren of the parking window.


4) At a later stage the form windows are wired up by the application's
plug-in mechanisms as children of the toolbar windows, and the window
hierarchy looks as you would expect it to be.

5) However, the windows message routing is screwed up. Some messages
(apparently all the notification messages which go to the parent window,
e.g. the owner draw WM_DRAWITEM) are apparently still being routed by
System.Windows.Forms.NativeWindow.DefWndProc through the parking window's
wndproc as though the combo boxes were still children of the parking window.
(I'm still not clear on some of the fine detail here - it all seems a bit
voodoo. I don't understand, especially after having seen the decompiled
System.Windows.Forms.NativeWindow.DefWndProc code, how this can be
converting a WM_PAINT message to the combo box handle into a
System.Windows.Forms.NativeWindow.DebuggableCallback call with WM_DRAWITEM
message and the parking window handle.) By a lucky coincidence, the messages
manage to end up via message reflection back in the combo box where they are
processed correctly, and everything looks fine and dandy.

6) That is, until you bring up a message box. MessageBox.Show, after the box
has been dismissed, calls EndModalMessageLoop which destroys the parking
window.

7) And now you can guess what happens next.

Here is an example stack trace of the routing of a message (WM_PAINT to the
combo box ultimately resulting in a CBM_GETDROPDOWN), with annotations:

i2.windows.forms.dll!i2.Windows.Forms.ComboBox.WndProc(System.Windows.Forms.
Message m = {System.Windows.Forms.Message}) Line 378 C#

system.windows.forms.dll!ControlNativeWindow.OnMessage(System.Windows.Forms.
Message m = {System.Windows.Forms.Message}) + 0x19 bytes

system.windows.forms.dll!ControlNativeWindow.WndProc(System.Windows.Forms.Me
ssage m = {System.Windows.Forms.Message}) + 0xda bytes

system.windows.forms.dll!System.Windows.Forms.NativeWindow.DebuggableCallbac
k(int hWnd = 0x14070a, int msg = 0x157, int wparam = 0x0, int lparam = 0x0)
+ 0x39 bytes

system.windows.forms.dll!System.Windows.Forms.Control.SendMessage(int msg =
0x157, int wparam = 0x0, int lparam = 0x0) + 0x41 bytes

system.windows.forms.dll!System.Windows.Forms.ComboBox.get_DroppedDown() +
0x28 bytes

i2.windows.forms.dll!i2.Windows.Forms.ComboBox.DrawString(i2.Windows.Forms.C
omboBoxItem item = {i2.Windows.Forms.ComboBoxItem}, System.Drawing.Rectangle
bounds = {X=0x3 Y=0x3 Width=0x7b Height=0xf},
System.Windows.Forms.DrawItemEventArgs e =
{System.Windows.Forms.DrawItemEventArgs}) Line 1050 + 0x9 bytes C#

i2.windows.forms.dll!i2.Windows.Forms.ComboBox.OnDrawItem(System.Windows.For
ms.DrawItemEventArgs e = {System.Windows.Forms.DrawItemEventArgs}) Line 188
+ 0x1d bytes C#

system.windows.forms.dll!System.Windows.Forms.ComboBox.WmReflectDrawItem(Sys
tem.Windows.Forms.Message m = {System.Windows.Forms.Message}) + 0x158 bytes

system.windows.forms.dll!System.Windows.Forms.ComboBox.WndProc(System.Window
s.Forms.Message m = {System.Windows.Forms.Message}) + 0x23b bytes

i2.windows.forms.dll!i2.Windows.Forms.ComboBox.WndProc(System.Windows.Forms.
Message m = {System.Windows.Forms.Message}) Line 527 + 0xc bytes C#

system.windows.forms.dll!ControlNativeWindow.OnMessage(System.Windows.Forms.
Message m = {System.Windows.Forms.Message}) + 0x19 bytes

system.windows.forms.dll!ControlNativeWindow.WndProc(System.Windows.Forms.Me
ssage m = {System.Windows.Forms.Message}) + 0xda bytes

system.windows.forms.dll!System.Windows.Forms.NativeWindow.DebuggableCallbac
k(int hWnd = 0x14070a, int msg = 0x202b, int wparam = 0x14070a, int lparam =
0x12ce40) + 0x39 bytes

system.windows.forms.dll!System.Windows.Forms.Control.SendMessage(int msg =
0x202b, int wparam = 0x14070a, int lparam = 0x12ce40) + 0x41 bytes

REFLECTED MESSAGE COMES BACK TO THE COMBO BOX:

(ALL CALLS ABOVE USING THE COMBO BOX HANDLE)


system.windows.forms.dll!System.Windows.Forms.Control.ReflectMessageInternal
(int hWnd = 0x14070a, System.Windows.Forms.Message m =
{System.Windows.Forms.Message}) + 0x6f bytes

ALL CALLS BELOW USING THE PARKING WINDOW HANDLE:

system.windows.forms.dll!System.Windows.Forms.Control.WmOwnerDraw(System.Win
dows.Forms.Message m = {System.Windows.Forms.Message}) + 0x39 bytes

system.windows.forms.dll!System.Windows.Forms.Control.WmDrawItem(System.Wind
ows.Forms.Message m = {System.Windows.Forms.Message}) + 0x42 bytes

system.windows.forms.dll!System.Windows.Forms.Control.WndProc(System.Windows
..Forms.Message m = {System.Windows.Forms.Message}) + 0x27c bytes

system.windows.forms.dll!System.Windows.Forms.ScrollableControl.WndProc(Syst
em.Windows.Forms.Message m = {System.Windows.Forms.Message}) + 0x7d bytes

system.windows.forms.dll!System.Windows.Forms.ContainerControl.WndProc(Syste
m.Windows.Forms.Message m = {System.Windows.Forms.Message}) + 0x42 bytes

system.windows.forms.dll!System.Windows.Forms.Application.ParkingWindow.WndP
roc(System.Windows.Forms.Message m = {System.Windows.Forms.Message}) + 0x25
bytes

system.windows.forms.dll!ControlNativeWindow.OnMessage(System.Windows.Forms.
Message m = {System.Windows.Forms.Message}) + 0x19 bytes

system.windows.forms.dll!ControlNativeWindow.WndProc(System.Windows.Forms.Me
ssage m = {System.Windows.Forms.Message}) + 0xda bytes

WM_DRAWITEM MESSAGE TO THE PARKING WINDOW HANDLE:

(ALL MESSAGES ABOVE USING THE PARKING WINDOW HANDLE)

system.windows.forms.dll!System.Windows.Forms.NativeWindow.DebuggableCallbac
k(int hWnd = 0x1f06f4, int msg = 0x2b, int wparam = 0x14070a, int lparam =
0x12ce40) + 0x39 bytes

WM_PAINT MESSAGE TO THE COMBO BOX HANDLE:

(ALL MESSAGES BELOW USING THE COMBO BOX HANDLE)

system.windows.forms.dll!System.Windows.Forms.NativeWindow.DefWndProc(System
..Windows.Forms.Message m = {System.Windows.Forms.Message}) + 0xc2 bytes

system.windows.forms.dll!System.Windows.Forms.Control.DefWndProc(System.Wind
ows.Forms.Message m = {System.Windows.Forms.Message}) + 0x19 bytes

system.windows.forms.dll!System.Windows.Forms.Control.WndProc(System.Windows
..Forms.Message m = {System.Windows.Forms.Message}) + 0x2d7 bytes

system.windows.forms.dll!System.Windows.Forms.ComboBox.WndProc(System.Window
s.Forms.Message m = {System.Windows.Forms.Message}) + 0x4c9 bytes

i2.windows.forms.dll!i2.Windows.Forms.ComboBox.WndProc(System.Windows.Forms.
Message m = {System.Windows.Forms.Message}) Line 478 + 0xc bytes C#

system.windows.forms.dll!ControlNativeWindow.OnMessage(System.Windows.Forms.
Message m = {System.Windows.Forms.Message}) + 0x19 bytes

system.windows.forms.dll!ControlNativeWindow.WndProc(System.Windows.Forms.Me
ssage m = {System.Windows.Forms.Message}) + 0xda bytes

system.windows.forms.dll!System.Windows.Forms.NativeWindow.DebuggableCallbac
k(int hWnd = 0x14070a, int msg = 0xf, int wparam = 0x0, int lparam = 0x0) +
0x39 bytes



When I change the form code's InitializeComponent so that

a) the Parent property of the control is set to be the form object, and

b) the form's CreateControl is called

before anything happens which might cause the control windows to be created,
the problem does not occur. The call stack now looks like (part thereof):

CALL STACK ABOVE HERE OMITTED

system.windows.forms.dll!System.Windows.Forms.Control.ActiveXImpl.System.Win
dows.Forms.IWindowTarget.OnMessage(System.Windows.Forms.Message m =
{System.Windows.Forms.Message}) + 0xb8 bytes

system.windows.forms.dll!ControlNativeWindow.WndProc(System.Windows.Forms.Me
ssage m = {System.Windows.Forms.Message}) + 0xda bytes

WM_DRAWITEM MESSAGE TO THE FORM WINDOW HANDLE

system.windows.forms.dll!System.Windows.Forms.NativeWindow.DebuggableCallbac
k(int hWnd = 0x105ee, int msg = 0x2b, int wparam = 0x205fc, int lparam =
0x12ed80) + 0x39 bytes

system.windows.forms.dll!System.Windows.Forms.NativeWindow.DefWndProc(System
..Windows.Forms.Message m = {System.Windows.Forms.Message}) + 0xc2 bytes

system.windows.forms.dll!System.Windows.Forms.Control.DefWndProc(System.Wind
ows.Forms.Message m = {System.Windows.Forms.Message}) + 0x19 bytes

system.windows.forms.dll!System.Windows.Forms.Control.WndProc(System.Windows
..Forms.Message m = {System.Windows.Forms.Message}) + 0x2d7 bytes

system.windows.forms.dll!System.Windows.Forms.ComboBox.WndProc(System.Window
s.Forms.Message m = {System.Windows.Forms.Message}) + 0x4c9 bytes

i2.windows.forms.dll!i2.Windows.Forms.ComboBox.WndProc(System.Windows.Forms.
Message m = {System.Windows.Forms.Message}) Line 463 C#

system.windows.forms.dll!ControlNativeWindow.OnMessage(System.Windows.Forms.
Message m = {System.Windows.Forms.Message}) + 0x19 bytes

system.windows.forms.dll!ControlNativeWindow.WndProc(System.Windows.Forms.Me
ssage m = {System.Windows.Forms.Message}) + 0xda bytes

system.windows.forms.dll!System.Windows.Forms.NativeWindow.DebuggableCallbac
k(int hWnd = 0x205fc, int msg = 0xf, int wparam = 0x0, int lparam = 0x0) +
0x39 bytes

the point being that System.Windows.Forms.NativeWindow.DebuggableCallback is
now being called with the form handle rather than the parking window handle,
and the messaging is now routed through
System.Windows.Forms.Control.ActiveXImpl.System.Windows.Forms.IWindowTarget.
OnMessage which was not happening before.

However a workaround in this way is simply not an acceptable solution to our
problem. We need a solution in which the combo boxes do not break in this
way regardless of when the controls are actually created, and not having to
mess around with order of operations and explicit parent setting and form
creation.
 

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