下載AppWizard工程源代碼:http://www.vckbase.com/vckbase/vckbase11/src/VckbaseWiz.zip
下載用Custom AppWizard創建的工程源代碼:http://www.vckbase.com/vckbase/vckbase11/src/vckproj.zip
第三部分 定制一個高級的AppWizard
下載AppWizard工程源代碼
下載用Custom AppWizard創建的工程源代碼
添加定制的對話框
創建自己的模板文件
修改newproj.inf文件
修改AppWizard模板
修改模板資源定義文件
修改模板資源文件
修改文檔/視圖源文件模板和對話框模板文件
修改confirm.inf文件
在注冊表中存儲宏
在CCustomAppWiz派生類中引入注冊表的操作
我們在第二部分中示范的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:[email protected]");
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文件的最前面(注釋之後)加上如下代碼://
找到_APS_NEXT_RESOURCE_VALUE和_APS_NEXT_CONTROL_VALUE,並降下一個值的定義改為:
$$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
//
#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)
接下來用相同的方法打開dlgloc_chs.rc文件,如法炮制。注意對話框定義中宏的使用方法。它說明了用戶在Custom AppWizard對話框中輸入的信息如何最終反映在資源文件中。現在如果你構造AppWizard工程,則它可以創建基於對話框的應用程序,填寫完“程序員”、“Web 站點”等信息,並構造新創建的工程就可以欣賞定制的“關於”對話框了。但是如果對話框中沒有系統菜單怎麼辦呢?從哪裡訪問“關於”對話框呢?為了解決這個問題,我們還要對資源模板文件(dlgloc_chs.rc)進行修改。在它定義的主對話框中加一個“關於”按鈕。這樣的話,定制的AppWizard創建每一個基於對話框的程序時都會自動在對話框上加一個“關於”按鈕。打開dlgloc_chs.rc文件,將主對話框的資源定義替換為以下代碼,注意“幫助”按鈕的位置取決於是否創建“關於”按鈕。
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 //ABOUTIDD_$$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
4、 到文件頂部,在#include "$$root$$.h"語句之後加上,#include "about.h"
void $$APP_CLASS$$::OnAppAbout()
{
CAboutDlg().DoModal();
}
現在基於文檔/視圖的模板文件已經修改完成,下面要修改基於對話框的模板文件。分析一下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)
3、 定位到MESSAGE_MAP 部分中的ON_WM_SYSCOMMAND()位置,添加下列代碼行:
#include "about.h"
$$ENDIF //ABOUT
ON_BN_CLICKED(ID_APP_ABOUT, OnAbout)
4、 在文件末尾添加下列函數定義:$$IF(ABOUT)
到此,定制AppWizard要做的主要工作以及要編寫的主要代碼已經完成,在構造和測試它之前,讓我們再做一些錦上添花的工作。每次用AppWizard產生工程的時候,其最後一個對話框是一個確認對話框,其中總結性地顯示在前面一系列對話框中做出的選擇或者選項。下面我們將學習如何輕松確認輸入信息,將我們在AppWizard定制對話框中輸入的信息也顯示在這個確認對話框裡。
void $$DLG_CLASS$$::OnAbout()
{
CAboutDlg().DoModal();
}
$$ENDIF //ABOUT
修改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 < sizeof macroPairs / sizeof macroPairs[0]; i++)
{
if (registry.ReadString(macroPairs[i].szRegistryValueName, strValue.GetBuffer(strValue.GetLength())))
{
m_Dictionary.SetAt(macroPairs[i].szMacroName, strValue);
}
else
{
m_Dictionary.SetAt(macroPairs[i].szMacroName, macroPairs[i].szMacroDefaultValue);
if (m_Dictionary.Lookup(macroPairs[i].szMacroName, strValue))
{
registry.WriteString(macroPairs[i].szRegistryValueName, strValue.GetBuffer(strValue.GetLength()));
}
}
}
5、 將下列代碼添加到CVckbaseWizAppWiz::ExitCustomAppWiz()函數末尾,其作用是當卸載定制的AppWizard DLL時程序會調用這個函數存儲數據。CRegistry registry(HKEY_LOCAL_MACHINE, VCKBASEWIZ_KEY);
CString strValue;
for (int i = 0; i < sizeof macroPairs / sizeof macroPairs[0]; i++)
{
if (m_Dictionary.Lookup(macroPairs[i].szMacroName, strValue))
{
registry.WriteString(macroPairs[i].szRegistryValueName, strValue.GetBuffer(strValue.GetLength()));
}
}
大功告成,現在構造定制的AppWizard。如果沒有出錯的話,則編譯生成的.awx文件會被自動拷貝到Visual Studio的Template目錄。 接下來測試一下我們定制的AppWizard,New一個新工程,在工程類型列表中選擇“MFC AppWizard (exe) – VC知識庫”,創建一個基於對話框的應用程序,最後一個對話框是我們在Custom AppWizard工程中定制的對話框,如圖五:
圖五
輸入相應的信息後,單擊“Finish”按鈕,顯示確認對話框,你在定制對話框中輸入的信息也應該在此確認對話框中顯示。單擊“OK”按鈕創建工程。然後編譯並運行。對話框中可以見到三個按鈕:“確定”、“取消”、“關於”。單擊“關於”按鈕,彈出對話框如圖六:
圖六
這就是我們定制的“關於”對話框。這個對話框中有帶URL鏈接的靜態文字、icon和Bitmap圖像。
我真的覺得它很酷!
[全文完]