破解 FontDialog
如何隐藏 FontDialog 的 UI 元素(例如,字体大小)。
引言
我们经常希望使用通用对话框(如字体对话框),但不希望用户选择对话框提供的所有属性,这是一个常见的问题。本文展示了我们如何做到这一点,以字体对话框为例。
背景
我确实需要一个没有字体大小属性的字体对话框,用于自动管理字体大小的绘图应用程序,所以我们不希望用户能够更改它。 有两种选择:
- 重写整个字体对话框。
- 破解它。
因为它提供了一个不错的字体预览,所以我决定采用第二种方法。
黑客技术
如果我们有要破解的窗口的句柄,整个问题就非常容易了。 应该添加一些本地函数调用,例如
[DllImport("user32.dll")]
static extern IntPtr GetDlgItem(IntPtr hDlg, int nIDDlgItem);
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
const int SW_HIDE = 0
通用对话框的每个 UI 组件都有一个固定的 ID。 Spy++ 可以帮助我们识别它们。 对于字体大小,这些 ID 是 1090 和 1138;因此,我们可以通过调用来隐藏不需要的组件
ShowWindow(GetDlgItem(dialogHandle, 1090), SW_HIDE);
ShowWindow(GetDlgItem(dialogHandle, 1138), SW_HIDE);
查找对话框句柄
关键问题是识别 dialogHandle
。 示例展示了三种可能的解决方案,其中两种很流行,但存在自身的问题。
FindWindow
:让我们在使用FindWindow()
本地调用显示字体对话框之后找到它。 在显示对话框之前,启动一个计时器,如果在找到对话框时完成。 这种方法存在以下问题- 计时器的正确间隔是多少? 如果选择大量时间,用户可以看到不需要的 UI 元素,这些元素稍后会消失。
FindWindow
将窗口的标题作为输入参数。 如果 Windows 不是英文,字体对话框的标题不是“字体”。- 如果它找不到正确的对话框,它将继续运行到永远。
GetActiveWindow
:让我们假设活动窗口是我们的字体对话框; 再次,在显示之前启动一个计时器,并希望计时器第一次触发时字体对话框将成为活动窗口。 同样,正确的计时器间隔是未知的,如果我们的字体对话框碰巧不是最顶层的,我们会向另一个应用程序发送命令。- 最佳解决方案:让我们采用本文中描述的方法:在 .NET 中自定义 OpenFileDialog。
我从中删除了所有不需要的代码,以获得我们问题的简单解决方案。 FontDialogEx
类将代替 FontDialog
使用。 它的 ShowDialog
方法创建一个名为 DummyForm
的虚拟的、不可见的窗体。 通过设置名为 UFLAGSHIDE
的标志来实现不可见性。 DummyForm
等待直到它被激活,这可以在它重写的 WndProc(ref Message m)
函数中找到。
protected override void WndProc(ref Message m)
{
if (mWatchForActivate && m.Msg == (int)Win32.WM_ACTIVATE)
{
mWatchForActivate = false;
mFontDialogHandle = m.LParam;
mNativeDialog = new FontDialogNative(m.LParam, mFileDialogEx);
}
base.WndProc(ref m);
}
这是我们可以找到字体对话框句柄的地方。 在找到正确的句柄后创建 FontDialogNative
,因此我们不需要的 UI 元素可以在 FontDialogNative
的构造函数中隐藏。
历史
这是 Font Dialog 破解的第一个版本。