Background:
Coming from the land of Windows Forms, I have grown addicted to having tools like wfSpy and Hawkeye. A software engineer at the presentation layer needs to have tools like these in order to inspect the objects in a running application and change properties on the fly.
Very soon after jumping in head first into WPF, I discovered Pete Blois’ utility: Snoop. It was definitely what I was looking for. Unfortunately, after playing with it for a while, I learned that it didn’t support our WPF interop scenario. In particular, our application is a Windows Forms application that is hosting WPF content through ElementHost. When you use ElementHost, you don’t have an Application object and Snoop coughs up a hairball because of that.
After that, Josh Smith created Woodstock and Mole soon followed. These are both debugger visualizers, and in particular, Mole is extremely nice and powerful. The Achilles tendon of debugger visualizers, however, is that you have to be running in the Visual Studio debugger. There are many cases where you can’t or don’t want to do this.
Josh Smith realized this and has created Crack.NET. This is a heap walker in the vein of Snoop and Mole … but it can be run outside of the debugger. It is a useful tool in its own right, and of particular note, you can modify the object you’ve ‘cracked’ using IronPython in the tool’s Scriptorium area.
However, even given the above, I was still missing the functionality of good, old wfSpy. Snoop was very close to what I wanted, it just didn’t handle the WPF interop story.
So, after reading this post in the WPF forums, I finally broke down, dove into the source code and figured out why. It was rather easy to fix, actually, and here is what I had to do.
The Fix:
In the Enqueue method of the DelayedCall class, you need to get hold of the Dispatcher from something other than the Application.Current object. Here is the whole Enqueue method:
public void Enqueue() { if (!this.queued) { this.queued = true; Dispatcher dispatcher = null; if (Application.Current == null) dispatcher = Dispatcher.CurrentDispatcher; else dispatcher = Application.Current.Dispatcher; dispatcher.BeginInvoke ( this.priority, new DispatcherOperationCallback(this.Process), null ); } }
In the Inspect method of the SnoopUI class, you once again need to protect yourself from not having an Application object:
public void Inspect(object target) { this.rootObject = target; this.Load(target); this.CurrentSelection = this.root; this.OnPropertyChanged("Root"); if (Application.Current != null) this.Owner = Application.Current.MainWindow; this.Show(); }
After those two changes, you can Snoop WPF content hosted in a Windows Forms application through ElementHost. Do I hear a ‘hallelujah’?
On a related note:
One thing I should mention is that property editing is broken in Snoop 2.0. In the WPF forum post mentioned above, Mark points out how to fix this. You just need to add the following line in the correct place in the PropertyInformation constructor:
binding.Mode = property.IsReadOnly ? BindingMode.OneWay : BindingMode.TwoWay;
However, this doesn’t seem to fix it completely when you are running in a WPF interop scenario … because there is still an issue to find and fix. Namely, you can’t use the keyboard to modify the property values. However, I have discovered that you can paste a value in using the clipboard. If you find out how to fix this, please let me know.
Finally:
Mark also has fixes for a couple other items. One takes care of an issue when the nesting gets too deep and another is a fix for popping the context after you ‘delve’ in on something.
I hope this helps someone. I personally am happy to the point of tears that I will now be able to Snoop our application!
Update:
I have found the keyboard issue and have fixed it. I have also uncovered a couple other changes that you need to make. For example, the Previewer wasn’t working. See my next blog post for details.
Update:
(The source code and binaries for Snoop are now (see posts: 1, 2) up on CodePlex. Please go download it from there as that code will contain the latest and greatest bits.)