Retired Microsoft Blog disclaimer

This directory is a mirror of retired "Decrypt My World" MSDN blog and is provided as is. All posting authorship and copyrights belong to respective authors.

Posts on this page:

Original URL: https://blogs.msdn.microsoft.com/alejacma/2009/08/11/controls-wont-get-resized-once-the-nesting-hierarchy-of-windows-exceeds-a-certain-depth-x64/
Post name: Controls won’t get resized once the nesting hierarchy of windows exceeds a certain depth (x64)
Original author: Alejandro Campos Magencio
Posting date: 2009-08-11T18:59:00+00:00


 

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)

 

Original URL: https://blogs.msdn.microsoft.com/alejacma/2009/08/11/managed-debugging-with-windbg-thread-stacks-part-2/
Post name: MANAGED DEBUGGING with WINDBG. Thread Stacks. Part 2
Original author: Alejandro Campos Magencio
Posting date: 2009-08-11T06:06:00+00:00


Hi all,


This post is a continuation of MANAGED DEBUGGING with WINDBG. Thread Stacks. Part 1.


Original URL: https://blogs.msdn.microsoft.com/alejacma/2009/08/11/managed-debugging-with-windbg-thread-stacks-part-1/
Post name: MANAGED DEBUGGING with WINDBG. Thread Stacks. Part 1
Original author: Alejandro Campos Magencio
Posting date: 2009-08-11T05:47:00+00:00


Hi all,

This post is a continuation of MANAGED DEBUGGING with WINDBG. Threads. Part 2.

THREAD STACKS. Part 1

· We can take a look to the objects in the stack:

The CLR is stack-based. All references to objects are in the stack, but objects themselves are in the managed heap. We also have to take into account that Structs that we instantiate go to the stack and Classes we instantiate go to the managed heap.

In any thread we can see a list of references to objects that are still on the thread’s stack (remember we have one stack per thread ;-):

0:000> !DumpStackObjects

...

0:000> !dso

OS Thread Id: 0x1334 (0)

ESP/REG Object Name

0018e710 01b3ed34 System.IndexOutOfRangeException

0018e758 01b3ed34 System.IndexOutOfRangeException

0018e79c 01aef338 WindowsApplication1.Form1

0018e7a0 01b3ed1c System.Int32[]

0018e7a4 01b3cf94 System.Windows.Forms.MouseEventArgs

0018e7ac 01aef338 WindowsApplication1.Form1

0018e7b4 01b3ed1c System.Int32[]

0018e7bc 01b3cf94 System.Windows.Forms.MouseEventArgs

0018e838 01b3ed1c System.Int32[]

0018e84c 01aef338 WindowsApplication1.Form1

0018e860 01af0398 System.Windows.Forms.Button

...

Note that the objects at the top of the stack correspond to the objects used by the methods at the top of the call stack:

0:000> kpL

ChildEBP RetAddr

0018e6f0 79f071ac KERNEL32!RaiseException(unsigned long dwExceptionCode = 0xe0434f4d, unsigned long dwExceptionFlags = 1, unsigned long nNumberOfArguments = 1, unsigned long * lpArguments = 0x0018e728)+0x58

0018e750 79f9293a mscorwks!RaiseTheExceptionInternalOnly(class Object * throwable = 0x01b3ed34, int rethrow = 0, int fForStackOverflow = 0)+0x2a8

0018e788 7a129a34 mscorwks!UnwindAndContinueRethrowHelperAfterCatch(class Frame * pEntryFrame = 0x0018e7e0, class Exception * pException = 0x003e65a8)+0x70

0018e828 002c1aad mscorwks!JIT_RngChkFail(void)+0xb0

0018e870 002c19fe WindowsApplication1!WindowsApplication1.Form1.PlayWithArray(<HRESULT 0x80004001>)+0x55

...

0:000> !CLRStack -a

OS Thread Id: 0x1334 (0)

ESP EIP

0018e7e0 761db09e [HelperMethodFrame: 0018e7e0]

0018e830 002c1aad WindowsApplication1.Form1.PlayWithArray(Int32[])

PARAMETERS:

this = 0x01aef338

array = 0x01b3ed1c

LOCALS:

0x0018e848 = 0x00000000

0x0018e844 = 0x00000006

0x0018e840 = 0x00000003

0x0018e834 = 0x00000000

0x0018e83c = 0x00000003

0018e878 002c19fe WindowsApplication1.Form1.Button6_Click(System.Object, System.EventArgs)

PARAMETERS:

this = 0x01aef338

sender = 0x01af0398

e = 0x01b3cf94

LOCALS:

0x0018e87c = 0x00000000

<CLR reg> = 0x01b3ed1c

<CLR reg> = 0x00000000

...

· We can inspect the objects:

With a reference to an object we can inspect the object itself:

0:000> !DumpObj 0x01aef338

...

0:000> !do 0x01aef338

Name: WindowsApplication1.Form1

MethodTable: 001c6ecc

EEClass: 002d0a30

Size: 352(0x160) bytes

(C:\__WORKSHOP\Demos\BuggyNETApp\bin\Debug\WindowsApplication1.exe)

Fields:

MT Field Offset Type VT Attr Value Name

...

7ae75fa8 40010f2 2b8 System.Drawing.Font 0 static 01af1d18 defaultFont

...

7ae75b24 40011e0 80 ...Drawing.Rectangle 1 instance 01aef3b8 displayRect

...

7b47f3dc 4000011 154 ...dows.Forms.Button 0 instance 01af0398 _Button6

7912d7c0 4000013 158 System.Int32[] 0 instance 01b3ed1c myarray

...

0:000> !do 01af1d18

Name: System.Drawing.Font

...

Fields:

MT Field Offset Type VT Attr Value Name

...

790fd8c4 4000641 c System.String 0 instance 01af1544 systemFontName

...

0:000> !do -nofields 01af1544

Name: System.String

MethodTable: 790fd8c4

EEClass: 790fd824

Size: 40(0x28) bytes

(C:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)

String: DefaultFont

0:000> !do 01aef3b8

<Note: this object has an invalid CLASS field>

Invalid object

0:000> !DumpVC 7ae75b24 01aef3b8

Name: System.Drawing.Rectangle

MethodTable 7ae75b24

EEClass: 7ae75aac

Size: 24(0x18) bytes

(C:\Windows\assembly\GAC_MSIL\System.Drawing\2.0.0.0__b03f5f7f11d50a3a\System.Drawing.dll)

Fields:

MT Field Offset Type VT Attr Value Name

79102290 4000462 0 System.Int32 0 instance 0 x

79102290 4000463 4 System.Int32 0 instance 0 y

...

0:000> !DumpArray 01b3ed1c

...

0:000> !da 01b3ed1c

Name: System.Int32[]

MethodTable: 7912d7c0

EEClass: 7912d878

Size: 24(0x18) bytes

Array: Rank 1, Number of elements 3, Type Int32

Element Methodtable: 79102290

[0] 00000001

[1] 00000002

[2] 00000003

Note that the column VT contains the value 1 if the field is a valuetype structure, and 0 if the field contains a pointer to another object.

If we only need to see the static fields of the object, we can inspect the EEClass of the class (representation of a class created before any method invocations are made on it) of the object:

0:000> !DumpClass 002d0a30

Class Name: WindowsApplication1.Form1

mdToken: 02000008 (C:\__WORKSHOP\Demos\BuggyNETApp\bin\Debug\WindowsApplication1.exe)

Parent Class: 7b47b140

Module: 001c2c3c

Method Table: 001c6ecc

Vtable Slots: 180

Total Method Slots: 18f

Class Attributes: 100001

NumInstanceFields: 47

NumStaticFields: 2

MT Field Offset Type VT Attr Value Name

...

7ae75fa8 40010f2 2b8 System.Drawing.Font 0 static 01af1d18 defaultFont

...

7ae75b24 40011e0 80 ...Drawing.Rectangle 1 instance displayRect

...

7a7559dc 40011de 308 ...stics.TraceSwitch 0 static 00000000 AutoScrolling

...

7b47f3dc 4000011 154 ...dows.Forms.Button 0 instance _Button6

7912d7c0 4000013 158 System.Int32[] 0 instance myarray

...

But if we are looking for a way to display a static field of a class (and we don't have an instance of the class, so !DumpObj won't help us), we can inspect the EEClass of the class itself:

0:000> !Name2EE WindowsApplication1 WindowsApplication1.Form1

Module: 001c2c3c (WindowsApplication1.exe)

Token: 0x02000008

MethodTable: 001c6ecc

EEClass: 002d0a30

Name: WindowsApplication1.Form1

0:000> !DumpClass 002d0a30

Class Name: WindowsApplication1.Form1

...

Next post: MANAGED DEBUGGING with WINDBG. Thread Stacks. Part 2.

Index: MANAGED DEBUGGING with WINDBG. Introduction and Index.

Regards,

Alex (Alejandro Campos Magencio)

Original URL: https://blogs.msdn.microsoft.com/alejacma/2009/08/10/managed-debugging-with-windbg-threads-part-2/
Post name: MANAGED DEBUGGING with WINDBG. Threads. Part 2
Original author: Alejandro Campos Magencio
Posting date: 2009-08-10T06:25:00+00:00


Hi all,

This post is a continuation of MANAGED DEBUGGING with WINDBG. Threads. Part 1.

THREADS. Part 2

· We can see all .NET related threads in our process:

There are different types of .NET threads: finalizer, GC, debugger, timer, threadpool, primary and background threads. But not all threads related to .NET are managed. We can see all .NET threads like this:

0:004> !Threads -special

ThreadCount: 2

UnstartedThread: 0

BackgroundThread: 1

PendingThread: 0

DeadThread: 0

Hosted Runtime: no

PreEmptive GC Alloc Lock

ID OSID ThreadOBJ State GC Context Domain Count APT Exception

0 1 1f3c 003ec600 6020 Enabled 0198c744:0198dfe8 003b4bd8 0 STA

2 2 904 003f5990 b220 Enabled 00000000:00000000 003b4bd8 0 MTA (Finalizer)

OSID Special thread type

1 1b08 DbgHelper

2 904 Finalizer

· We can inspect the .NET Thread Pool of our process:

A thread pool is a collection of worker threads that efficiently execute asynchronous callbacks on behalf of the application. The thread pool is primarily used to reduce the number of application threads and provide management of the worker threads. Applications can queue work items, associate work with waitable handles, automatically queue based on a timer, and bind with I/O.

.NET implements the entire pooling and load-balancing mechanism using the native ThreadPoolMgr class. The ThreadPoolMgr class has five main thread types, and we can only use the first two to run our managed code:

1. Completion port thread : executes the callback function after an item is queued by QueueUserWorkItem(). If there are not enough completion port threads available, the work item is sent to the work request queue.

2. Worker thread: services the work request queue.

3. Gate thread: Monitors the health of the thread pool.

4. Wait thread: to wait on a synchronization object (i.e. System.Threading.WaitHandle).

5. Timer thread: timer callback functionality of the System.Threading.Timer class.

We can see basic information about the Thread Pool, including the number of work requests in the queue, timers and completion port threads. This info has been taken from an ASP.NET (w3wp.exe) process:

0:030> !ThreadPool

CPU utilization 4%

Worker Thread: Total: 5 Running: 5 Idle: 0 MaxLimit: 200 MinLimit: 2

Work Request in Queue: 0

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

Number of Timers: 8

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

Completion Port Thread:Total: 2 Free: 2 MaxFree: 4 CurrentLimit: 1 MaxLimit: 200 MinLimit: 2

Note that CPU utilization is the complete CPU utilization on the box, so not specifically for the ASP.NET process. In this sample, there are no work requests in the queue, and only a few timers waiting. We have a maximum of 200 worker threads to execute our requests, and a minimum of 2 alive at any time (this comes from our machine.config settings). And currently we are executing on 5 of these. There are no work requests in the queue, so the thread pool is not exhausted. All the completion port threads are free.

· The following list may help us identify some typical threads we may find in our applications as shown by calls to these specific functions:

- mscorwks!DebuggerRCThread: Debugger thread. All managed processes have a debugger thread, even if there is no debugger attached. There is only one debugger thread in a given process. This thread exists so that a managed debugger can attach and control the managed code.

- mscorwks!GCHeap::FinalizerThreadStart: Finalizer thread. Just like the debugger thread, there is always one finalizer thread per managed process.

- RPCRT4!LRPC_ADDRESS::ReceiveLotsaCalls: Lightweight Remote Procedure Call (RPC) thread. It’s used for local remote procedure call (LRPC) communication. Here it handles incoming RPC calls to this process.

- aspnet_wp!wmain: Main ASP.NET thread. It has initialized the runtime, connected the named pipes back to InetInfo.exe, and created the ping thread. Once it has completed the initialization, it loops, waiting for the process to exit.

- aspnet_wp!DoPingThread: ASP.NET ping thread. It responds to the pings it receives from the health monitoring thread in InetInfo.exe.

- comsvcs!CSTAThreadPool::KillThreadControlLoop: COM+ thread pool.

- comsvcs!CSTAThreadPool::LoadBalanceThreadControlLoop: COM+ thread pool.

- ole32!CRpcThread::WorkerLoop: COM worker threads. They may be waiting for COM to dispatch work.

- ole32!DLLHostThreadEntry: COM has created this to instantiate a COM component marked as Apartment.

- ole32!GetToSTA: You need to switch to an STA thread in the process for components marked as Aparment. This thread switch won't happen with ASPCompat page directive or if Apartment component is in COM+ (The component must be registered in COM+ as a library application, and Synchronization must be set to Required so that it runs on a COM+ STA worker thread), which forces the page's ProcessRequest method to execute on a COM+ STA worker thread. If component is marked as Free, we can run in MTA thread.

- comsvcs!CSTAThread::WorkerLoop: COM+ thread pool. STA threads used with ASPCompat or Aparment component in COM+. ASPCompat forces the ProcessRequest method to execute in a thread that has been initialized to the STA threading model.

Next post: MANAGED DEBUGGING with WINDBG. Thread Stacks. Part 1.

Index: MANAGED DEBUGGING with WINDBG. Introduction and Index.

Regards,

Alex (Alejandro Campos Magencio)

Original URL: https://blogs.msdn.microsoft.com/alejacma/2009/08/10/managed-debugging-with-windbg-threads-part-1/
Post name: MANAGED DEBUGGING with WINDBG. Threads. Part 1
Original author: Alejandro Campos Magencio
Posting date: 2009-08-10T06:10:00+00:00


Hi all,

This post is a continuation of MANAGED DEBUGGING with WINDBG. Call Stacks. Part 3.

THREADS. Part 1

· We can see all threads in our process:

If we reach a breakpoint or break on an exception, WinDbg command prompt shows the ID of the thread which reached the breakpoint or raised the exception. We can directly see the call stack, or inspect the exception we got, etc., for that thread.

If we break manually (i.e. Ctrl+Break), the command prompt shows the ID of the debugger thread (injected by the debugger in our process to interact with it). We can’t see or do anything useful there:

0:004> kL

ChildEBP RetAddr

04f1fcfc 77c0f0a9 ntdll!DbgBreakPoint

04f1fd2c 77a43833 ntdll!DbgUiRemoteBreakin+0x3c

04f1fd38 77bba9bd KERNEL32!BaseThreadInitThunk+0xe

04f1fd78 00000000 ntdll!_RtlUserThreadStart+0x23

So we need to change to another more useful thread. We can take a look to all threads as in any unmanaged application:

0:004> ~

0 Id: 1910.1f3c Suspend: 1 Teb: 7ffdf000 Unfrozen

1 Id: 1910.1b08 Suspend: 1 Teb: 7ffde000 Unfrozen

2 Id: 1910.904 Suspend: 1 Teb: 7ffdd000 Unfrozen

3 Id: 1910.1bbc Suspend: 1 Teb: 7ffdc000 Unfrozen

. 4 Id: 1910.1b60 Suspend: 1 Teb: 7ffdb000 Unfrozen

We can see for how long they’ve been running:

0:004> !runaway

User Mode Time

Thread Time

0:1f3c 0 days 0:00:00.078

4:1b60 0 days 0:00:00.000

3:1bbc 0 days 0:00:00.000

2:904 0 days 0:00:00.000

1:1b08 0 days 0:00:00.000

We can move to any other thread and see i.e. its call stack. Let’s take a look to thread 0 because it’s the one which apparently has been doing more stuff, and then go back to the thread where we broke:

0:004> ~0s

eax=00000000 ebx=00000000 ecx=003b008c edx=77be0f34 esi=00dd8c30 edi=00000000

eip=77be0f34 esp=0027e3a8 ebp=0027e3dc iopl=0 nv up ei pl zr na pe nc

cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246

ntdll!KiFastSystemCallRet:

77be0f34 c3 ret

0:000> kL

ChildEBP RetAddr

0027e3a4 77afb5bc ntdll!KiFastSystemCallRet

0027e3a8 77af1598 USER32!NtUserWaitMessage+0xc

0027e3dc 77af1460 USER32!DialogBox2+0x202

0:000> ~

. 0 Id: 1910.1f3c Suspend: 1 Teb: 7ffdf000 Unfrozen

1 Id: 1910.1b08 Suspend: 1 Teb: 7ffde000 Unfrozen

2 Id: 1910.904 Suspend: 1 Teb: 7ffdd000 Unfrozen

3 Id: 1910.1bbc Suspend: 1 Teb: 7ffdc000 Unfrozen

# 4 Id: 1910.1b60 Suspend: 1 Teb: 7ffdb000 Unfrozen

0:000> ~#

# 4 Id: 1910.1b60 Suspend: 1 Teb: 7ffdb000 Unfrozen

Start: ntdll!RtlUserThreadStart (77be0f18)

Priority: 0 Priority class: 32 Affinity: 3

0:000> ~#s

0:004>

Thread 0 is the main GUI thread of a WinForms application, for instance.

Note that ‘~’ command will use a ‘.’ to indicate the active thread, and a ‘#’ to indicate the thread where the debugger broke in the first place.

Additionally, we may want to see all call stacks (kL100) for all threads (~*) to find the thread where we want to move:

0:000> ~*kL100

. 0 Id: 1910.1f3c Suspend: 1 Teb: 7ffdf000 Unfrozen

ChildEBP RetAddr

0027e3a4 77afb5bc ntdll!KiFastSystemCallRet

0027e3a8 77af1598 USER32!NtUserWaitMessage+0xc

0027e3dc 77af1460 USER32!DialogBox2+0x202

...

0027f894 00000000 ntdll!_RtlUserThreadStart+0x23

1 Id: 1910.1b08 Suspend: 1 Teb: 7ffde000 Unfrozen

ChildEBP RetAddr

00bef7b8 77be0690 ntdll!KiFastSystemCallRet

...

00bef980 00000000 ntdll!_RtlUserThreadStart+0x23

...

# 4 Id: 1910.1b60 Suspend: 1 Teb: 7ffdb000 Unfrozen

ChildEBP RetAddr

04f1fcfc 77c0f0a9 ntdll!DbgBreakPoint

...

04f1fd78 00000000 ntdll!_RtlUserThreadStart+0x23

Note that we will always see more threads than the ones we created. In a typical WinForms application we will see our main GUI thread, a Finalizer thread (Garbage Collector related), a GDI+ thread, a couple of debugger related threads, and any other thread we’ve created.

· We can see all managed threads in our process:

Some threads in our process will contain some managed calls (managed threads), but not all (unmanaged threads). We can only focus on managed threads:

0:004> !Threads

ThreadCount: 2

UnstartedThread: 0

BackgroundThread: 1

PendingThread: 0

DeadThread: 0

Hosted Runtime: no

PreEmptive GC Alloc Lock

ID OSID ThreadOBJ State GC Context Domain Count APT Exception

0 1 1f3c 003ec600 6020 Enabled 0198c744:0198dfe8 003b4bd8 0 STA

2 2 904 003f5990 b220 Enabled 00000000:00000000 003b4bd8 0 MTA (Finalizer)

0:004> ~0kL

ChildEBP RetAddr

0027e3a4 77afb5bc ntdll!KiFastSystemCallRet

0027e3a8 77af1598 USER32!NtUserWaitMessage+0xc

0027e3dc 77af1460 USER32!DialogBox2+0x202

00000001 00371a20 System_Windows_Forms_ni!System.Windows.Forms.MessageBox.Show(System.String)+0x26

00000001 7b062c9a WindowsApplication1!WindowsApplication1.Form1.Button6_Click(System.Object, System.EventArgs)+0xa8

...

0:004> ~2kL

ChildEBP RetAddr

03b4f5b0 77be0690 ntdll!KiFastSystemCallRet

03b4f5b4 77a47e09 ntdll!ZwWaitForMultipleObjects+0xc

03b4f650 77a48150 KERNEL32!WaitForMultipleObjectsEx+0x11d

03b4f66c 79ef224b KERNEL32!WaitForMultipleObjects+0x18

03b4f68c 79fb997b mscorwks!WKS::WaitForFinalizerEvent+0x77

03b4f6a0 79ef3207 mscorwks!WKS::GCHeap::FinalizerThreadWorker+0x79

03b4f6b4 79ef31a3 mscorwks!ManagedThreadBase_DispatchInner+0x4f

03b4f748 79ef30c3 mscorwks!ManagedThreadBase_DispatchMiddle+0xb1

03b4f784 79fb9643 mscorwks!ManagedThreadBase_DispatchOuter+0x6d

03b4f7ac 79fb960d mscorwks!ManagedThreadBase_NoADTransition+0x32

03b4f7bc 79fba09b mscorwks!ManagedThreadBase::FinalizerBase+0xd

03b4f7f4 79f95a2e mscorwks!WKS::GCHeap::FinalizerThreadStart+0xbb

03b4f88c 77a43833 mscorwks!Thread::intermediateThreadProc+0x49

03b4f898 77bba9bd KERNEL32!BaseThreadInitThunk+0xe

03b4f8d8 00000000 ntdll!_RtlUserThreadStart+0x23

We may want to see only managed calls (!CLRStack) in all threads (~*):

0:004> ~*e !CLRStack

OS Thread Id: 0x1f3c (0)

ESP EIP

0027e6c8 77be0f34 [NDirectMethodFrameStandalone: 0027e6c8] System.Windows.Forms.SafeNativeMethods.MessageBox(System.Runtime.InteropServices.HandleRef, System.String, System.String, Int32)

...

0027e780 7b28595e System.Windows.Forms.MessageBox.Show(System.String)

0027e788 00371a20 WindowsApplication1.Form1.Button6_Click(System.Object, System.EventArgs)

...

0027ecdc 00370117 WindowsApplication1.My.MyApplication.Main(System.String[])

0027ef24 79e7c74b [GCFrame: 0027ef24]

OS Thread Id: 0x1b08 (1)

Unable to walk the managed stack. The current thread is likely not a

managed thread. You can run !threads to get a list of managed threads in

the process

OS Thread Id: 0x904 (2)

Failed to start stack walk: 80004005

OS Thread Id: 0x1bbc (3)

Unable to walk the managed stack. The current thread is likely not a

managed thread. You can run !threads to get a list of managed threads in

the process

...

Note that we have to use ‘e’ with ‘~*’ to be able to execute a command from a debugger extension in all threads.

Note that thread 2 (Finalizer) is supposed to be a managed thread, but it doesn’t currently contain any managed calls in the call stack.

Next post: MANAGED DEBUGGING with WINDBG. Threads. Part 2.

Index: MANAGED DEBUGGING with WINDBG. Introduction and Index.

Regards,

Alex (Alejandro Campos Magencio)