65.9K
CodeProject 正在变化。 阅读更多。
Home

使用资源创建 WTL 对话框、属性表或向导(简单方法)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.95/5 (49投票s)

2004 年 6 月 18 日

GPL3

19分钟阅读

viewsIcon

136128

downloadIcon

4434

在 Visual Studio 中设计完对话框后,使用 WTL 类向导将它们转换为 WTL 类,**而且不仅仅是从 Visual Studio 6 开始!**

The WTL wizards in action: one for dialogs, other for property sheets

目录

引言

在使用 WTL 几年后,我发现有些操作会反复进行

  • 基于资源创建对话框类。
  • 为类添加混入(mixin)(最常见的是 CDialogResize)。
  • 将几个对话框推入属性表或向导。
  • 注册应用程序将处理的扩展程序。

所以,有一天,当我(再次)发现自己复制默认的 AboutDlg.h 文件并为其进行调整时,我问自己:有没有更简单的方法?有什么自动化的东西吗?

好吧,就 Visual Studio 6 而言,实际上有两种方法:插件和宏。由于这项工作对宏来说太繁重了,所以插件是答案。

第三种方法,由 Tage 建议(见下文):一个独立的 .EXE 文件,可以添加到 VS .NET 的“工具”菜单中。对于那些在没有 Microsoft IDE 的情况下进行编码的人来说,它也可以使用。谢谢 Tage!

背景

有两种方法可以为 Visual Studio 6 编写插件:MFC 和 ATL。

在 ATL 中,您创建一个 ATL DLL 项目,添加一个插件对象,然后就可以了!

挑战在于如何访问活动项目 .rc 文件中的资源:基本上,这些资源不属于 Visual Studio 的对象模型(6.0 版本)。解决方案很明显:.dsp.rc 文件是文本文件,资源标记分隔符是已知的(sscanf 分隔符和逗号),并且 <cstdio>(以前称为 <stdio.h>)在此方面非常有帮助。我更喜欢 C 标准 IO 库而不是 Windows API,因为我认为在逐字节读取文件时,它更方便、更简洁。

之后,就只剩下处理字符串了……

安装插件(Visual Studio 6)

将文件 WTLWizard.dll 复制到您选择的目录,然后

  1. 在 Visual Studio 6 中,打开菜单 **“工具/自定义”**
  2. 打开 **“插件和宏文件”** 选项卡
  3. 点击 **“览...”** 按钮
  4. 打开您复制 DLL 的位置。
  5. 开始使用它!

它应该看起来像这样

Installing the Wizard - Tools/Customize Property Sheet

安装独立工具(Visual Studio .NET)

在 VS.NET 或 Visual Studio 2003 中,您可以使用 Wizard 的可执行版本。

这是一个 WTL 对话框应用程序,带有与 VS6 工具栏上的按钮相同的按钮。

要安装它,请将可执行文件复制到您选择的文件夹,打开 “工具|外部工具” 菜单,点击“添加”,然后填写类似的内容

Installing the Wizard - Tools/External Tools

首次运行后,可执行文件将添加几个上下文菜单项,每当您右键单击 .rc 文件时都会显示这些项

The .rc Context Menu

使用 WTL 类向导(两个版本)

仅限 Visual Studio 6:插件

目前,**WTL 类向导**只有一个工具栏,带有四个按钮

The WTL Class Wizard ToolBar

插件工具栏上的第一个按钮,以及可执行文件中的“对话框向导”按钮,使您能够从资源创建 WTL 对话框类。两者都会打开一个向导,让您选择当前打开项目资源脚本中的一个对话框,设置一些选项(例如生成 .cpp 文件、继承 CDialogResize、继承 CPropertyPageImpl 或使用 DDX),甚至选择将附加到子控件的成员变量。

第二个按钮(相当于“属性表向导”)创建一个 WTL 属性表或向导,带有嵌入的对话框成员和一个嵌入的数据类。它会打开另一个向导,让您检查多个对话框资源,这些资源将成为属性表或属性页的成员,决定是否要分离接口与实现(通过使用 .cpp 文件),并在属性表和向导样式之间进行选择。

第三个按钮打开“扩展程序处理程序向导”,它创建一个类,并包含将应用程序注册为扩展程序处理程序的代码;在不同项目中生成此类时,唯一会改变的函数是其析构函数,它负责完成所有实际工作。理想情况下,您应该在关闭应用程序主窗口后立即声明该类的一个变量,并且它应该在调用 _Module.Term() **之前**超出范围。

最后一个按钮将打开您的 Internet 浏览器,显示本文档以供参考。

所有生成的文件都以一些注释开头,包括

  • 作者姓名(也就是您)。如果您至少使用 Windows 2000,则会以“友好格式”显示;否则,将显示您登录系统的用户名。
  • 版权所有者(即 Visual Studio 的许可所有者)。
  • 引用 GPL 许可证,与 WTL 7.5 源代码中的相同,这对于编写开源项目或发布代码的人来说可能很有用。
  • 免责声明,用于类似目的。**请注意:我不是律师**,我也不知道代码将在哪个国家/地区使用,因此如果您认为免责声明与您的源代码无关,请随时删除它。

较新版本的 Visual Studio、非 Microsoft IDE,或记事本及无 IDE:可执行文件

由于可执行版本无法访问您的“活动项目”,因此您必须将所需项目的 .rc(资源)文件 **拖放到** 其主对话框上。您可以从任何 Windows 资源管理器窗口拖动它。

之后,您还可以单击任何按钮来运行与 Visual Studio 6 上看到的向导完全相同的向导。指向此页面的链接(供参考)可在“于...”框中找到。

The Executable Wizard

运行任何向导后,将显示以下对话框,您可以从中复制生成的文件名,并将它们添加到您的项目中

The Executable Wizard - After Running

该对话框是可调整大小的,正如真正的 WTL 对话框一样。

首次运行可执行文件后,它将注册以处理 .rc 文件(资源脚本)。

免责声明。

我不是您的律师。我不是任何人的律师。我不是律师。我不知道您住在哪个国家/地区,或分发您的软件。因此,上述注释的某些部分可能对您完全无关、不当或不足。您可以根据需要随时删除或修改它们。

创建对话框

Using the Dialog Wizard - Page 1

向导的第一个页面显示您资源脚本中的所有对话框。如果您将其作为文本文件编辑,并将对话框注释掉了,它仍然会显示在这里,因为注释会被忽略。

选择一个,然后继续...

Using the Dialog Wizard - Page 1

在第二个页面上,您可以决定对话框类的一些常规设置。

默认的类名和文件名基于对话框资源名称,排除下划线之前的任何前缀,将下划线之后的所有字符大写,并省略所有下划线。如果您选择生成 .cpp 文件,其文件名将与头文件相同,只是扩展名不同。

如果您的对话框是属性页,它将继承自 CPropertyPageImpl,链接到 CPropertyPageImpl 的消息映射,在构造函数中设置一些样式,并覆盖几个可覆盖的函数。如果您的对话框是可调整大小的,它将继承自 CDialogResize 并链接到它,并在其 WM_INITDIALOG 处理程序中调用 DlgResize_Init()
顺便说一句,如果您在 WM_INITDIALOG 处理程序中执行了任何非平凡的初始化,请不要忘记在 WM_DESTROY 处理程序中撤销它。

如果您实现 DDX,则仅创建一个原型映射,因为控件和 DDX 宏之间没有明显的_一一_对应关系。例如,一个数字编辑框可以使用 DDX_FLOATDDX_INTDDX_UINT 进行映射,无论是否进行验证。

Using the Dialog Wizard - Page 1

您可以选择性地创建成员控件变量,这些变量将(使用 operator =)附加到对话框中的控件。

对于您未勾选的那些,将生成注释掉的成员变量和赋值操作,从而允许稍后更改主意。

无论您在此页面上的选择如何,都会为所有按钮生成命令处理程序,并为所有组合框、列表框和扩展组合框的 CBN_SELCHANGELBN_SELCHANGE 通知生成一个处理函数。

如果您在继承自 CDialogImpl 的类中使用了 Rich Edit 控件,则会生成一个构造函数和一个虚析构函数。构造函数确保加载了相关的 DLL(由 CRichEditCtrl::GetLibraryName() 返回),析构函数包含注释掉的代码以在构造函数中加载它时卸载它。

调用 FreeLibrary() 被注释掉的原因是,无模式对话框之间的交互超出了插件的猜测范围。无论如何,Rich Edit DLL(如果使用)应尽可能靠近 _Module.Init() 的调用加载,并在尽可能靠近 _Module.Term() 被调用的地方卸载。当然,向导不会更改它生成的源文件以外的任何源文件,因此使用了上述的规避方法。如果需要,可以复制粘贴。如果您的类继承自 CPropertyPageImpl,则属性表负责加载 DLL。更多内容如下……

创建属性表或向导

Using the Property Page Wizard - Page 1

属性表是对话框的集合,向导是伪装的属性表。您可以在此处选择任意数量的对话框(尽管少于两个可能意义不大),您还可以使用右侧的按钮来移动选定的项目,从而重新排序它们。

在您的属性表类中,将为所有标记的对话框生成嵌入的类。页面顺序将是此处显示的从上到下的顺序。

如果您的任何属性页具有 Rich Edit 控件,则在属性表的构造函数中将加载(如果需要)Rich Edit DLL。

Using the Property Page Wizard - Page 2

最后,您可以选择属性表的类名。由于表本身不来自资源,因此项目的名称(假定与 .rc 文件名称相同)用于生成类名和文件名。

您还可以决定是否将实现细节移到 .cpp 文件中,以及是生成属性页还是向导。

如果您选择向导,则会生成一些条件代码(取决于 _WIN32_IE 的值)。对于 Wizard97 样式,您还可以为第一页和最后一页选择水印,为所有未选择水印的页面选择一个标题。位图是从活动项目中**所有**的位图资源中选择的。

您还可以生成 DDX 原型映射,其限制与对话框类生成所应用的限制相同。

生成的属性表类

该类有几个嵌入的类

  • 一个 CData 类,用于保存属性表及其页面之间共享的所有数据。为每个属性页面中的每个控件创建一个数据项,只要可以合理地猜测默认值,并为编辑框使用 CString。随意删除您不喜欢的内容,这是您的代码!(生成的默认值旨在具有接近 40% 的可用性)。**不**执行重复项检查,因此在多个页面上具有相同 ID 的同类型控件将生成编译时错误(重复标识符)。
  • 每个页面的一个对话框类,以 CData 为模板,并包含指向属性表的 CData 成员的指针,从而使大多数状态在所有页面(和表)之间共享。
  • 属性表的构造函数调用其所有属性页成员上的 AddPage,并设置它们的 CData 指针的值。它还会根据您的选择(是否为向导)设置样式标志。
  • 如果至少有一个属性页中包含 Rich Edit 控件,则表的构造函数会确保已加载相关的 DLL。

您需要做什么?将数据从控件移动到 CData 成员,并且,如果 DoModal 返回 IDOK,则调用一个函数(未生成)来执行有意义的操作。

就是这么简单。

将您的应用程序注册为扩展程序处理程序。

您的程序可能为一种或多种文件类型提供上下文菜单选项,这些文件类型由其扩展名标识,可以是您自己的,也可以与其他程序共享。

例如,Visual Studio 处理多种扩展名:.C, .H, .CPP, .DSP, .DSW, .RC……

如上所述,WTL 类向导的可执行版本还将自身注册为 .RC(资源脚本)文件的处理程序。

此任务足够频繁,值得自动化。

Registering Extensions - The Extension List

在向导的第一页,您将决定您的程序将处理的扩展名列表。

对于每个扩展名,您可以设置几个选项:其文件类型(可能与多个扩展名相同:例如,.htm.html 扩展名相同),您的应用程序是否会为该扩展名提供图标,以及 DDE 设置。

您可以通过在第一次进入此页面时点击“添加”,然后通过点击“克隆”(将选定项的副本添加到列表中,递增“扩展名”字段中的序列号)、“编辑”(编辑当前选定的项)或“删除”(删除当前选定的项)来编辑列表视图中的记录。

生成的类仅在目标计算机上未找到文件类型和图标设置时使用它们,因此,如果这不是您期望的行为,则必须相应地更改生成的代码(注释掉一对“if”)。

Registering Extensions - Editing Settings for an Extension

这仍然是第一个向导页面,编辑器已打开。当您更改“扩展名”字段时,对话框过程将尝试在注册表中查找它,如果找到,其类型将出现在“文档类型”字段中。否则,文档类型将由您的项目名称(从资源文件名获取)、扩展名和序数“1”组成。

在点击“确定”之前,字段不会添加到列表中,因此当编辑器打开时,“下一步”按钮将禁用。

Registering Extensions - The Commands Added to the Context Menu

对于您的程序将支持的每个扩展名,您可以添加一个或多个上下文菜单选项。

“标志”字段启用了这种多重性。例如,WTL 类向导的可执行版本向 .rc 文件菜单添加了四个选项(并非偶然,上面显示了这些选项)。

同样,您会看到熟悉的“添加”、“克隆”、“编辑”和“删除”按钮。

第一个添加的处理程序的默认值为“使用 <项目名称> 打开”,并且没有标志。

Registering Extensions - Editing a Command Added to the Context Menu

这个编辑器要简单得多。

一个有趣的特性是,您可以将项目从一个扩展名复制到另一个扩展名,方法是点击“编辑”,从组合框中选择所需的扩展名,然后点击“确定”。

同样,在继续之前,您必须在“确定”和“取消”之间做出选择。

工作原理

在插件版本中,资源脚本基于活动项目定位,活动项目作为 IDE 环境的成员变量传递给插件。

在可执行版本中,必须将资源脚本名称作为命令行参数提供,或者将其拖放到向导的主对话框上。

当执行命令时,资源脚本的对话框将被加载到一个基于 std::map(作为 WTL::CStrings)的模型中,该模型还包含每个对话框的子控件。

用户(也就是您)可以做出相关选择,最终,如果使用的是插件版本,则会向您的项目中添加一个 .h 文件,或者可选地添加一个 .cpp 文件;在可执行版本中,它们的名称会显示在编辑框中,您可以从中复制。

当然,这些向导存在一些局限性

  • 插件仅适用于 Visual Studio 6。如果您使用的是任何其他环境,例如 VS.NET,请使用可执行版本。
  • 由于 .rc 文件被读取为文本文件,因此目前注释掉的对话框会显示在要从中选择的对话框列表中,并且资源 #includes 被忽略。在使用 IDE 生成的资源文件时,这应该不是问题。
  • DDX 作为原型映射生成,而不是完整的映射,因为控件类型和 DDX 宏之间没有_一一_对应关系:例如,一个数字编辑框可以映射到一个整数或一个浮点数,无论是否进行边界检查。
  • 恐怕我在编写向导时可能忽略了一些 .NET 或 CE 特定的问题。如果有,请通知我(在下方),我很乐意进行任何必要的更改。

亮点

Visual Studio 6 插件

在编写本文时,我发现了一些在为 Visual Studio 6 编写带有 WTL 用户界面的 ATL 插件时可能有所帮助的内容

它像任何其他 WTL 应用程序一样简单!

在开发插件时,一个“奇怪”的部分是测试代码,因为您的调试会话的可执行文件是 IDE 本身。最好在工作区中有两个项目,一个用于插件,另一个用于常规 WTL 应用程序,后者可用于原型设计和初步测试。为了测试插件,您需要安装它,将另一个项目设置为活动项目,并运行相关命令,对插件源代码进行所有相关更改和添加,在 工具/自定义/插件和宏文件 复选列表中取消选中该插件,重新编译(无需关闭 Visual Studio),然后再来一次……

为了获取 OutputDebugString()(或 ATLTRACE)输出,需要一个外部工具,网络上有许多此类工具可用。另一方面,消息框就在那里帮助您……

工具和插件

另一方面,开发 VS.NET 插件并非易事。

VS.NET 中的对象模型要强大得多,但因此也远比复杂。例如,它允许访问项目中的资源(我在 VS6 中非常怀念这一点)、菜单、子菜单、输出窗格、几乎屏幕上所有可见内容的上下文菜单:对于专业的插件编写者来说,梦想成真。
但是,对于“普通程序员”来说,想要自动化一些经常性任务,这个工具可能太重了。
而且别忘了,当前项目不一定是 C++ 项目。

一个 VS6 ATL 插件,从生成的那一刻起,其图像列表中就有两个按钮的工具栏,加载第一个按钮的代码,以及当点击该按钮时执行的命令。

比我更懂的人(Oz Solomon,在他的这篇文章中)解释了编写 VS.NET 插件的一些(正面和负面)影响。

对于像我这样试图移植一个特定目的的插件的人来说,学习曲线似乎有点太陡峭了。

另一方面,可执行的独立工具完全独立于 IDE,这意味着它将能抵御 IDE 的大多数更改(除非 .rc 文件格式发生剧烈变化),并且可以被那些使用命令行编译器甚至完全没有 IDE 的人使用。

外部工具的缺点在于它是外部的,它不知道 IDE,所以它无法,例如,从活动项目中推断出活动项目资源文件的名称,或者将生成的文件添加到活动项目中。当然,您可以自己作为文本文件处理 .dsp.vcproj 文件。

一如既往,总有选择的空间。

拖放文件。

这是由一个混入(mixin)完成的,定义在 DropFileHandler.h 中,对对话框、视图或控件都很有用。

头文件(包含在可执行文件的源代码中,查找 DropFileHandler.h)非常明确,但您可以在 将文件拖放到 WTL 窗口中(简单方法) 中阅读更多相关内容。

WTL 宏

对于更简单的任务(例如,为类添加混入,这至少需要两个修改:将其添加到继承列表中并链接到其消息映射),宏是一个更温和但功能较弱的工具。项目源代码中附带了一个小的宏文件。它绝不是杰作,只是一个可能的方向的提示……

您的任务,如果您决定接受

明确地说:这个程序仍有**大量**的增长空间,所以,如果您有任何改进的想法,或想分享类似的工具,我很乐意听取。

我希望您发现这个项目和我在 CodeProject 上找到的许多其他项目一样有用。

编程愉快!

免责声明

作者“按原样”提供向导、代码和信息,并免除任何明示或暗示的保证,包括但不限于适销性和特定用途的适用性暗示保证。在任何情况下,作者均不对因使用本软件而产生的任何直接、间接、偶然、特殊、示范性或后果性损害(包括但不限于采购替代商品或服务;使用、数据或利润损失;或业务中断)负责,无论是由合同、严格责任还是侵权行为(包括疏忽或其他)引起的任何理论引起的,即使已告知可能发生此类损害。

历史

  • 2004 年 6 月 - 创建。
  • 2004 年 8 月
    • 新增功能
      • 为扩展程序生成器添加了上下文菜单注册。
      • 添加了可执行版本。
      • 将本文档的链接添加到插件和可执行文件中,供参考。
      • 如果您使用 Windows 2000 或更高版本,版权中的姓名将是“友好名称”格式:否则,仍将是您的登录名。
      • 生成的一些代码,特别是在 .CPP 文件中,现在是版本相关的,基于 _MSC_VER_ATL_VER,因为 VC6 和 VC7 在模板方面有不同的方法。
    • 修复
      • CData(嵌入在生成的属性表中)现在是结构体,而不是类,以便所有成员默认都是公共的。
      • 修复了生成代码中的一些缩进错误,但仍有相当多的要修复……
      • 修复了 Aaron Hudon 提到的由未初始化变量引起的错误。
      • 生成的代码在 IDOKIDCANCEL 处理程序中不再为属性页调用 EndDialog(wID)
© . All rights reserved.