一个简单的 Windows Forms 属性监视器






4.98/5 (48投票s)
一个可以用来监视系统中任何 Windows 窗体控件属性的实用工具
引言
wfspy是一个可以帮助您查看系统中任何Windows Form控件属性的工具。我最初需要一个小工具,能够根据窗口句柄获取托管控件的类型名称和程序集名称,但后来这个工具逐渐变得足够强大,可以显示托管控件的所有属性(并可选地修改它们)。目前唯一缺少的功能是监视事件,我计划以后添加此功能。
wfspy使用Windows钩子来完成其工作。该项目中有3个程序集
wfspy
- 一个C#可执行文件。这是具有UI代码的主应用程序。wfspylib
- 一个C#类库,其中包含实用函数和控件。该库会被注入到窗口所属的进程中。wfspyhook
- 一个托管C++类库(*.dll*),其中包含将托管程序集注入进程的代码。
使用wfspy
该工具本身非常简单。主窗口显示所有托管窗口及其层级结构,以桌面窗口为根。任何不直接或间接作为托管窗口父级的非托管窗口都不会显示在树中。托管窗口使用略有不同的图标显示。您可以通过单击主窗体中的“详细信息”按钮来查看托管窗口的属性。这将打开一个对话框,其中包含属性网格,如第二个屏幕截图所示。属性甚至可以在网格中修改。接下来的几节将讨论实现中的一些重要方面。
枚举托管窗口
为了枚举托管窗口,使用了标准的Win32 API函数EnumChildWindows
。如果非托管窗口不作为任何托管窗口的父窗口,则会从树中过滤掉。为了确定一个窗口是否为托管窗口,会检查其类名。任何派生自System.Windows.Forms.Control
的托管窗口,其类名形式为WindowsForms10.<字符序列>.app<应用程序域的哈希码>
。中间的字符序列是正在被超类化的窗口的类名,例如Button
、SysListView32
等。以下代码用于确定类名是否为托管的。
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);
}
将.NET程序集注入进程
将.NET程序集注入另一个进程的技术有点棘手,因为与常规Win32 DLL中的函数不同。.NET函数在运行时被编译成原生代码,因此地址不是静态的。这个问题可以通过使用托管C++来解决,它允许从程序集中导出托管的全局函数。导出的函数实际上是一个在运行时指向JIT编译器生成的代码的“thunk”。因此,导出的函数可以用作钩子过程。该函数可用于加载另一个程序集。我将在另一篇文章中详细介绍这项技术。
查看控件的属性
给定一个HWND
,可以通过Control.FromHandle
方法找到托管的Control
对象。然后可以在属性网格中查看控件对象的属性。这里有一个问题,属性网格应该在控件所属的进程中创建。幸运的是,Windows允许一个进程创建属于另一个进程的父窗口的子窗口。这项技术被用来将属性网格作为wfspy
进程中某个窗体的子项,在目标进程中创建。为了做到这一点,属性网格控件被放置在一个用户控件中。用户控件的CreateParams
属性被重写。
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;
}
}
parentWindow
实际上是属于wfspy
进程的一个窗体的句柄。这使得在wfspy
应用程序中可以进行控件的可编辑属性视图。
已知错误/备注
包含的wfspy
仅适用于.NET Windows 1.0.3705。如果您希望该工具适用于.NET 1.1,请使用VS.NET 2003构建代码。如果您使用wfspy
查看使用不同.NET版本的应用程序中的窗口,可能会出现奇怪的结果。欢迎就如何修复此错误提出建议。我打算尽快更新本文,加入监视控件事件的功能。