Debugging Dependency Properties with WinDbg

June 1, 2013

no comments

Dependency properties are one of the corner stones of the WPF framework. They were designed to support various features like change notification, multiple value providers, validation, value inheritance and more. On the other hand, their implementation makes them less intuitive to debug with WinDbg. Unlike regular CLR properties, getting their value is more than just dumping the object in which they are declared. In this post I would like to explain and demonstrate how WinDbg can play nicely with dependency properties.

Overview

Before we launch WinDbg, I want to explain how dependency properties are implemented under the hood. Please note that the below information is undocumented and is based entirely on reviewing the source code of the DependencyObject and DependencyProperty classes. Even though it is unlikely, it may change in the future.

Whenever a new dependency property is registered, it gets a unique id and stored in a static list with all the other dependency properties in the application. This unique id starts from 0 and is being incremented sequentially until 65,535 (which means that, by design, a single application can contain up to 65,535 dependency properties. After that, an exception is thrown. But don’t worry, this is more than enough). Because of memory usage considerations, that unique id is stored at the bottom 16 bytes of the _packedData private member of the DependencyProperty object.

Upon registration, a default value can be set for each dependency property. This default value is stored in the _defaultMetadata member of the dependency property instance.

It is important to understand that there is only one instance of each dependency property in the application. Its metadata (including its default value), name, type, validation callbacks, and all the other characteristics are stored once and are shared between all the dependency objects that are using the property. So how is it possible to set a different FontSize value to two different Control instances?

Each DependencyObject contains a member named _effectiveValues. This member contains a list of all the values that are specific for that object. Each entry in that list contains a _value member to store the local value and a _propertyIndex member to store the unique id of the property for which that value is set. Whenever GetValue is called to get the value of a specific dependency property, it first checks the _effectiveValues list for an entry with a _propertyIndex that matches the unique id of the property. If there is such an entry, it means that the dependency object has its own value for that property, otherwise, the default value is returned from the dependency property metadata (This is of course a simplified explanation of the algorithm). This is what is being referred to in the documentation as sparse storage.

Based on the above information, I want to demonstrate two common use cases:

How to get the value of a specific dependency property

Below is a step by step demonstration of how to get the FontSize property value of a Button control.

First, dump the Button object for which you want to get the property value:

   1: 0:010> !do 000000ad88c33170

   2: Name:        System.Windows.Controls.Button

   3: MethodTable: 000007f9d3178d20

   4: EEClass:     000007f9d2b52428

   5: Size:        344(0x158) bytes

   6: File:        C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\PresentationFramework\v4.0_4.0.0.0__31bf3856ad364e35\PresentationFramework.dll

   7: Fields:

   8:               MT    Field   Offset                 Type VT     Attr            Value Name

   9: 000007f9e58e5f08  400137e       20 ...ctiveValueEntry[]  0 instance 000000ad88d23c58 _effectiveValues

  10: ...

  11: 000007f9e58df7b0  40002c2      8f8 ...ependencyProperty  0   static 000000ad88c01750 FontSizeProperty

  12: ...

You are interested in two of its members, the _effectiveValues member, in which the local values are stored, and the FontSizeProperty, from which you will extract the property’s unique id like so:

   1: 0:010> !do 000000ad88c01750

   2: Name:        System.Windows.DependencyProperty

   3: MethodTable: 000007f9e58df7b0

   4: EEClass:     000007f9e5756528

   5: Size:        88(0x58) bytes

   6: File:        C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\WindowsBase\v4.0_4.0.0.0__31bf3856ad364e35\WindowsBase.dll

   7: Fields:

   8:               MT    Field   Offset                 Type VT     Attr            Value Name

   9: 000007f9d56cc358  4001397        8        System.String  0 instance 000000ad88bddf68 _name

  10: 000007f9d56ce3e0  4001398       10          System.Type  0 instance 000000ad88be1370 _propertyType

  11: 000007f9d56ce3e0  4001399       18          System.Type  0 instance 000000ad88bfd840 _ownerType

  12: 000007f9e58dd498  400139a       20 ....PropertyMetadata  0 instance 000000ad88c016b0 _defaultMetadata

  13: ...

  14: 000007f9e5b7b190  400139d       40         System.Int32  1 instance          1638560 _packedData

  15: ...

  16: 000007f9e58e4090  40013a0      828 ...ty, WindowsBase]]  1   static 000000ad98bc2748 RegisteredPropertyList

  17: ...

  18: 000007f9d56cf108  40013a2      5d4         System.Int32  1   static              412 GlobalIndexCount

  19: ...

As I described earlier, the unique id is stored at the bottom 16 bytes of the _packedData member. You can do the calculation from within WinDbg itself:

   1: 0:010> .formats 0n1638560

   2: Evaluate expression:

   3:   Hex:     00000000`001900a0

   4:   Decimal: 1638560

   5:   Octal:   0000000000000006200240

   6:   Binary:  00000000 00000000 00000000 00000000 00000000 00011001 00000000 10100000

   7:   Chars:   ........

   8:   Time:    Tue Jan 20 01:09:20 1970

   9:   Float:   low 2.29611e-039 high 0

  10:   Double:  8.09556e-318

  11: 0:010> .formats 00a0

  12: Evaluate expression:

  13:   Hex:     00000000`000000a0

  14:   Decimal: 160

  15:   Octal:   0000000000000000000240

  16:   Binary:  00000000 00000000 00000000 00000000 00000000 00000000 00000000 10100000

  17:   Chars:   ........

  18:   Time:    Thu Jan 01 02:02:40 1970

  19:   Float:   low 2.24208e-043 high 0

  20:   Double:  7.90505e-322

So here it is, the unique id of the FontSize property is 160. Let’s dump the contents of the _effectiveValues member and see if the button holds a local value for that _propertyIndex:

   1: 0:010> !da -details 000000ad88d23c58 

   2: Name:        System.Windows.EffectiveValueEntry[]

   3: MethodTable: 000007f9e58e5f08

   4: EEClass:     000007f9e57829c0

   5: Size:        376(0x178) bytes

   6: Array:       Rank 1, Number of elements 22, Type VALUETYPE

   7: Element Methodtable: 000007f9e58e0dc0

   8: [0] ...

   9: [1] ...

  10: ...

  11: [8] 000000ad88d23ce8

  12:     Name:        System.Windows.EffectiveValueEntry

  13:     MethodTable: 000007f9e58e0dc0

  14:     EEClass:     000007f9e5780fb0

  15:     Size:        32(0x20) bytes

  16:     File:        C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\WindowsBase\v4.0_4.0.0.0__31bf3856ad364e35\WindowsBase.dll

  17:     Fields:

  18:                       MT    Field   Offset                 Type VT     Attr            Value Name

  19:         000007f9d56cc938  40013c8        0            System.Object      0     instance     000000ad88c19c88     _value

  20:         000007f9d56e0770  40013c9        8             System.Int16      1     instance                  160     _propertyIndex

  21:         000007f9e5b7b250  40013ca        a             System.Int16      1     instance                    2     _source

  22: [9] ...

  23: ...

  24: [21] ...

There is a local value. The final step is to dump it and find out that the value of the FontSize property is 50.0:

   1: 0:010> !do 000000ad88c19c88

   2: Name:        System.Double

   3: MethodTable: 000007f9d56e1210

   4: EEClass:     000007f9d50e78e0

   5: Size:        24(0x18) bytes

   6: File:        C:\WINDOWS\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll

   7: Fields:

   8:               MT    Field   Offset                 Type VT     Attr            Value Name

   9: 000007f9d56e1210  40003c3        8        System.Double  1 instance 50.000000 m_value

  10: 000007f9d56e1210  40003c4      c30        System.Double  1   shared           static NegativeZero

In case there is no local value, the value will be the default value, which is part of the dependency property _defaultMetadata member (12.0 in our case):

   1: 0:011> !do 000000ad88c01750

   2: Name:        System.Windows.DependencyProperty

   3:

   4: Fields:

   5:               MT    Field   Offset                 Type VT     Attr            Value Name

   6: 000007f9d56cc358  4001397        8        System.String  0 instance 000000ad88bddf68 _name

   7: ...

   8: 000007f9e58dd498  400139a       20 ....PropertyMetadata  0 instance 000000ad88c016b0 _defaultMetadata

   9: ...

  10: 000007f9e5b7b190  400139d       40         System.Int32  1 instance          1638560 _packedData

  11: ...

  12:  

  13: 0:011> !do 000000ad88c016b0 

  14: Name:        System.Windows.FrameworkPropertyMetadata

  15:

  16: Fields:

  17:               MT    Field   Offset                 Type VT     Attr            Value Name

  18: 000007f9d56cc938  40014de        8        System.Object  0 instance 0000001509321698 _defaultValue

  19: ...

  20:  

  21: 0:011> !do 0000001509321698

  22: Name:        System.Double

  23:

  24: Fields:

  25:               MT    Field   Offset                 Type VT     Attr            Value Name

  26: 000007f9d56e1210  40003c3        8        System.Double  1 instance 12.000000 m_value

  27: 000007f9d56e1210  40003c4      c30        System.Double  1   shared           static NegativeZero

How to find out which dependency property holds a reference to an object

This is another common scenario which you usually encounter when you look for memory leaks and see that an object is being referenced by the _effectiveValues array of some dependency object. As described before, it means that the object is set as the local value of one of the dependency object’s dependency properties, and it can be helpful to know exactly which property it is.

To demonstrate how to do it, I will use the following trivial case in which the MainWindowViewModel is being referenced by the EffectiveValueEntry array of a MainWindow instance:

   1: 0:010> !gcroot 0000001b80128f58

   2: Thread c74:

   3:     0000001bec47e750 000007fab2fc7640 System.Windows.Threading.Dispatcher.GetMessage(System.Windows.Interop.MSG ByRef, IntPtr, Int32, Int32)

   4:         rbp+8: 0000001bec47e788 (pinned)

   5:             ->  0000001b80011230 System.Windows.Threading.Dispatcher

   6:             ->  0000001b8015f440 System.EventHandler

   7:             ->  0000001b8012d108 System.Object[]

   8:             ->  0000001b80084e98 System.EventHandler

   9:             ->  0000001b80084060 System.Windows.Media.MediaContext

  10:             ->  0000001b800842a0 System.Collections.Generic.Dictionary`2[[System.Windows.Media.ICompositionTarget, PresentationCore],[System.Object, mscorlib]]

  11:             ->  0000001b8012c280 System.Collections.Generic.Dictionary`2+Entry[[System.Windows.Media.ICompositionTarget, PresentationCore],[System.Object, mscorlib]][]

  12:             ->  0000001b8012b2d8 System.Windows.Interop.HwndTarget

  13:             ->  0000001b8005d3e0 DependencyProperties.MainWindow

  14:             ->  0000001b8015b500 System.Windows.EffectiveValueEntry[]

  15:             ->  0000001b80128f58 DependencyProperties.MainWindowViewModel

The first step is to identify the property index for which the reference is being held. For that, you need to dump to contents of the EffectiveValueEntry array and look for the object’s reference (0000001b80128f58 in our case) in the _value member of its entries.

   1: 0:010> !da -details 0000001b8015b500

   2: Name:        System.Windows.EffectiveValueEntry[]

   3: MethodTable: 000007fab3025f08

   4: EEClass:     000007fab2ec29c0

   5: Size:        456(0x1c8) bytes

   6: Array:       Rank 1, Number of elements 27, Type VALUETYPE

   7: Element Methodtable: 000007fab3020dc0

   8: [0] ...

   9: ...

  10: [7] ...

  11: [8] 0000001b8015b590

  12:     Name:        System.Windows.EffectiveValueEntry

  13:     MethodTable: 000007fab3020dc0

  14:     EEClass:     000007fab2ec0fb0

  15:     Size:        32(0x20) bytes

  16:     File:        C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\WindowsBase\v4.0_4.0.0.0__31bf3856ad364e35\WindowsBase.dll

  17:     Fields:

  18:                       MT    Field   Offset                 Type VT     Attr            Value Name

  19:         000007fa9b9bc938  40013c8        0            System.Object      0     instance     0000001b80128f58     _value

  20:         000007fa9b9d0770  40013c9        8             System.Int16      1     instance                   28     _propertyIndex

  21:         000007fab32bb250  40013ca        a             System.Int16      1     instance                   11     _source

  22: [9] ...

  23: ...

  24: [26] ...

As you can see, the 8th entry holds that value and the corresponding property index is 28. This is the index in the static list of dependency properties where that dependency property is being held (because dependency properties are added sequentially). All that is left to be done is to dump the contents of the static array that holds all the registered dependency properties and find the property that is stored at index 28. That array is a static member of the DependencyProperty class and you can reach it in the following way:

   1: 0:010> !name2ee WindowsBase.dll System.Windows.DependencyProperty

   2: Module:      000007fab2e71000

   3: Assembly:    WindowsBase.dll

   4: Token:       0000000002000250

   5: MethodTable: 000007fab301f7b0

   6: EEClass:     000007fab2e96528

   7: Name:        System.Windows.DependencyProperty

   8:  

   9: 0:010> !DumpClass 000007fab2e96528

  10: Class Name:      System.Windows.DependencyProperty

  11: mdToken:         0000000002000250

  12: File:            C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\WindowsBase\v4.0_4.0.0.0__31bf3856ad364e35\WindowsBase.dll

  13: Parent Class:    000007fa9b3237c8

  14: Module:          000007fab2e71000

  15: Method Table:    000007fab301f7b0

  16: Vtable Slots:    4

  17: Total Method Slots:  5

  18: Class Attributes:    100101  

  19: Transparency:        Transparent

  20: NumInstanceFields:   9

  21: NumStaticFields:     6

  22:               MT    Field   Offset                 Type VT     Attr            Value Name

  23: 000007fa9b9bc358  4001397        8        System.String  0 instance           _name

  24: 000007fa9b9be3e0  4001398       10          System.Type  0 instance           _propertyType

  25: 000007fa9b9be3e0  4001399       18          System.Type  0 instance           _ownerType

  26: 000007fab301d498  400139a       20 ....PropertyMetadata  0 instance           _defaultMetadata

  27: 000007fab301d320  400139b       28 ...dateValueCallback  0 instance           _validateValueCallback

  28: 000007fab3025dc8  400139c       30 ...ndencyPropertyKey  0 instance           _readOnlyKey

  29: 000007fab32bb190  400139d       40         System.Int32  1 instance           _packedData

  30: 000007fab3025848  400139e       48 ....InsertionSortMap  1 instance           _metadataMap

  31: 000007fab301d268  400139f       38 ...erceValueCallback  0 instance           _designerCoerceValueCallback

  32: 000007fa9b9bc938  4001396      830        System.Object  0   static 0000001b8000b6b0 UnsetValue

  33: 000007fab3024090  40013a0      828 ...ty, WindowsBase]]  1   static 0000001b90012748 RegisteredPropertyList

  34: 000007fa9b9c0548  40013a1      838 ...ections.Hashtable  0   static 0000001b8000cee8 PropertyFromName

  35: 000007fa9b9bf108  40013a2      5d4         System.Int32  1   static              412 GlobalIndexCount

  36: 000007fa9b9bc938  40013a3      840        System.Object  0   static 0000001b8000cf98 Synchronized

  37: 000007fa9b9be3e0  40013a4      848          System.Type  0   static 0000001b8000cfb0 NullableType

The RegisteredPropertyList static member is the one you’re looking for. It is a value type, so use DumpVC to dump its contents. For a reason which I didn’t had the time to explore, the inner List member does not hold the dependency properties directly, it just holds another list. Dump that list as well:

   1: 0:010> !dumpvc 000007fab3024090  0000001b90012748

   2: Name:        MS.Utility.ItemStructList`1[[System.Windows.DependencyProperty, WindowsBase]]

   3: MethodTable: 000007fab3024090

   4: EEClass:     000007fab2eb40d0

   5: Size:        32(0x20) bytes

   6: File:        C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\WindowsBase\v4.0_4.0.0.0__31bf3856ad364e35\WindowsBase.dll

   7: Fields:

   8:               MT    Field   Offset                 Type VT     Attr            Value Name

   9: 000007fa9b9a0630  4000101        0      System.Object[]  0 instance 0000001b8000b638 List

  10: 000007fa9b9bf108  4000102        8         System.Int32  1 instance      -2147436880 Count

  11:  

  12: 0:010> !do 0000001b8000b638

  13: Name:        MS.Utility.ItemStructList`1[[System.Windows.DependencyProperty, WindowsBase]]

  14: MethodTable: 000007fab3024090

  15: EEClass:     000007fab2eb40d0

  16: Size:        32(0x20) bytes

  17: File:        C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\WindowsBase\v4.0_4.0.0.0__31bf3856ad364e35\WindowsBase.dll

  18: Fields:

  19:               MT    Field   Offset                 Type VT     Attr            Value Name

  20: 000007fa9b9a0630  4000101        8      System.Object[]  0 instance 0000001b8000b6c8 List

  21: 000007fa9b9bf108  4000102       10         System.Int32  1 instance              412 Count

Finally, you can dump the item at index 28, to find the dependency property for which the reference is being held:

   1: 0:010> !da -start 28 -length 1 -details 0000001b8000b6c8 

   2: Name:        System.Windows.DependencyProperty[]

   3: MethodTable: 000007fa9b9a0630

   4: EEClass:     000007fa9b3d1858

   5: Size:        6176(0x1820) bytes

   6: Array:       Rank 1, Number of elements 768, Type CLASS

   7: Element Methodtable: 000007fab301f7b0

   8: [28] 0000001b80034548

   9:     Name:        System.Windows.DependencyProperty

  10:     MethodTable: 000007fab301f7b0

  11:     EEClass:     000007fab2e96528

  12:     Size:        88(0x58) bytes

  13:     File:        C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\WindowsBase\v4.0_4.0.0.0__31bf3856ad364e35\WindowsBase.dll

  14:     Fields:

  15:                       MT    Field   Offset                 Type VT     Attr            Value Name

  16:         000007fa9b9bc358  4001397        8            System.String      0     instance     0000001b80033248     _name

  17:         000007fa9b9be3e0  4001398       10              System.Type      0     instance     0000001b800025f8     _propertyType

  18:         000007fa9b9be3e0  4001399       18              System.Type      0     instance     0000001b80030820     _ownerType

  19:         000007fab301d498  400139a       20     ....PropertyMetadata      0     instance     0000001b800344e8     _defaultMetadata

  20:         000007fab301d320  400139b       28     ...dateValueCallback      0     instance     0000000000000000     _validateValueCallback

  21:         000007fab3025dc8  400139c       30     ...ndencyPropertyKey      0     instance     0000000000000000     _readOnlyKey

  22:         000007fab32bb190  400139d       40             System.Int32      1     instance              4718620     _packedData

  23:         000007fab3025848  400139e       48     ....InsertionSortMap      1     instance     0000001b80034590     _metadataMap

  24:         000007fab301d268  400139f       38     ...erceValueCallback      0     instance     0000000000000000     _designerCoerceValueCallback

  25:         000007fa9b9bc938  4001396      830            System.Object      0       static     0000001b8000b6b0     UnsetValue

  26:         000007fab3024090  40013a0      828     ...ty, WindowsBase]]      1       static     0000001b90012748     RegisteredPropertyList

  27:         000007fa9b9c0548  40013a1      838     ...ections.Hashtable      0       static     0000001b8000cee8     PropertyFromName

  28:         000007fa9b9bf108  40013a2      5d4             System.Int32      1       static                  412     GlobalIndexCount

  29:         000007fa9b9bc938  40013a3      840            System.Object      0       static     0000001b8000cf98     Synchronized

  30:         000007fa9b9be3e0  40013a4      848              System.Type      0       static     0000001b8000cfb0     NullableType

  31:         

  32: 0:010> !do 0000001b80033248     

  33: Name:        System.String

  34: MethodTable: 000007fa9b9bc358

  35: EEClass:     000007fa9b323720

  36: Size:        48(0x30) bytes

  37: File:        C:\WINDOWS\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll

  38: String:      DataContext

  39: Fields:

  40:               MT    Field   Offset                 Type VT     Attr            Value Name

  41: 000007fa9b9bf108  40000aa        8         System.Int32  1 instance               11 m_stringLength

  42: 000007fa9b9bd640  40000ab        c          System.Char  1 instance               44 m_firstChar

  43: 000007fa9b9bc358  40000ac       18        System.String  0   shared           static Empty

In our case, as expected, it is the DataContext property of the window that holds a reference to the MainWindowViewModel instance. As a double check, you can check that the property index is actually 28 (by inspecting the bottom 16 bytes of its _packedData member, as described earlier).

That’s it for today. I hope that your future adventures with WinDbg and WPF will be more effective.

Cross-posted from http://www.programmingtidbits.com/post/2013/05/31/Debugging-Dependency-Properties-with-WinDbg.aspx

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*