Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

A Simple Windows Forms Properties Spy

0.00/5 (No votes)
19 Aug 2003 1  
A utility that can be used to spy the properties of any Windows forms control in the system

Introduction

wfspy is a tool that helps you to view properties of any Windows Form control in the system. I originally needed a small utility that will give me the managed control type name and the assembly name from a window handle, but gradually the tool became sophisticated enough to show all properties of a managed control (and optionally modify them). The only feature currently missing is the spying of events, which I plan to add later on.

wfspy uses Windows hooks to do its job. There are 3 assemblies in the project:

  1. wfspy - a C# executable. This is the main application which has the UI code.
  2. wfspylib - a C# class library that contains utility functions and controls. This library gets injected into the process where the window belongs.
  3. wfspyhook - a managed C++ class library (.dll) which has code to inject a managed assembly into a process.

Using wfspy

The tool itself is pretty simple. The main window shows all the managed windows and their hierarchy, with the desktop window as the root. Any unmanaged window which is not parent of a managed window directly or indirectly, is not shown in the tree. The managed windows are shown using a slightly different icon. You can view the properties of the managed window by clicking on the details button in the main form. This brings up the dialog box with the property grid as shown in the second screen shot. The properties can even be modified in the grid. The next few sections discuss some important aspects of the implementation.

Enumerating Managed Windows

To enumerate managed windows, the standard Win32 API function EnumChildWindows is used. Unmanaged windows are filtered out from the tree if they don't parent any manage windows. In order to find out whether a window is managed or not, its class name is inspected. Any managed window that derives from System.Windows.Forms.Control has class name of the form WindowsForms10.<character sequence>.app<hash code of appdomain>. The character sequence in the middle is the class name of the window that is being superclassed like Button, SysListView32, etc. The following code determines whether a class name is managed or not.

private static Regex classNameRegex = new 
           Regex(@"WindowsForms10\..*\.app[\da-eA-E]*$", 
           RegexOptions.Singleline);
            
public static bool IsDotNetWindow(string className)
{
    Match match = classNameRegex.Match(className);
    return (match.Success);
}

Injecting a .NET Assembly into a Process

The technique to inject a .NET assembly in another process is slightly tricky as unlike functions in a regular Win32 DLL. .NET functions are compiled into native code during run time so the addresses are not static. This problem can be overcome by using managed C++, which allows managed global functions to be exported from an assembly. The exported function is actually a thunk that at runtime, points to the code generated by the JIT compiler. Thus the exported function can be used as a hook procedure. That function can be used to load another assembly. I will cover this technique in detail in another article.

Viewing Properties of a Control

Given an HWND, the managed Control object can be found from the Control.FromHandle method. The properties of the control object can then be viewed in a property grid. There is a problem here the property grid should be created in the process where control object belongs. Luckily, Windows allows a process to create child windows to a parent window belonging to another process. This technique is used to create the property grid from the target process, as a child of a form in the wfspy process. In order to do this, the property grid control is placed in a user control. The CreateParams property of the user control is overridden.

protected override CreateParams CreateParams
{
    get
    {
        System.Windows.Forms.CreateParams cp = base.CreateParams;
        cp.Parent = parentWindow;
        RECT rc = new RECT();
        UnmanagedMethods.GetClientRect(parentWindow, ref rc);
        
        cp.X = rc.left;
        cp.Y = rc.top;
        cp.Width = rc.right - rc.left;
        cp.Height = rc.bottom - rc.top;

        return cp;
    }
}

The parentWindow is actually the handle of a form belonging to the wfspy process. This enables editable property view of a control in the wfspy application.

Known Bugs/ Remarks

The included wfspy works only for .NET windows 1.0.3705. If you want the tool to work with .NET 1.1, please build the code using VS.NET 2003. If you use wfspy to view a window in an application that uses a different .NET version, strange results may occur. Suggestions are welcome on how to fix this bug. I intend to update the article soon with the ability to spy control events.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here