在Visual C++中定制AppWizard
第三部分 定制一个高级的AppWizard
我们在第二部分中示范的AppWizard例子很简单,没有任何实用性。在这一部分我们将讨论几个关于制作AppWizard的高级话题。然后利用VC提供的Custom
AppWizard来创建一个在编程中非常实用的AppWizard。与MFC AppWizard(exe) 产生的常规应用程序相比,用这个定制的AppWizard所创建的工程构造出来的应用程序有两个定制特点:
一是所有程序都会有一个定制的“关于”对话框,在这个对话框中显示自己或公司的有关信息,对话框中还有一个将用户定向到Web站点的静态文字控制或图像(icon和bmp)。
二是工程中每一个源代码文件(*.h和*.cpp)的最上面都会有程序编写着的名字及程序创建日期以及简单的程序说明和注释。
这一部分要介绍的主要技术包括:
1、 如何定义和添加AppWizard要用到步进对话框。
2、 如何将Custom AppWizard的专用宏添加到字典中。
3、 如何修改定制AppWizard要用到的模板文件,包括inf文件,资源模板文件等。
4、 将输入信息存储到注册表中,使得每一个工程的公共信息都不用重复输入。
下面我们就开始吧: 进入Visual C++开发环境,如图一:
图一
选择“Project”标签,工程名字可以随便取。这里我取的名字是“VckbaseWiz”,其它选项都默认。
然后单击OK。进入下一个对话框。如图二:
图二
因为我们要建一个标准的MFC AppWizard,所以选择“Standard MFC AppWizard steps”单选按钮。AppWizard的命名最好规范一些,这样便于记忆和辨认。与AppWizard的工程名不同,这个名字要在Project类型清单中列出。我们把它命名为“MFC
AppWizard(exe)——VC知识库”。因为在我们创建的这个Custom AppWizard中有一个额外的对话框,所以在设置步进步骤的数目时输入1。单击“Next”进入下一个对话框。如图三:
图三
单选按钮部分选择“MFC AppWizard Executable”,语言支持部分选择 “中文[中国](APPWZCHS.DLL)”。然后单击“Finish”进入确认对话框。单击“OK”开始产生定制AppWizard的程序代码。
添加定制的对话框
因为我们的Custom AppWizard有一个额外的对话框。所以我们首先要定制这个对话框的模板资源,以便它能收集输入信息,今后用此定制AppWizard创建的所有应用程序的“关于”对话框中都会显示这些信息。选择“ResourceView”标签,打开工程资源表中的“Dialog”。你会发现有一个原始对话框,其ID是IDD_CUSTOM1。定制后的对话框应该如图四:
图四
表一中是对话框中编译框控制的ID,注意这里的“程序介绍”和“代码注释”编辑框控制的风格属性都要设置成“Multiline”。
控制
控制ID
程序员编辑框
IDC_EDT_PROGRAMMER
Web 站点编辑框
IDC_EDT_WEB_PAGE
程序介绍编辑框
IDC_EDT_GENERAL_INFO
代码注释编辑框
IDC_EDT_COMMENT_INFO
表一 对话框中的控制的资源IDs
添加完对话框的资源,我们还要为对话框控制定义成员变量。进入菜单“View|ClassWizard”,选择“Member Variables”标签,程序变量的类型都是CString类型,名称分别为:m_strProgrammer、m_strWebPage、m_strGeneralInfo、m_strCommentInfo。
接下来是实现CCustom1Dlg对话框类初始化成员函数OnInitDialog()。在OnInitDialog()的return语句前面添加如下代码
//
VckbaseWizaw.m_Dictionary.Lookup("PROGRAMMER", m_strProgrammer);
VckbaseWizaw.m_Dictionary.Lookup("WEB_PAGE", m_strWebPage);
VckbaseWizaw.m_Dictionary.Lookup("GENERAL_INFO", m_strGeneralInfo);
VckbaseWizaw.m_Dictionary.Lookup("COMMENT_INFO", m_strConmmentInfo);
UpdateData(FALSE);
//
此段代码的作用是从Dictionary字典中获取定制AppWizard宏的值。VckbaseWizaw是一个CVckbaseWizAppWiz(派生于CCustomAppWiz)类型的全局对象,它在VckbaseWizaw.h中定义。接下来是从CCustom1Dlg的构造函数中删除初始化代码,因为它们的值将在CVckbaseWizAppWiz::InitCustomAppWiz()函数中初始化。
我们还要做一件事情就是存储输入对话框中的数据,也就是说要用创建新工程时输入的数据更新Dictionary字典。这件事情要在CCustom1Dlg::OnDismiss()函数中完成。在CCustom1Dlg::OnDismiss()的return语句前面加入以下代码:
//
VckbaseWizaw.m_Dictionary.SetAt("PROGRAMMER", m_strProgrammer);
VckbaseWizaw.m_Dictionary.SetAt("WEB_PAGE", m_strWebPage);
VckbaseWizaw.m_Dictionary.SetAt("GENERAL_INFO", m_strGeneralInfo);
VckbaseWizaw.m_Dictionary.SetAt("COMMENT_INFO", m_strCommentInfo);
CTime date = CTime::GetCurrentTime();
CString szDate = date.Format( "%A, %B %d, %Y" );
VckbaseWizaw.m_Dictionary["DATE_INFO"] = szDate;
return TRUE;
//
如果你现在构造这个定制的AppWizard并用它来创建新的应用程序的话,你可以看到刚才创建的对话框,但是还有问题,那就是如何将输入对话框的值作为宏存储在AppWizard的字典中,以便今后在新的工程中使用?答案是使用模板文件中的占位符,AppWizard正是用这些包含有占位符的模板文件来构造新的工程文件。在下面的主题中,我们将讨论如何创建新的模板文件。
创建自己的模板文件
对于一个用AppWizard创建的默认的MFC程序来说,用于定义“关于”对话框对象和App对象的文件是相同的。我们在本文中定制的AppWizard除了要产生常规的新工程文件模板以外,还要创建一个全新的模板文件——About.h。这个文件必须存放在AppWizard工程的Template文件夹中。下面是About.h的代码:
/////////////////////////////////////////////////////////////////////////////
// Project:$$ROOT$$
// Author:$$PROGRAMMER$$
// Date:$$DATE_INFO$$
// Description:$$COMMENT_INFO$$
//
/////////////////////////////////////////////////////////////////////////////
#pragma once
#include "StatLink.h"
/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App $$Root$$
class CAboutDlg : public CDialog
{
public:
CAboutDlg();
protected:
CStaticLink m_wndLink1;
CStaticLink m_wndLink2;
CStaticLink m_wndLink3;
CStaticLink m_wndLink4;
// Dialog Data
//{{AFX_DATA(CAboutDlg)
enum { IDD = IDD_ABOUTBOX };
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CAboutDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
//{{AFX_MSG(CAboutDlg)
virtual BOOL OnInitDialog();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
//{{AFX_DATA_INIT(CAboutDlg)
//}}AFX_DATA_INIT
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CAboutDlg)
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(CAboutDlg)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
BOOL CAboutDlg::OnInitDialog()
{
CDialog::OnInitDialog();
m_wndLink1.m_link = _T("http://www.vckbase.com");
m_wndLink2.m_link = _T("http://www.vckbase.com");
m_wndLink3.m_link = _T("mailto:vckbase@publik.hk.hi.cn");
m_wndLink4.m_link = _T("http://www.vckbase.com");
m_wndLink1.SubclassDlgItem(IDC_STATIC_ICON, this);
m_wndLink2.SubclassDlgItem(IDC_STATIC_TEXT, this);
m_wndLink3.SubclassDlgItem(IDC_STATIC_MAIL, this);
m_wndLink4.SubclassDlgItem(IDB_STATIC_IMG, this);
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
IDC_STATIC_ICON、IDC_STATIC_TEXT、IDC_STATIC_MAIL、IDB_STATIC_IMG是“关于”对话框中要用到的四个控制,它们是在Dlgres.h和resource.h中定义的,稍候我们在修改资源模板文件时会定义这四个控制ID。
About.h的代码中包含了OnInitDialog()处理。当它被调用时,做一些控制的初始化。接下来我们要做的事情是把模板添加到工程中。
前面我们讲过,Custom AppWizard创建新工程的时候,由MFCAPWZ.DLL负责用模板文件来生成新工程的源文件。那么如何告诉AppWizard除了要产生默认的源文件以外,你还要求创建一个新模板文件呢?实际上,要解决这个问题需要两个步骤。第一步,你必须更新资源文件,在创建新工程时,将自定义资源插入到每一个Custom
AppWizard创建的文件中。为此,必须以文本模式打开.rc文件,定位到下面注释的位置:
//TEMPLATE
你应该看到这个注释行的后面列出了所有模板文件,它们都是定制AppWizard要用来生成新工程源文件的模板。我们要在其中加入一个新的模板文件——About.h,将下面这几行代码加到模板文件清单中:
//
ABOUT.H TEMPLATE DISCARDABLE "template\\about.h"
VCKBASELOGO.BMP TEMPLATE DISCARDABLE "template\\vckbaselogo.bmp"
HYPRLINK.H TEMPLATE DISCARDABLE "template\\HyprLink.h"
STATLINK.H TEMPLATE DISCARDABLE "template\\StatLink.h"
STATLINK.CPP TEMPLATE DISCARDABLE "template\\StatLink.cpp"
//
这里hyprlink.h、statlink.h、statlink.cpp三个文件是超链接类,它是由MSDN 专栏作家Paul Dilascia
编写的可重用类,很多读者一定熟悉Paul 在MSDN的专栏——《C++ Q&A》,他的大多数文章的示例代码都用到这个类在“关于”对话框中创建静态超链接。笔者深受启发,在VC知识库中也多次使用这个类来做Demo程序的“关于”对话框。但每次做都要去重复定制“关于”对话框岂不是很累。所以才决定做一个自己的AppWizard。
下一步,我们的任务是修改newproj.inf文件
修改newproj.inf文件
这个文件乍一看有点怪模怪样,它位于AppWizard工程的Template目录。在创建新的工程文件时,MFCAPWZ.DLL需要用到这个文件。前面我们提到过占位符,它以“$$”作为前缀和后缀,并且它与AppWizard宏的名字相关联,宏名所对应的宏的值存储在Dictionary字典中。除此之外,“$$”也被用于表示某种命令——这些命令被称为AppWizard指令,它们被用于处理模板文件。
Newproj.inf中的第一行指令是注释行,用“$$//”表示,这四个字符后的任何文本都被MFCAPWZ.DLL忽略,不予解析。打开newproj.inf文件,其第一行就是:
$$// newproj.inf = template for list of template files
通常在一个模板文件被转换为新工程中的源文件时,宏的作用是命名目的文件。但是,某些标准文件的名字与所建工程类型无关,不管创建什么样的工程,其名字都是一样的。例如:stdafx.h和stdafx..cpp。下面的代码行告诉MFCAPWZ.DLL将stdafx.h和stdafx.cpp文件拷到新工程文件夹,文件名不变。这里要注意两个文件名(模板文件和目的文件)之间使用一个Tab键分开,这一点很重要,而且经常被忽略。如果你是手工编写此代码行,必须保证两个文件之间只能是一个Tab,不能是别的任何字符。
stdafx.h StdAfx.h stdafx.cpp StdAfx.cpp newproj.inf文件中的下一条指令是$$IF。它检查括弧中宏的布尔状态。注意这里宏的使用方法,$$IF中用的不是宏本身,换句话说,如果你想在代码中引用宏的话,必须加上前缀和后缀$$,如$$PROGRAMMER$$。
参见下面的代码段,其前两行表示的意义是:如果此工程不是DLL并且不是基于对话框的程序,则将模板文件Frame.h和Frame.cpp拷贝成名字为frame_hfile
和frame_ifile宏所表示的文件名。你可能还记得用AppWizard创建MDI程序时最后一个对话框可以让你根据不同的类命名文件,因为程序员可以改变这些类的名字,其对应的值存储在宏中。这个对话框就象我们在前面定制对话框那样,使用输入对话框的文件名并更新Dictionary字典中的宏。请看一下代码如何命名目的文件:
$$IF(!PROJTYPE_DLL)
$$IF(!PROJTYPE_DLG)
frame.h $$frame_hfile$$.h
frame.cpp $$frame_ifile$$.cpp
下面的$$IF指令和前面的两个$$IF的作用一样,唯一不同的是根据这个宏判断的结果导致另一个从模板到文件的拷贝。
$$IF(MDICHILD)
childfrm.h $$child_frame_hfile$$.h
childfrm.cpp $$child_frame_ifile$$.cpp
最后,每一个$$IF指令都必须有匹配的$$ENDIF指令。此外,指令行末尾可以加入类似C++的注释,如:
$$ENDIF //MDICHILD
$$ENDIF //!PROJTYPE_DLG
现在你应该很容易理解newproj.inf文件了,我们来加入自己的代码,首先,在文件最上面(在注释的后面)加入以下代码,记住在模板文件和目的文件之间是一个Tab键。
//
$$IF(PROJTYPE_DLG)
$$IF(ABOUT)
about.h about.h
HYPRLINK.H HyprLink.h
STATLINK.CPP StatLink.cpp
STATLINK.H StatLink.h
$$ENDIF //ABOUT
$$ELIF(PROJTYPE_MDI)
about.h about.h
HYPRLINK.H HyprLink.h
STATLINK.CPP StatLink.cpp
STATLINK.H StatLink.h
$$ELIF(PROJTYPE_SDI)
about.h about.h
HYPRLINK.H HyprLink.h
STATLINK.CPP StatLink.cpp
STATLINK.H StatLink.h
$$ENDIF //PROJTYPE_DLG
//
这段代码的主要作用是保证about.h、hyprlink.h、statlink.cpp、statlink.h四个文件在三种情况下都被拷贝:
基于对话框的工程类型,程序员不用取消“About Box”复选框。
SDI类型的工程
MDI类型的工程
其次,不要忘了拷贝我们在“关于”对话框中要用到的图像资源文件,定位到newproj.inf的/res行,然后找到这一段的如下代码行
$$IF(!PROJTYPE_DLL)
=:root.ico res\$$root$$.ico
在这行代码后加上:
=:root.ico res\APP.ico
=:VCKBASELOGO.BMP res\VCKBASELOGO.BMP
newproj.inf文件的修改就OK了。
修改AppWizard模板
前面我们已经知道了如何创建新的模板文件并将它添加到新的工程中(在newproj.inf文件中操作)。现在我们来学习如何修改创建Custom AppWizard时由AppWizard建立的一般模板。
——修改模板资源定义文件
由于我们在AppWizard所创建之新工程的“关于”对话框中加入了几个静态控制,那么我们就必须在模板文件中将这些控制的资源ID定义好。以便AppWizard在生成新工程的源文件时也能在目标源文件中定义这些资源ID。这里要涉及两个文件,一个是AppWizard工程Template目录中的Dlgres.h,另一个是同目录中的resource.h。前者用于基于对话框的程序,后者用于SDI和MDI。在Dlgres.h文件的最前面(注释之后)加上如下代码:
//
$$IF(PROJTYPE_DLG)
$$IF(ABOUT)
#define IDC_STATIC_ICON 1000
#define IDC_STATIC_TEXT 1001
#define IDC_STATIC_MAIL 1002
#define IDB_STATIC_IMG 129
$$ENDIF //ABOUT
$$ELIF(PROJTYPE_MDI)
#define IDC_STATIC_ICON 1000
#define IDC_STATIC_TEXT 1001
#define IDC_STATIC_MAIL 1002
#define IDB_STATIC_IMG 129
$$ELIF(PROJTYPE_SDI)
#define IDC_STATIC_ICON 1000
#define IDC_STATIC_TEXT 1001
#define IDC_STATIC_MAIL 1002
#define IDB_STATIC_IMG 129
$$ENDIF //PROJTYPE_DLG
//
找到_APS_NEXT_RESOURCE_VALUE和_APS_NEXT_CONTROL_VALUE,并降下一个值的定义改为:
#define _APS_NEXT_RESOURCE_VALUE 130
#define _APS_NEXT_CONTROL_VALUE 1003
如法炮制resource.h文件,不同的是IDB_STATIC_IMG的值为:
#define IDB_STATIC_IMG 130
而#define _APS_NEXT_RESOURCE_VALUE定义的下一个值是131。
——修改模板资源文件
因为我们创建的定制AppWizard包含了一个新对话框,所以你必须在资源模板文件中插入一个新的对话框模板资源。你在创建定制AppWizard时,你规定了起始点是“Standard
MFC AppWizard steps”。所以在Template目录中,你会看到用这个定制AppWizard创建每种类型的MFC可执行文件所需的全部模板文件。这意味着你可以按自己的要求任意修改这些模板文件。但是我们要确定如何改,以及改什么!这个目录中的模板资源文件(.rc)不止一个,到底应该改哪一个呢?,其实细想一下,很容易确定要改哪一个rc文件。
主要的资源文件名中都包含有三个字母的后缀,它表示语言支持(English=enu,Chinese=chs等等)。此外还有每个资源文件的本地化Macintosh版本。因此,对于一个面向运行Windows的Intel
PC的中文版应用程序来说,资源文件的数目无外乎四个:all.rc、dlgall.rc、dlgloc_chs.rc、loc_chs.rc。
打开all.rc和dlgall.rc文件,你会发现其中的指令和宏都是用于支持语言和平台的基本资源文件。进而可以断定这两个文件包含特定语言和平台所需的资源定义,由串表和对话框这样的资源使用。排除了这两个文件,那么我们必须修改的两个对话框资源文件非dlgloc_chs.rc和loc_chs.rc莫属。每种语言之所以有两个资源文件是因为其中一个文件(loc_chs.rc)用于基于文档/视图的应用程序(SDI和MDI),另一个文件(dlgloc_chs.rc)用于基于对话框的程序。
既然知道了要修改哪一个资源文件,那么就把对话框资源添加进去吧,遗憾的是你不能象往常创建对话框模板资源那样用资源编辑器来做这件事情。这是因为在你试图打开模板资源文件的时候,资源编辑器会去编译文件的资源。而现在这些模板文件含有AppWizard指令,文件编译将不会成功,因此Visual
Studio会强行让你用文本方式(Edit code)打开它。也可以在“File Open Dialog”对话框中,将“Open As”设为“Text”。
按照上面所讲的方法打开loc_chs.rc文件,用下面的代码替换IDD_ABOUT对话框模板资源定义:
$$IF(ABOUT)
IDD_ABOUTBOX DIALOG DISCARDABLE 34, 22, 313, 159
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "关于 111"
FONT 9, "宋体"
BEGIN
ICON IDR_MAINFRAME,IDC_STATIC_ICON,11,10,21,21
LTEXT "$$GENERAL_INFO$$",IDC_STATIC,46,14,237,45
CONTROL 129,IDB_STATIC_IMG,"Static",SS_BITMAP |
WS_BORDER,46,72,110,36
LTEXT "@@VCKBASE 版权所有 (C) $$YEAR$$@@",IDC_STATIC,187,83,106,8
LTEXT "VC 知识库\n$$WEB_PAGE$$",IDC_STATIC_TEXT,187,97,73,
16
DEFPUSHBUTTON "@@确定@@",IDOK,56,117,84,14,WS_GROUP
LTEXT "@@与我们联系@@",IDC_STATIC_MAIL,187,119,58,8
END
$$ENDIF //ABOUT
接下来用相同的方法打开dlgloc_chs.rc文件,如法炮制。注意对话框定义中宏的使用方法。它说明了用户在Custom AppWizard对话框中输入的信息如何最终反映在资源文件中。现在如果你构造AppWizard工程,则它可以创建基于对话框的应用程序,填写完“程序员”、“Web
站点”等信息,并构造新创建的工程就可以欣赏定制的“关于”对话框了。但是如果对话框中没有系统菜单怎么办呢?从哪里访问“关于”对话框呢?为了解决这个问题,我们还要对资源模板文件(dlgloc_chs.rc)进行修改。在它定义的主对话框中加一个“关于”按钮。这样的话,定制的AppWizard创建每一个基于对话框的程序时都会自动在对话框上加一个“关于”按钮。打开dlgloc_chs.rc文件,将主对话框的资源定义替换为以下代码,注意“帮助”按钮的位置取决于是否创建“关于”按钮。
IDD_$$SAFE_ROOT$$_DIALOG DIALOGEX 0, 0, 320, 200
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_APPWINDOW
CAPTION "$$TITLE$$"
FONT @@9@@, "@@宋体@@"
BEGIN
DEFPUSHBUTTON "@@确定@@",IDOK,260,7,50,14
PUSHBUTTON "@@取消@@",IDCANCEL,260,23,50,14
$$IF(ABOUT)
PUSHBUTTON "@@关于(&A)@@",ID_APP_ABOUT,260,45,50,14
$$IF(HELP)
PUSHBUTTON "@@帮助(&H)@@",ID_HELP,260,40,50,14
$$ENDIF
$$ELIF(HELP)
PUSHBUTTON "@@帮助(&H)@@",ID_HELP,260,45,50,14
$$ENDIF
LTEXT "@@TODO: 在这里设置对话控制。@@",IDC_STATIC,50,90,200,8
END
——修改模版头文件和模板实现文件
为了在AppWizard所创建的每一个工程源文件中加上作者自己的专门注释(比如,作者姓名、代码许可声明、创建日期等),我们必须修改AppWizard工程Template目录中所有的.h文件和.cpp文件,在每个文件最上面添加如下代码段:
/////////////////////////////////////////////////////////////////////////////
// Project:$$ROOT$$
// Author:$$PROGRAMMER$$
// Date:$$DATE_INFO$$
// Description:$$COMMENT_INFO$$
//
/////////////////////////////////////////////////////////////////////////////
——修改文档/视图模板文件和对话框模板文件
这一部分我们将修改文档/视图类和对话框类的头文件和实现文件,之所以要改这些文件是因为缺省的视图实现文件和对话框实现文件通常都要声明和实现默认的CAboutDlg类,而我们在前面创建的about.h文件中已经包含了CAboutDlg的声明。
如果你用AppWizard创建一个名叫MyApp的SDI或者MDI程序,主程序类的声明将会在MyApp.h中,实现将会在MyApp.cpp中。这是因为名为$$ROOT$$的宏其值被设置为工程的名字,并且被用来命名包含主程序类定义和声明的文件。newproj.inf中的两行代码证明了这一点:
root.h $$root$$.h
root.cpp $$root$$.cpp
1、 打开root.cpp文件(在Template目录中),按照如下的步骤进行修改:
2、 删除文件尾部的CAboutDlg类声明。
3、 删除所有的CAboutDlg成员函数。添加如下代码:
// App command to run the About dialog
void $$APP_CLASS$$::OnAppAbout()
{
CAboutDlg().DoModal();
}
4、 到文件顶部,在#include "$$root$$.h"语句之后加上,#include "about.h"
现在基于文档/视图的模板文件已经修改完成,下面要修改基于对话框的模板文件。分析一下root.h和root.cpp以及dlgroot.h和dlgroot.cpp模板文件的作用,不难得出root.h和root.cpp模板用于创建所有基于文档/视图程序的主程序类源文件,dlgroot.h和dlgroot.cpp模板用于创建所有基于对话框程序的主程序类源文件。与基于文档/视图的应用程序不同,基于对话框的程序其“关于”对话框的定义与主程序类的定义不在同一个文件当中。而是定义在主对话框的实现文件中,其模板文件是dialog..cpp。打开dialog.h文件(位于Template目录),定位到以下代码处:
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
插入下面的函数声明:
afx_msg void OnAbout();
打开dialog.cpp,按照以下步骤进行修改:
1、 删除$$IF(ABOUT) 到 $$ENDIF之间的所有代码行。
2、 在$$IF(ABOUT) 和 $$ENDIF //ABOUT之间加上语句 #include "about.h":
$$IF(ABOUT)
#include "about.h"
$$ENDIF //ABOUT
3、 定位到MESSAGE_MAP 部分中的ON_WM_SYSCOMMAND()位置,添加下列代码行:
ON_BN_CLICKED(ID_APP_ABOUT, OnAbout)
4、 在文件末尾添加下列函数定义:
$$IF(ABOUT)
void $$DLG_CLASS$$::OnAbout()
{
CAboutDlg().DoModal();
}
$$ENDIF //ABOUT
到此,定制AppWizard要做的主要工作以及要编写的主要代码已经完成,在构造和测试它之前,让我们再做一些锦上添花的工作。每次用AppWizard产生工程的时候,其最后一个对话框是一个确认对话框,其中总结性地显示在前面一系列对话框中做出的选择或者选项。下面我们将学习如何轻松确认输入信息,将我们在AppWizard定制对话框中输入的信息也显示在这个确认对话框里。
修改confirm.inf文件
打开confirm.inf文件,你会马上感觉到这个文件与newproj.inf太相似了,其中充斥着大量的AppWizard指令和宏。我们对它的修改很简单,把自己定义的宏加入这个文件即可,如果你想显示一下在定制对话框中输入的内容,那么在confirm.inf文件的顶部加入下面的代码就可以了:
$$IF(PROJTYPE_DLG)
$$IF(ABOUT)
定制对话框信息:
作者:$$PROGRAMMER$$
网站: $$WEB_PAGE$$
程序说明:$$GENERAL_INFO$$
程序注释:$$COMMETN_INFO$$
$$ENDIF //ABOUT
$$ELIF(PROJTYPE_MDI)
定制对话框信息:
作者:$$PROGRAMMER$$
网站:$$WEB_PAGE$$
程序说明:$$GENERAL_INFO$$
程序注释:$$COMMETN_INFO$$
$$ELIF(PROJTYPE_SDI)
定制对话框信息:
作者:$$PROGRAMMER$$
网站:$$WEB_PAGE$$
程序说明:$$GENERAL_INFO$$
程序注释:$$COMMETN_INFO$$
$$ENDIF //PROJTYPE_DLG
在这个文件中,$$IF/$$ELIF/$$ENDIF的结构和语法完全与newproj.inf一样。$$IF/$$ENDIF指令之间的语句是按原样显示的对话框输入信息。
在注册表中存储宏
通过前面的努力,我们已经创建了一个自己定制的AppWizard,用它创建的每一个MFC应用程序,不论是SDI还是MDI,或是基于对话框,都包含一个特制的“关于”对话框,在这个对话框中可以显示关于作者的信息,程序说明,以及静态超链接。另外这个AppWizard还会在每个源代码文件中加上专门定制的注释说明。但美中不足的是每次创建新工程的时候都要程序员重新输入信息。而这些信息对于每一个新的应用程序都是一样的。为了在可用性方面使我们的程序更加完美,下面将针对这个问题对我们定制的AppWizard进行改进,将工程的公共信息存储在注册表中,当创建新的工程时,AppWizard会首先从注册表中读取这些公共信息,不需要重新输入,除非你确实要改变这些公共信息。
——在CCustomAppWiz派生类中引入注册表的操作
为了存取注册表信息,我们在定制的AppWizard工程中使用了一个封装类CRegistry。这个类封装了针对注册表的常用操作。我们要对CVckbaseWizAppWiz实现进行修改。以便能在它的实现中使用CRegistry类存取注册表。方法如下:
1、 打开VckbaseWizAw.cpp,在“#include "chooser.h"” 包含语句后面加上“#include "registry.h"”
2、 在InitCustomAppWiz成员函数前面加上如下注册表键值定义:
#define VCKBASEWIZ_KEY "Software\\VCKBASE\\VckbaseWiz"
3、 在VCKBASEWIZ_KEY #define指令之后,定义下列静态结构,其中包含:宏名、注册表值名、宏的缺省值。注册表值名被用来在注册表中查找VCKBASEWIZ_KEY指定的键。如果没有找到键值(例如第一次运行程序时),则会使用宏的缺省值:
static struct
{
char szMacroName[50];
char szRegistryValueName[50];
char szMacroDefaultValue[1024];//512个汉字
} macroPairs[] = {
"PROGRAMMER", "Programmer", "程序员",
"WEB_PAGE", "Web Page", "网站",
"GENERAL_INFO", "General Info", "程序描述",
"COMMENT_INFO", "Comment Info", "程序注释"
};
4、 将下列代码添加到CVckbaseWizAppWiz::InitCustomAppWiz()函数末尾,其作用是在堆栈创建CRegistry对象,然后遍历上一步定义的静态结构。对于这个宏结构数组每一个元素,程序都会在注册表中找对应值,如果找到则取注册表中的值,否则取缺省值。不论哪一种情况,宏的值一旦建立,Dictionary字典的值就被更新为当前宏的值:
CRegistry registry(HKEY_LOCAL_MACHINE, VCKBASEWIZ_KEY);
CString strValue;
for (int i = 0; i
5、 将下列代码添加到CVckbaseWizAppWiz::ExitCustomAppWiz()函数末尾,其作用是当卸载定制的AppWizard
DLL时程序会调用这个函数存储数据。
CRegistry registry(HKEY_LOCAL_MACHINE, VCKBASEWIZ_KEY);
CString strValue;
for (int i = 0; i
大功告成,现在构造定制的AppWizard。如果没有出错的话,则编译生成的.awx文件会被自动拷贝到Visual Studio的Template目录。
接下来测试一下我们定制的AppWizard,New一个新工程,在工程类型列表中选择“MFC AppWizard (exe) – VC知识库”,创建一个基于对话框的应用程序,最后一个对话框是我们在Custom
AppWizard工程中定制的对话框,如图五:
图五
输入相应的信息后,单击“Finish”按钮,显示确认对话框,你在定制对话框中输入的信息也应该在此确认对话框中显示。单击“OK”按钮创建工程。然后编译并运行。对话框中可以见到三个按钮:“确定”、“取消”、“关于”。单击“关于”按钮,弹出对话框如图六:
图六
这就是我们定制的“关于”对话框。这个对话框中有带URL链接的静态文字、icon和Bitmap图像。
我真的觉得它很酷!
[全文完]