Controls won't get resized once the nesting hierarchy of windows exceeds a certain depth (x64)
Hi all, welcome back,
I've been working on an issue where WM_SIZE events are not properly generated once the nesting hierarchy of windows exceeds a certain depth. This issue only occurs on current x64 Windows: like XP, Server 2008 or the latest Windows 7.
For ilustration purposes, let's imagine we have a C# application which creates a hierarchy of nested panels. Panels' OnSize handler resize their child panel so that it has the same size as the parent panel minus a border frame:
protected override void OnSizeChanged(System.EventArgs ea)
{
if (childPanel != null)
{
childPanel.Size = new Size(
this.ClientSize.Width - 2 * childPanel.Left,
this.ClientSize.Height - 2 * childPanel.Top);
}
base.OnResize(ea);
}
We have around 30 nested panels. If we resize the main dialog, only the first 12-15 panels will get resized along with it. The OnSizeChanged method of the smaller panels is not getting called at all. On x86, all panels get resized, though.
Well, this behavior that we are experiencing is a design limitation on Windows kernel. Let's see this in greater detail.
In this sample application, this is how the call stack looks after several calls to WindowsApplication1!WindowsApplication1.Form1+MyPanel.OnSizeChanged:
0:000> kL100
Child-SP RetAddr Call Site
00000000`002589f8 00000642`7605eb0b WindowsApplication1!WindowsApplication1.Form1+MyPanel.OnSizeChanged(System.EventArgs)
00000000`00258a00 00000642`7605e77c System_Windows_Forms_ni!System.Windows.Forms.Control.UpdateBounds(Int32, Int32, Int32, Int32, Int32, Int32)+0x10b
00000000`00258a80 00000642`7606fcbc System_Windows_Forms_ni!System.Windows.Forms.Control.UpdateBounds()+0x2bc
00000000`00258ba0 00000642`7605d44b System_Windows_Forms_ni!System.Windows.Forms.Control.WmWindowPosChanged(System.Windows.Forms.Message ByRef)+0x3c
00000000`00258c10 00000642`7605d182 System_Windows_Forms_ni!System.Windows.Forms.Control.WndProc(System.Windows.Forms.Message ByRef)+0x27b
00000000`00258dc0 00000642`7605cff5 System_Windows_Forms_ni!System.Windows.Forms.Control+ControlNativeWindow.WndProc(System.Windows.Forms.Message ByRef)+0x52
00000000`00258e10 00000642`76278bd9 System_Windows_Forms_ni!System.Windows.Forms.NativeWindow.Callback(IntPtr, Int32, IntPtr, IntPtr)+0xb5
00000000`00258ec0 00000642`7f67945a System_Windows_Forms_ni!DomainBoundILStubClass.IL_STUB(Int64, Int32, Int64, Int64)+0x29
00000000`00258f00 00000000`76f73ee2 mscorwks!UMThunkStubAMD64+0x7a
00000000`00258f90 00000000`76f7bf9b USER32!UserCallWinProcCheckWow+0x163
00000000`00259050 00000000`76f73789 USER32!DispatchClientMessage+0xc3
00000000`002590b0 00000000`770759a6 USER32!__fnINLPWINDOWPOS+0x2d
00000000`00259110 00000000`76f73a5a ntdll!KiUserCallbackDispatcherContinue
00000000`002591b8 00000642`7f67b167 USER32!ZwUserSetWindowPos+0xa
00000000`002591c0 00000642`7606fbd3 mscorwks!DoNDirectCall__PatchGetThreadCall+0x7b
00000000`00259280 00000642`76067ae1 System_Windows_Forms_ni!DomainBoundILStubClass.IL_STUB(System.Runtime.InteropServices.HandleRef, System.Runtime.InteropServices.HandleRef, Int32, Int32, Int32, Int32, Int32)+0xa3
00000000`00259360 00000642`7606764e System_Windows_Forms_ni!System.Windows.Forms.Control.SetBoundsCore(Int32, Int32, Int32, Int32, System.Windows.Forms.BoundsSpecified)+0x251
00000000`00259470 00000642`76067423 System_Windows_Forms_ni!System.Windows.Forms.Control.SetBounds(Int32, Int32, Int32, Int32, System.Windows.Forms.BoundsSpecified)+0xae
00000000`002594e0 00000642`80150b74 System_Windows_Forms_ni!System.Windows.Forms.Control.set_Size(System.Drawing.Size)+0x33
00000000`00259520 00000642`7605eb0b WindowsApplication1!WindowsApplication1.Form1+MyPanel.OnSizeChanged(System.EventArgs)+0x1d4
00000000`002595e0 00000642`7605e77c System_Windows_Forms_ni!System.Windows.Forms.Control.UpdateBounds(Int32, Int32, Int32, Int32, Int32, Int32)+0x10b
00000000`00259660 00000642`7606fcbc System_Windows_Forms_ni!System.Windows.Forms.Control.UpdateBounds()+0x2bc
00000000`00259780 00000642`7605d44b System_Windows_Forms_ni!System.Windows.Forms.Control.WmWindowPosChanged(System.Windows.Forms.Message ByRef)+0x3c
00000000`002597f0 00000642`7605d182 System_Windows_Forms_ni!System.Windows.Forms.Control.WndProc(System.Windows.Forms.Message ByRef)+0x27b
00000000`002599a0 00000642`7605cff5 System_Windows_Forms_ni!System.Windows.Forms.Control+ControlNativeWindow.WndProc(System.Windows.Forms.Message ByRef)+0x52
00000000`002599f0 00000642`76278bd9 System_Windows_Forms_ni!System.Windows.Forms.NativeWindow.Callback(IntPtr, Int32, IntPtr, IntPtr)+0xb5
00000000`00259aa0 00000642`7f67945a System_Windows_Forms_ni!DomainBoundILStubClass.IL_STUB(Int64, Int32, Int64, Int64)+0x29
00000000`00259ae0 00000000`76f73ee2 mscorwks!UMThunkStubAMD64+0x7a
00000000`00259b70 00000000`76f7bf9b USER32!UserCallWinProcCheckWow+0x163
00000000`00259c30 00000000`76f73789 USER32!DispatchClientMessage+0xc3
00000000`00259c90 00000000`770759a6 USER32!__fnINLPWINDOWPOS+0x2d
00000000`00259cf0 00000000`76f73a5a ntdll!KiUserCallbackDispatcherContinue
00000000`00259d98 00000642`7f67b167 USER32!ZwUserSetWindowPos+0xa
00000000`00259da0 00000642`7606fbd3 mscorwks!DoNDirectCall__PatchGetThreadCall+0x7b
00000000`00259e60 00000642`76067ae1 System_Windows_Forms_ni!DomainBoundILStubClass.IL_STUB(System.Runtime.InteropServices.HandleRef, System.Runtime.InteropServices.HandleRef, Int32, Int32, Int32, Int32, Int32)+0xa3
00000000`00259f40 00000642`7606764e System_Windows_Forms_ni!System.Windows.Forms.Control.SetBoundsCore(Int32, Int32, Int32, Int32, System.Windows.Forms.BoundsSpecified)+0x251
00000000`0025a050 00000642`76067423 System_Windows_Forms_ni!System.Windows.Forms.Control.SetBounds(Int32, Int32, Int32, Int32, System.Windows.Forms.BoundsSpecified)+0xae
00000000`0025a0c0 00000642`80150b74 System_Windows_Forms_ni!System.Windows.Forms.Control.set_Size(System.Drawing.Size)+0x33
00000000`0025a100 00000642`7605eb0b WindowsApplication1!WindowsApplication1.Form1+MyPanel.OnSizeChanged(System.EventArgs)+0x1d4
00000000`0025a1c0 00000642`7605e77c System_Windows_Forms_ni!System.Windows.Forms.Control.UpdateBounds(Int32, Int32, Int32, Int32, Int32, Int32)+0x10b
00000000`0025a240 00000642`7606fcbc System_Windows_Forms_ni!System.Windows.Forms.Control.UpdateBounds()+0x2bc
00000000`0025a360 00000642`7605d44b System_Windows_Forms_ni!System.Windows.Forms.Control.WmWindowPosChanged(System.Windows.Forms.Message ByRef)+0x3c
00000000`0025a3d0 00000642`7605d182 System_Windows_Forms_ni!System.Windows.Forms.Control.WndProc(System.Windows.Forms.Message ByRef)+0x27b
00000000`0025a580 00000642`7605cff5 System_Windows_Forms_ni!System.Windows.Forms.Control+ControlNativeWindow.WndProc(System.Windows.Forms.Message ByRef)+0x52
00000000`0025a5d0 00000642`76278bd9 System_Windows_Forms_ni!System.Windows.Forms.NativeWindow.Callback(IntPtr, Int32, IntPtr, IntPtr)+0xb5
00000000`0025a680 00000642`7f67945a System_Windows_Forms_ni!DomainBoundILStubClass.IL_STUB(Int64, Int32, Int64, Int64)+0x29
00000000`0025a6c0 00000000`76f73ee2 mscorwks!UMThunkStubAMD64+0x7a
00000000`0025a750 00000000`76f7bf9b USER32!UserCallWinProcCheckWow+0x163
00000000`0025a810 00000000`76f73789 USER32!DispatchClientMessage+0xc3
00000000`0025a870 00000000`770759a6 USER32!__fnINLPWINDOWPOS+0x2d
00000000`0025a8d0 00000000`76f73a5a ntdll!KiUserCallbackDispatcherContinue
00000000`0025a978 00000642`7f67b167 USER32!ZwUserSetWindowPos+0xa
00000000`0025a980 00000642`7606fbd3 mscorwks!DoNDirectCall__PatchGetThreadCall+0x7b
00000000`0025aa40 00000642`76067ae1 System_Windows_Forms_ni!DomainBoundILStubClass.IL_STUB(System.Runtime.InteropServices.HandleRef, System.Runtime.InteropServices.HandleRef, Int32, Int32, Int32, Int32, Int32)+0xa3
00000000`0025ab20 00000642`7606764e System_Windows_Forms_ni!System.Windows.Forms.Control.SetBoundsCore(Int32, Int32, Int32, Int32, System.Windows.Forms.BoundsSpecified)+0x251
00000000`0025ac30 00000642`76067423 System_Windows_Forms_ni!System.Windows.Forms.Control.SetBounds(Int32, Int32, Int32, Int32, System.Windows.Forms.BoundsSpecified)+0xae
00000000`0025aca0 00000642`80150b74 System_Windows_Forms_ni!System.Windows.Forms.Control.set_Size(System.Drawing.Size)+0x33
00000000`0025ace0 00000642`7605eb0b WindowsApplication1!WindowsApplication1.Form1+MyPanel.OnSizeChanged(System.EventArgs)+0x1d4
00000000`0025ada0 00000642`7605e77c System_Windows_Forms_ni!System.Windows.Forms.Control.UpdateBounds(Int32, Int32, Int32, Int32, Int32, Int32)+0x10b
00000000`0025ae20 00000642`7606fcbc System_Windows_Forms_ni!System.Windows.Forms.Control.UpdateBounds()+0x2bc
00000000`0025af40 00000642`7605d44b System_Windows_Forms_ni!System.Windows.Forms.Control.WmWindowPosChanged(System.Windows.Forms.Message ByRef)+0x3c
00000000`0025afb0 00000642`7605d182 System_Windows_Forms_ni!System.Windows.Forms.Control.WndProc(System.Windows.Forms.Message ByRef)+0x27b
00000000`0025b160 00000642`7605cff5 System_Windows_Forms_ni!System.Windows.Forms.Control+ControlNativeWindow.WndProc(System.Windows.Forms.Message ByRef)+0x52
00000000`0025b1b0 00000642`76278bd9 System_Windows_Forms_ni!System.Windows.Forms.NativeWindow.Callback(IntPtr, Int32, IntPtr, IntPtr)+0xb5
00000000`0025b260 00000642`7f67945a System_Windows_Forms_ni!DomainBoundILStubClass.IL_STUB(Int64, Int32, Int64, Int64)+0x29
00000000`0025b2a0 00000000`76f73ee2 mscorwks!UMThunkStubAMD64+0x7a
00000000`0025b330 00000000`76f7bf9b USER32!UserCallWinProcCheckWow+0x163
00000000`0025b3f0 00000000`76f73789 USER32!DispatchClientMessage+0xc3
00000000`0025b450 00000000`770759a6 USER32!__fnINLPWINDOWPOS+0x2d
00000000`0025b4b0 00000000`76f73a5a ntdll!KiUserCallbackDispatcherContinue
00000000`0025b558 00000642`7f67b167 USER32!ZwUserSetWindowPos+0xa
00000000`0025b560 00000642`7606fbd3 mscorwks!DoNDirectCall__PatchGetThreadCall+0x7b
00000000`0025b620 00000642`76067ae1 System_Windows_Forms_ni!DomainBoundILStubClass.IL_STUB(System.Runtime.InteropServices.HandleRef, System.Runtime.InteropServices.HandleRef, Int32, Int32, Int32, Int32, Int32)+0xa3
00000000`0025b700 00000642`7606764e System_Windows_Forms_ni!System.Windows.Forms.Control.SetBoundsCore(Int32, Int32, Int32, Int32, System.Windows.Forms.BoundsSpecified)+0x251
00000000`0025b810 00000642`76067423 System_Windows_Forms_ni!System.Windows.Forms.Control.SetBounds(Int32, Int32, Int32, Int32, System.Windows.Forms.BoundsSpecified)+0xae
00000000`0025b880 00000642`80150b74 System_Windows_Forms_ni!System.Windows.Forms.Control.set_Size(System.Drawing.Size)+0x33
00000000`0025b8c0 00000642`7605eb0b WindowsApplication1!WindowsApplication1.Form1+MyPanel.OnSizeChanged(System.EventArgs)+0x1d4
00000000`0025b980 00000642`7605e77c System_Windows_Forms_ni!System.Windows.Forms.Control.UpdateBounds(Int32, Int32, Int32, Int32, Int32, Int32)+0x10b
00000000`0025ba00 00000642`7606fcbc System_Windows_Forms_ni!System.Windows.Forms.Control.UpdateBounds()+0x2bc
00000000`0025bb20 00000642`7605d44b System_Windows_Forms_ni!System.Windows.Forms.Control.WmWindowPosChanged(System.Windows.Forms.Message ByRef)+0x3c
00000000`0025bb90 00000642`7605d182 System_Windows_Forms_ni!System.Windows.Forms.Control.WndProc(System.Windows.Forms.Message ByRef)+0x27b
00000000`0025bd40 00000642`7605cff5 System_Windows_Forms_ni!System.Windows.Forms.Control+ControlNativeWindow.WndProc(System.Windows.Forms.Message ByRef)+0x52
00000000`0025bd90 00000642`76278bd9 System_Windows_Forms_ni!System.Windows.Forms.NativeWindow.Callback(IntPtr, Int32, IntPtr, IntPtr)+0xb5
00000000`0025be40 00000642`7f67945a System_Windows_Forms_ni!DomainBoundILStubClass.IL_STUB(Int64, Int32, Int64, Int64)+0x29
00000000`0025be80 00000000`76f73ee2 mscorwks!UMThunkStubAMD64+0x7a
00000000`0025bf10 00000000`76f7bf9b USER32!UserCallWinProcCheckWow+0x163
00000000`0025bfd0 00000000`76f73789 USER32!DispatchClientMessage+0xc3
00000000`0025c030 00000000`770759a6 USER32!__fnINLPWINDOWPOS+0x2d
00000000`0025c090 00000000`76f73a5a ntdll!KiUserCallbackDispatcherContinue
00000000`0025c138 00000642`7f67b167 USER32!ZwUserSetWindowPos+0xa
00000000`0025c140 00000642`7606fbd3 mscorwks!DoNDirectCall__PatchGetThreadCall+0x7b
00000000`0025c200 00000642`76067ae1 System_Windows_Forms_ni!DomainBoundILStubClass.IL_STUB(System.Runtime.InteropServices.HandleRef, System.Runtime.InteropServices.HandleRef, Int32, Int32, Int32, Int32, Int32)+0xa3
00000000`0025c2e0 00000642`7604fb99 System_Windows_Forms_ni!System.Windows.Forms.Control.SetBoundsCore(Int32, Int32, Int32, Int32, System.Windows.Forms.BoundsSpecified)+0x251
00000000`0025c3f0 00000642`7606d3bf System_Windows_Forms_ni!System.Windows.Forms.Control.System.Windows.Forms.Layout.IArrangedElement.SetBounds(System.Drawing.Rectangle, System.Windows.Forms.BoundsSpecified)+0x249
00000000`0025c490 00000642`76069f84 System_Windows_Forms_ni!System.Windows.Forms.Layout.DefaultLayout.ApplyCachedBounds(System.Windows.Forms.Layout.IArrangedElement)+0x24f
00000000`0025c580 00000642`76069d64 System_Windows_Forms_ni!System.Windows.Forms.Layout.DefaultLayout.xLayout(System.Windows.Forms.Layout.IArrangedElement, Boolean, System.Drawing.Size ByRef)+0x204
00000000`0025c6c0 00000642`76069ce6 System_Windows_Forms_ni!System.Windows.Forms.Layout.DefaultLayout.LayoutCore(System.Windows.Forms.Layout.IArrangedElement, System.Windows.Forms.LayoutEventArgs)+0x24
00000000`0025c700 00000642`76069b9d System_Windows_Forms_ni!System.Windows.Forms.Layout.LayoutEngine.Layout(System.Object, System.Windows.Forms.LayoutEventArgs)+0x26
00000000`0025c740 00000642`7604b182 System_Windows_Forms_ni!System.Windows.Forms.Control.OnLayout(System.Windows.Forms.LayoutEventArgs)+0xad
00000000`0025c780 00000642`76069278 System_Windows_Forms_ni!System.Windows.Forms.Form.OnLayout(System.Windows.Forms.LayoutEventArgs)+0x52
00000000`0025c800 00000642`760690d1 System_Windows_Forms_ni!System.Windows.Forms.Control.PerformLayout(System.Windows.Forms.LayoutEventArgs)+0x118
00000000`0025c8c0 00000642`7604bf98 System_Windows_Forms_ni!System.Windows.Forms.Control.OnResize(System.EventArgs)+0x141
00000000`0025c920 00000642`7604c0d2 System_Windows_Forms_ni!System.Windows.Forms.Form.OnResize(System.EventArgs)+0x18
00000000`0025c960 00000642`7605eb0b System_Windows_Forms_ni!System.Windows.Forms.Control.OnSizeChanged(System.EventArgs)+0x32
00000000`0025c9a0 00000642`7605e77c System_Windows_Forms_ni!System.Windows.Forms.Control.UpdateBounds(Int32, Int32, Int32, Int32, Int32, Int32)+0x10b
00000000`0025ca20 00000642`7606fcbc System_Windows_Forms_ni!System.Windows.Forms.Control.UpdateBounds()+0x2bc
00000000`0025cb40 00000642`7605d44b System_Windows_Forms_ni!System.Windows.Forms.Control.WmWindowPosChanged(System.Windows.Forms.Message ByRef)+0x3c
00000000`0025cbb0 00000642`7610e197 System_Windows_Forms_ni!System.Windows.Forms.Control.WndProc(System.Windows.Forms.Message ByRef)+0x27b
00000000`0025cd60 00000642`7605d182 System_Windows_Forms_ni!System.Windows.Forms.Form.WmWindowPosChanged(System.Windows.Forms.Message ByRef)+0x27
00000000`0025cda0 00000642`7605cff5 System_Windows_Forms_ni!System.Windows.Forms.Control+ControlNativeWindow.WndProc(System.Windows.Forms.Message ByRef)+0x52
00000000`0025cdf0 00000642`76278bd9 System_Windows_Forms_ni!System.Windows.Forms.NativeWindow.Callback(IntPtr, Int32, IntPtr, IntPtr)+0xb5
00000000`0025cea0 00000642`7f67945a System_Windows_Forms_ni!DomainBoundILStubClass.IL_STUB(Int64, Int32, Int64, Int64)+0x29
00000000`0025cee0 00000000`76f73ee2 mscorwks!UMThunkStubAMD64+0x7a
00000000`0025cf70 00000000`76f7bf9b USER32!UserCallWinProcCheckWow+0x163
00000000`0025d030 00000000`76f73789 USER32!DispatchClientMessage+0xc3
00000000`0025d090 00000000`770759a6 USER32!__fnINLPWINDOWPOS+0x2d
00000000`0025d0f0 00000000`76f7ac4a ntdll!KiUserCallbackDispatcherContinue
00000000`0025d198 00000000`76f7ac94 USER32!ZwUserMessageCall+0xa
00000000`0025d1a0 00000000`76f7e026 USER32!RealDefWindowProcWorker+0xb1
00000000`0025d270 000007fe`fbfb30d6 USER32!RealDefWindowProcW+0x5a
00000000`0025d2b0 000007fe`fbfcddf2 uxtheme!DoMsgDefault+0x2a
00000000`0025d2e0 000007fe`fbfb6c8a uxtheme!OnDwpSysCommand+0x50
00000000`0025d310 000007fe`fbfb1711 uxtheme!_ThemeDefWindowProc+0x223
00000000`0025d3e0 00000000`76f7bb73 uxtheme!ThemeDefWindowProcW+0x11
00000000`0025d420 00000000`76f7d24a USER32!DefWindowProcW+0xe6
00000000`0025d470 00000000`76f7d80c USER32!UserCallWinProcCheckWow+0x1ad
00000000`0025d530 00000000`76f7d778 USER32!CallWindowProcAorW+0xdb
00000000`0025d580 00000642`7f67b382 USER32!CallWindowProcW+0x18
00000000`0025d5c0 00000642`7605e378 mscorwks!DoNDirectCallWorker+0x62
00000000`0025d660 00000642`760473f4 System_Windows_Forms_ni!System.Windows.Forms.NativeWindow.DefWndProc(System.Windows.Forms.Message ByRef)+0xa8
00000000`0025d750 00000642`7605da52 System_Windows_Forms_ni!System.Windows.Forms.Form.DefWndProc(System.Windows.Forms.Message ByRef)+0x74
00000000`0025d840 00000642`7605d182 System_Windows_Forms_ni!System.Windows.Forms.Control.WndProc(System.Windows.Forms.Message ByRef)+0x882
00000000`0025d9f0 00000642`7605cff5 System_Windows_Forms_ni!System.Windows.Forms.Control+ControlNativeWindow.WndProc(System.Windows.Forms.Message ByRef)+0x52
00000000`0025da40 00000642`76278bd9 System_Windows_Forms_ni!System.Windows.Forms.NativeWindow.Callback(IntPtr, Int32, IntPtr, IntPtr)+0xb5
00000000`0025daf0 00000642`7f67945a System_Windows_Forms_ni!DomainBoundILStubClass.IL_STUB(Int64, Int32, Int64, Int64)+0x29
00000000`0025db30 00000000`76f73ee2 mscorwks!UMThunkStubAMD64+0x7a
00000000`0025dbc0 00000000`76f7bf9b USER32!UserCallWinProcCheckWow+0x163
00000000`0025dc80 00000000`76f7c20d USER32!DispatchClientMessage+0xc3
00000000`0025dce0 00000000`770759a6 USER32!__fnDWORD+0x2d
00000000`0025dd40 00000000`76f7ac4a ntdll!KiUserCallbackDispatcherContinue
00000000`0025ddc8 00000000`76f7ac94 USER32!ZwUserMessageCall+0xa
00000000`0025ddd0 00000000`76f7e026 USER32!RealDefWindowProcWorker+0xb1
00000000`0025dea0 000007fe`fbfb30d6 USER32!RealDefWindowProcW+0x5a
00000000`0025dee0 000007fe`fbfcedf5 uxtheme!DoMsgDefault+0x2a
00000000`0025df10 000007fe`fbfb6c8a uxtheme!OnDwpNcLButtonDown+0x71
00000000`0025df50 000007fe`fbfb1711 uxtheme!_ThemeDefWindowProc+0x223
00000000`0025e020 00000000`76f7bb73 uxtheme!ThemeDefWindowProcW+0x11
00000000`0025e060 00000000`76f7d24a USER32!DefWindowProcW+0xe6
00000000`0025e0b0 00000000`76f7d80c USER32!UserCallWinProcCheckWow+0x1ad
00000000`0025e170 00000000`76f7d778 USER32!CallWindowProcAorW+0xdb
00000000`0025e1c0 00000642`7f67b382 USER32!CallWindowProcW+0x18
00000000`0025e200 00000642`7605e378 mscorwks!DoNDirectCallWorker+0x62
00000000`0025e2a0 00000642`760473f4 System_Windows_Forms_ni!System.Windows.Forms.NativeWindow.DefWndProc(System.Windows.Forms.Message ByRef)+0xa8
00000000`0025e390 00000642`7605d7a0 System_Windows_Forms_ni!System.Windows.Forms.Form.DefWndProc(System.Windows.Forms.Message ByRef)+0x74
00000000`0025e480 00000642`7605d182 System_Windows_Forms_ni!System.Windows.Forms.Control.WndProc(System.Windows.Forms.Message ByRef)+0x5d0
00000000`0025e630 00000642`7605cff5 System_Windows_Forms_ni!System.Windows.Forms.Control+ControlNativeWindow.WndProc(System.Windows.Forms.Message ByRef)+0x52
00000000`0025e680 00000642`76278bd9 System_Windows_Forms_ni!System.Windows.Forms.NativeWindow.Callback(IntPtr, Int32, IntPtr, IntPtr)+0xb5
00000000`0025e730 00000642`7f67945a System_Windows_Forms_ni!DomainBoundILStubClass.IL_STUB(Int64, Int32, Int64, Int64)+0x29
00000000`0025e770 00000000`76f7d24a mscorwks!UMThunkStubAMD64+0x7a
00000000`0025e800 00000000`76f7d39e USER32!UserCallWinProcCheckWow+0x1ad
00000000`0025e8c0 00000642`7f67b167 USER32!DispatchMessageWorker+0x389
00000000`0025e940 00000642`7607f1fd mscorwks!DoNDirectCall__PatchGetThreadCall+0x7b
00000000`0025e9e0 00000642`7607dfb4 System_Windows_Forms_ni!DomainBoundILStubClass.IL_STUB(MSG ByRef)+0x11d
00000000`0025eb60 00000642`7607d7de System_Windows_Forms_ni!System.Windows.Forms.Application+ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32, Int32, Int32)+0x604
00000000`0025edb0 00000642`7607d20d System_Windows_Forms_ni!System.Windows.Forms.Application+ThreadContext.RunMessageLoopInner(Int32, System.Windows.Forms.ApplicationContext)+0x59e
00000000`0025ef00 00000642`80150178 System_Windows_Forms_ni!System.Windows.Forms.Application+ThreadContext.RunMessageLoop(Int32, System.Windows.Forms.ApplicationContext)+0x6d
00000000`0025ef60 00000642`7f67ba32 WindowsApplication1!WindowsApplication1.Program.Main()+0x58
00000000`0025efa0 00000642`7f4bc645 mscorwks!CallDescrWorker+0x82
00000000`0025efe0 00000642`7f4d0496 mscorwks!CallDescrWorkerWithHandler+0xe5
00000000`0025f080 00000642`7f5aee5f mscorwks!MethodDesc::CallDescr+0x306
00000000`0025f2b0 00000642`7f5d3ba4 mscorwks!ClassLoader::RunMain+0x23f
00000000`0025f510 00000642`7f59acfa mscorwks!Assembly::ExecuteMainMethod+0xbc
00000000`0025f800 00000642`7f5e16c3 mscorwks!SystemDomain::ExecuteMainMethod+0x492
00000000`0025fdd0 00000642`7f5c641c mscorwks!ExecuteEXE+0x47
00000000`0025fe20 00000642`7ee69ade mscorwks!_CorExeMain+0xac
00000000`0025fe80 00000000`76e5495d mscoree!_CorExeMain+0x3e
00000000`0025feb0 00000000`77058791 KERNEL32!BaseThreadInitThunk+0xd
00000000`0025fee0 00000000`00000000 ntdll!RtlUserThreadStart+0x1d
A call to SetWindowPos API results in a kernel-to-usermode callback to the target WndProc in the case where the thread owns the target HWND. After enough nesting of multiple callbacks, the kernel will just stop making recursive calls to WndProcs and window messages will be dropped.
To understand this better, if we check previous call stack in more detail, we can see what we mean by "SetWindowPos results in a kernel-to-usermode callback":
0:000> kL100
Child-SP RetAddr Call Site
00000000`002589f8 00000642`7605eb0b WindowsApplication1!WindowsApplication1.Form1+MyPanel.OnSizeChanged(System.EventArgs)
...
00000000`00258dc0 00000642`7605cff5 System_Windows_Forms_ni!System.Windows.Forms.Control+ControlNativeWindow.WndProc(System.Windows.Forms.Message ByRef)+0x52
...
00000000`00259110 00000000`76f73a5a ntdll!KiUserCallbackDispatcherContinue
00000000`002591b8 00000642`7f67b167 USER32!ZwUserSetWindowPos+0xa
...
00000000`002594e0 00000642`80150b74 System_Windows_Forms_ni!System.Windows.Forms.Control.set_Size(System.Drawing.Size)+0x33
00000000`00259520 00000642`7605eb0b WindowsApplication1!WindowsApplication1.Form1+MyPanel.OnSizeChanged(System.EventArgs)+0x1d4
...
...
Basically, we get to OnSizeChanged in parent panel. Parent panel calls set_Size on its child panel. This produces a call to SetWindowsPos API (USER32!ZwUserSetWindowPos) which produces a kernel-to-usermode callback (ntdll!KiUserCallbackDispatcherContinue) to the WndProc of the child panel which finally calls OnSizeChanged on that child panel. And so on. After enough nesting of these callbacks in the same thread, the kernel will just stop making recursive calls to WndProcs.
So why is this happening, if current default nested window limit is 50 on Windows platforms?
The root cause of the issue is available kernel stack space. In user mode, if recursive calls to a function are made enough times you will end up with a stack overflow exception. The same thing can happen in kernel mode. In this case, the application is calling SetWindowPos to resize a window. The window handles the WM_SIZE message by resizing its child windows. This operation is done recursively for however deep the window hierarchy is.
A SetWindowPos call will transition into kernel mode in order to make changes to the specified window’s position. There is then a callback from kernel mode into user mode to call the window procedure of the window to process the WM_WINDOWPOSCHANGING/WM_WINDOWPOSCHANGED messages. Once the messages are handled, the SetWindowPos call returns.
Ultimately what happens is that a stack overflow exception is generated in kernel mode. Rather than not handling the exception (which would "blue-screen" the machine) the window manager handles the exception and returns from the SetWindowsPos call. This problem is not limited to x64 Windows as it can also occur on x86 Windows, although it would take a deeper window hierarchy for the problem to occur in x86 Windows. Why the difference? Well, the size of pointers doubled from 32-bit to 64-bit and the size of the kernel mode stack did not.
Note that this is not a bug, but a limitation. The fact of the matter is that, even if we were to increase the kernel stack size in the future, applications do not have infinite kernel stack space available. These are the only alternatives we have to deal with this:
1) Redesign the application to have less containers and reduce the number of nesting levels.
2) In order to workaround this limitation, the application has to break nesting by asynchronously setting sizes on the child control from the last control which receives the message.
2.1) In our sample application we can change OnSizeChanged method above to:
protected void ChangeSize()
{
if (childPanel != null)
{
childPanel.Size = new Size(
this.ClientSize.Width - 2 * childPanel.Left,
this.ClientSize.Height - 2 * childPanel.Top);
}
}
protected override void OnSizeChanged(System.EventArgs ea)
{
if (this.Handle != null)
{
BeginInvoke(new MethodInvoker(ChangeSize))
}
base.OnResize(ea);
}
All panels get their size changed. Thanks to BeginInvoke only one call to OnSizeChanged happens in the same thread at a given time, and that breaks the nesting of multiple kernel-to-user mode callbacks within the same thread.
2.2) Another way I found to break the nesting of callbacks which may help but I haven't tried personally:
1. Select a window with simple layout of children (for example: single child - a panel with DockStyle.Fill) which will be breaking your recursion . (Not necessarily the last window which receives the positioning message)
2. Override WndProc for the window like this:
internal class MyTabPage : TabPage
{
private const int WM_WINDOWPOSCHANGING = 70;
private const int WM_SETREDRAW = 0xB;
private const int SWP_NOACTIVATE = 0x0010;
private const int SWP_NOZORDER = 0x0004;
private const int SWP_NOSIZE = 0x0001;
private const int SWP_NOMOVE = 0x0002;
[DllImport("User32.dll", CharSet=CharSet.Auto)]
public static extern int SendMessage(HandleRef hWnd, int msg, int wParam, int lParam);
[DllImport("User32.dll", ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)]
public static extern bool SetWindowPos(HandleRef hWnd, HandleRef hWndInsertAfter,
int x, int y, int cx, int cy, int flags);
[StructLayout(LayoutKind.Sequential)]
private struct WINDOWPOS
{
public IntPtr hwnd;
public IntPtr hwndInsertAfter;
public int x;
public int y;
public int cx;
public int cy;
public int flags;
};
private unsafe delegate void ResizeChildDelegate(WINDOWPOS * wpos);
private unsafe void ResizeChild(WINDOWPOS * wpos)
{
// verify if it's the right instance of MyPanel if needed
if ((this.Controls.Count == 1) && (this.Controls[0] is Panel))
{
Panel child = this.Controls[0] as Panel;
// stop window redraw to avoid flicker
SendMessage(new HandleRef(child, child.Handle), WM_SETREDRAW, 0, 0);
// start a new stack of SetWindowPos calls
SetWindowPos(new HandleRef(child, child.Handle), new HandleRef(null, IntPtr.Zero),
0, 0, wpos->cx, wpos->cy, SWP_NOACTIVATE | SWP_NOZORDER);
// turn window repainting back on
SendMessage(new HandleRef(child, child.Handle), WM_SETREDRAW, 1, 0);
// send repaint message to this control and its children
this.Invalidate(true);
}
}
protected unsafe override void WndProc(ref Message m)
{
if (m.Msg == WM_WINDOWPOSCHANGING)
{
WINDOWPOS* wpos = (WINDOWPOS*)m.LParam;
Debug.WriteLine("WM_WINDOWPOSCHANGING received by " + this.Name + " flags " + wpos->flags);
if (((wpos->flags & (SWP_NOZORDER | SWP_NOACTIVATE)) == (SWP_NOZORDER | SWP_NOACTIVATE)) &&
((wpos->flags & ~(SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE)) == 0))
{
if ((wpos->cx != this.Width) || (wpos->cy != this.Height))
{
BeginInvoke(new ResizeChildDelegate(ResizeChild), new object[] { m.LParam });
return;
}
}
}
base.WndProc(ref m);
}
}
3. repeat for every 12-th or so window
I hope this helps.
Regards,
Alex (Alejandro Campos Magencio)
Comments
Anonymous
December 15, 2008
Thanks for the information, we've had this exact problem in an MFC application and the cause has been a complete mystery. One of the guys found your post, tried out one of your workarounds and its fixed.I must say this limitation is very odd, and I could find no documentation for it. It's very frustrating to have these differences in implementation across the platform as it makes testing a nightmare!Anonymous
April 20, 2009
I ran into the same problem (even with an 8 level deep window hierarchy) and managed to isolate a culprit: Setpoint software from Logitech. With it running I can reproduce the issue on 64-bit Vista. Without it I don't have the problem.I have no idea what's SetPoint doing to manage to break other apps but the question is what can I do about it?Anonymous
April 30, 2009
I'm afraid you will have to contact Logitech about their software...Anonymous
August 21, 2009
I've seen the same problem on Windows 7 x64 (RTM) with only 6 nested window levels when doing SetWindowPos() calls within MFC OnSize() processing (Vista x64 didn't have the same low limit, and neither does XP/XP64).If an app handles WM_MOVE/WM_SIZE then there's not only the call for WM_WINDOWPOSCHANGED but extra function calls for the nested WM_MOVE/WM_SIZE (along with any MFC overhead). Handling WM_WINDOWPOSCHANGED directly, removes the extra WM_MOVE/WM_SIZE calls, and increases the usable window nest level. But I'd have to think most people would be processing WM_SIZE not WM_WINDOWPOSCHANGED.It amazing to me that between the call to DefWindowProc for WM_WINDOWPOSCHANGED and when WM_SIZE gets to the windowproc it seems there are about 12 function calls (according to VS2008 debugger). If OpenGL is used by the window then there are about 25 function calls, which is perhaps why I've hit problems with such a small window nest level.I'd have to think, with problems occurring with such a small window nest level, Windows 7 is going to produce all sorts of odd application compatibility issues.Anonymous
August 27, 2009
Thank you, very frustrating after moving to Win7, our app was not resizing very well. Reducing the number of nested containers has solved the problem for now. Would not have figured it out without this post.Anonymous
December 09, 2009
Hey Guys,We had the same problem with this issue. Using your Win32 code, and a slightly different approach, we managed to solve it without any subclassing..Key is to use a System.Windows.Forms.NativeWindow, and setup the WndProc code there, then, right before you display a dialog or window to the user, loop through the controls/children, and setup a nativewindow hook at every (i % 14) level (14, arbitrary but works well for us).. I've posted an example project showing off the example:http://www.arcturus.com.au/code/NestedExample.zipAnonymous
December 09, 2009
sorry the url is http://www.arcturus.com.au/code/NestingExample.zipAnonymous
December 09, 2009
Thx a lot Sean!It's great to know that we have more alternate solutions to this. That way people can chose whichever solution fits them better.Cheers,AlexAnonymous
January 29, 2010
I have several x64 Windows 7 machine and I have this problem with 50% of them (usually in Acrobat, sometimes in IDEs).I read the whole artilce but I don't understand why you cannot increase the stack size. Yes, you cannot have unlimited stack size but the fact is with current limits, many users are hitting the limits. At least you should match the same nesting level that 32 bits Windows has. How much memory would be lost if you double the stack size? Are we talking about just a few killo bytes?Anonymous
February 11, 2010
Adobe software developers need to get working on Flex Builder to eliminate these issues, some workarounds of which are described in detail at http://www.danielansari.com/wordpress/2010/02/adobe-flex-builder-3-problems-in-64-bit-windows/.Anonymous
May 13, 2010
Great post!I ran into this problem recently, tried your solution for asynchronously setting sizes and it worked right away.Many thanks,Chris.Anonymous
June 09, 2010
Excellent article. It took me a day to only find out the true reason why my layout was messed up under certain circumstances. Your posting finally explained in detail what is going on and gave a solution. Many thanks from me too.MikeAnonymous
June 22, 2010
Be aware that the workaround code listed in section 2.2 will likely fail. The WndProc passes a native pointer that it does not own to the callback delegate. By the time the 'async' callback executes, Windows can (and probably has) recycled and overwritten the memory contents at that location.To get around this, remove all the pointer and unsafe logic, and instead Marshal the pointer to a struct within WndProc - 'WINDOWPOS pos = Marshal.PtrToStructure(m.LParam, typeof(WINDOWPOS));'Anonymous
July 04, 2010
Can you please pass along to the SQL Server team that SQL Server Management Studio has this problem on any WIn7 x64, system running a pen tablet (i.e. Wacom Intuos) when the Tablet PC Input service is running.Anonymous
July 12, 2010
We were experiencing severe resize issues with our app including entire panels disappearing when minimizing/restoring. We found that implementing Sean Malloys solution on the first panel of our app resolved most of the problems:
We still have minor issues with various controls such as ListView headers not painting correctly when the main window is resized but an immediate minimize/restore fixes it.It's surprising that Microsoft would let this app compatibility slip in x64 when historically they have worked hard to maintain a compatible upgrade path. Especially when their own apps are also affected!public MainWindow() { InitializeComponent(); new ResizeHandler(panelClientArea);
Anonymous
August 09, 2010
I am having trouble getting to Sean's link: www.arcturus.com.au/.../NestingExample.zip .Is there an alternate location that contains the example?Anonymous
October 25, 2010
For MFC it's simple: additional PostMessage(WM_SIZE, nType, MAKELPARAM(Size.cx, Size.cy)); after SetWindowPos solved the problem. It's no SendMessage, PostMessage!Anonymous
October 27, 2010
We implemented this as an alternative to catching OnResize and it has worked great for us with sizing events where needed in x64 architecture. /// <summary> /// OnSizeChanged Event Handler /// </summary> /// <param name="e"></param> protected override void OnSizeChanged(EventArgs e) { if (this.Handle != null) { this.BeginInvoke((MethodInvoker)delegate { base.OnSizeChanged(e); }); } }Anonymous
November 22, 2010
Please note that if one of WH_CALLWNDPROC, WH_CALLWNDPROCRET or WH_CBT hooks is installed in your GUI thread, then according to my experiments, the stack limit would be encountered two times faster.Say, 14 levels without a hook, and 7 when it's set.Anonymous
January 07, 2011
Hi all, i have the same problem bit with focus rect in combo and checkboxes. the the nesting is too deep, focus rect is not show anymore. Anybody have a solution for this problem? Thanks, DavidAnonymous
March 29, 2011
I ran into same problem resizing forms with nested panels on windows 7 64bit as well.I totally do not understand the solution posted here. However, I do notice that after the form is resized and some nested panels not being correctly resized, if I switch to another tab and come back, they would be resized correctly.So, I fixed it simply by redrawing the main panel:private void Form1_Resize(object sender, EventArgs e){ tableLayoutPanel1.Hide(); tableLayoutPanel1.Show();}Anonymous
April 05, 2011
This is a surprising though reasonable limitation.So what is the rough controls nesting depth limit?Anonymous
April 05, 2011
Is there a more official page detailing this problem?Like on MSDN instead of this blog? Am I missing any other solutions?Anonymous
April 05, 2011
There is no MSDN page or KB about this as far as I know... The solutions I know are in the post, and other solutions can be found within the comments of the post...Anonymous
May 11, 2011
The comment has been removedAnonymous
May 13, 2011
Also any QA team checking this problem should open the tested software with Spy++ running, as it reproduces all the different scenarios like having a specials drivers such as in the Logitech and Wacom issues.Anonymous
May 18, 2011
Jeremy Streeter's solution (code repeated below) was added to base controls on my project. This resolved the issue for me in a pretty unintrusive way. Thanks Jeremy! protected override void OnSizeChanged(EventArgs e) { if (this.Handle != null) { this.BeginInvoke((MethodInvoker)delegate { base.OnSizeChanged(e); }); } }Anonymous
May 25, 2011
FYI, there is a KB article out there, dated June 2008:support.microsoft.com/.../953934Also, checking for a handle via (this.Handle != null) is bad news as accessing the Handle property will force the handle to be created. I had run into an issue where the handle was recreating itself after disposal started, not a fun one to track down. I recommend using the IsHandleCreated property instead.Anonymous
February 15, 2012
I had to make 2 suddle changes to get this to work correctly. When overriding OnSizeChanged, I had to prevent the base.OnSizeChanged from being called. If it it continued to call into the base class, I still saw the redraw issue. In the ChangeSize method, instead of checking if the handle is null, you'll need to check "IsHandleCreated". The Handle property getter actually creates a handle and causes the controls Load event to fire which could cause some issues if you're doing some setup logic in the OnLoad event.Anonymous
April 25, 2012
FYI, there is a kb on this problem. but this does not helps more than this article! Thankssupport.microsoft.com/.../en-usAnonymous
April 25, 2012
Hi Camilio,Yes, I know about the KB article, as I requested it to our Documentation team and participated in the writting :-)Cheers!AlexAnonymous
July 17, 2012
Hi, Alejandro. Thanks for shedding some light on this topic.Do you have any information on why this limitation is hit on some installations of Win7 x64, but not all of them?On some machines, this limitation is being hit quite easily. Is there any way to isolate the machines (hardware + Win7 installation) that are most affected?Anonymous
July 17, 2012
Hi Thiago,If I recall well, there could be things like hooks that could reduce the number of nested levels before the issue happens.The limitation should be the same in all Win7 x64, unless they recently changed something (hotfix) that it is installed in some of your machines but not in others...