第一部分 AppWizard及其工作原理 |
AppWizard即應用程序向導,它是Visual Studio開發環境中強大的編程工具之一,用它可以創建各種不同類型的程序。比如Win32應用、ATL、MFC應用等等。在Windows的術語中,向導(wizard)一詞指得是一個應用程序,它的一個主要特點就是提供一系列對話框引導用戶進行必要的選擇來完成給定的任務。VC中的應用程序向導——AppWizard提供一系列特定工程類型對話框來讓程序員定義各種類型的新工程。其中每一個對話框都顯示一些用來指定工程類型的選項。例如,用AppWizard創建Windows DLL的時候,第一個對話框讓程序員指定諸如要創建什么類型的DLL,是常規類型還是MFC擴展類型,是否要包括自動化支持,以及要不要源代碼注釋等等。 根據程序員所填充的對話框,AppWizard會自動創建構造工程所需的框架文件,它們包括:工程文件、工作間文件、源代碼文件、頭文件、資源文件等等。AppWizard是Visual Studio開發環境中使用最多的工具之一。盡管如此,AppWizard也有它的不足之處。那就是常用的工程類型都是內建在Visual Studio中,無法創建自己的AppWizard。自從有了Custom AppWizard(Visual C++ 4.0)以后,這個問題得到了解決。Custom AppWizard也就是定制的AppWizard。在創建類似的多個工程時,Custom AppWizards顯得特別有用。例如你創建的工程都是SDI,并且都支持自動化(automation),那么你就可以創建一個自己定制的 AppWizard,將SDI自動化設為默認選項。這樣可以提高工作效率。此外,利用Custom AppWizard也可以創建具有個性化的工程。例如你想要所有工程都有一個“關于”對話框,并且在這個對話框中顯示個人信息或者公司的標徽及其它專有信息,每個源代碼文件中都加上自己的專門注釋。那么通過創建一個Custom AppWizard很容易實現這個需求。你甚至可以定義并顯示自己定制的對話框來收集工程類型所需的信息和選項。本文的第一部分我們將討論AppWizard的工作原理,然后在后續部分中循序漸進地學習如何創建Custom AppWizard。最終我們將創建一個在實際編程中非常實用的Custom AppWizard。并提供全部源代碼。 在學會使用Custom AppWizard之前,首先必須了解AppWizard的工作原理,理解 AppWizard是如何根據不同的用戶選擇來創建工程的。 AppWizard有一個管理裝置(manager),它不是一個單獨的應用程序。Custom AppWizard運行于Visual Studio框架之中。AppWizard的這個所謂的“管理器”,實際上就是MFCAPWZ.DLL,它控制不同的AppWizard執行。在創建新工程的對話框中,“Project”標簽是默認的選項,列表框中顯示出內建的工程類型。此外,這個列表框中還列出用戶定制的AppWizard,如圖一所示。 |
|
圖一 |
這些定制AppWizard文件擴展名為*.awx,它們存放在一個特定的目錄中。如果安裝VC6.0時是按照默認的路徑安裝的,則定制的AppWizard文件在成功編譯后都會被存放到\Program Files\Microsoft Visual Studio\Common\MSDev98\Template文件夾中。注意列表框中此新的列表項“MFC AppWizard (exe) – VC知識庫”,這就是我們后面要定制的AppWizard。從這里可以看出,只要產生了*.awx文件,那么它就會與標準的(或者說內建的)Visual C++ AppWizard一起自動顯示在這個列表框中。 |
——CCustomAppWiz 類和Dictionary字典 |
CCustomAppWiz 基類提供了MFCAPWZ.DLL 和Custom AppWizard之間的通訊服務。CCustomAppWiz()成員函數的實現就在MFCAPWZ.DLL中。為了實現特定應用的行為,你只要從CCustomAppWiz派生一個類,改寫相應的虛擬函數,然后在MFCAPWZ.DLL運行時調用SetCustomAppWizClass()函數注冊派生類即可。 通常,AppWizard顯示一系列對話框獲取創建新工程所需的設置。每一個步進對話框顯示不同的選項。AppWizard將這些選項的值存儲在一個串映射中。這個串映射就叫做Dictionary字典。Dictionary字典實際上是一個CCustomAppWiz 類的成員變量(m_Dictionary),其類型為CMapStringToString。Dictionary將AppWizard宏映射到相關聯的值。這里所說的宏是指工程選項或設置的名稱。例如,在創建MFC的時候,你可以選擇應用程序為SDI,那么,Dictionary中就會有一個名為PROJTYPE_SDI的宏。Dictionary中這個項目的值就是1,否則這個與這個宏關聯的值為0。 m_Dictionary成員變量可以被用于創建宏,刪除宏或者更新宏的值。因為m_Dictionary是一個CMapStringToString對象,肯定有相應的成員函數存取不同宏的值。下面的代碼返回PROJTYPE_SDI宏的值,它被用于判斷這個工程是不是一個SDI應用。 m_Dictionary.Lookup("PROJTYPE_SDI", m_strProjType); if (_T("1") == m_strProjType) { // SDI類型應用 } else // 其它類型的程序 { } MFCAPWZ.DLL提供了一些標準宏,任何其它定制AppWizard所需要的宏都可以用SetAt函數添加到Dictionary字典中。你從在線文檔中可以找到六十多個標準宏 當你創建Custom AppWizard并按下Finish按鈕后,MFCAPWZ.DLL用Dictionary創建新的工程文件。每一個AppWizard(不論是標準的還是定制的)都有一套模板文件用于創建AppWizard生成的工程源文件。Dictionary中的值被用于與模板文件相連接來創建最終的輸出(新的工程文件)。下面是一個例子,中文的基于對話框程序的模板資源文件名叫DlgLoc_chs.rc。下面是從中摘錄出的一段: ... IDD_ABOUTBOX DIALOGEX 0, 0, 160, 129 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "關于$$ROOT$$" ... 注意這里 $$ROOT$$ 串的用法。當MFCAPWZ.DLL創建新工程文件時,它首先掃描每一個模板,查找以$$開始并以$$結尾的串。在這兩個前綴和后綴之間的文本串叫做占位苻。每一個占位符是Dictionary中一個宏的名字。MFCAPWZ.DLL在Dictionary中查詢占位符的值并用這個值替換占位符。當所有的占位符都被Dictionary中相應的值替換之后,工程文件也就產生了。 ——用MFCAPWZ.DLL替代占為符 為了理解MFCAPWZ.DLL用Dictionary中宏的值替換模板文件的占位符。我們來做一個實驗: 1、 用AppWizard創建一個基于對話框的應用程序,將工程取名為MyTestDlgApp。 2、 創建完工程之后,以文本方式打開MyTestDlgApp.rc文件。 3、 找到IDD_ABOUT的對話框模板資源。 4、 你應該看到原來模板文件中$$ROOT$$占位符已經被工程的名字(MyTestDlgApp)替換掉了。這是因為Dictionary有一個ROOT宏,其缺省值被設置為工程的名字。 宏即可被用于定義模板文件中指定的占位符的替換值,有時AppWizard也用宏來協助步進對話框的顯示,或者確定用哪個模板來創建新的工程文件。例如,如果Dictionary中PROJTYPE_SDI宏的值為1,則創建的應用程序是SDI類型。但是,如果PROJTYPE_DLG宏的值為1的話,創建的應用程序是基于對話框的。根據宏的值是否為1,MFCAPWZ.DLL使用不同的模板文件來創建工程文件。 大多數AppWizard都由一系列固定的對話框組成。其中后一個對話框的顯示完全依賴于前一個對話框所選擇的選項來決定。這種多步進對話框形式稱為軌跡。創建MFC應用程序的AppWizard是一個多軌跡的AppWizard。 ——多軌跡AppWizard 多軌跡AppWizard提供了更為復雜的應用程序設置。為了理解多軌跡AppWizard概念,請做一個如下實驗: 1、 按下Ctrl+N創建新工程 2、 單擊“Project”標簽,然后選中“MFC AppWizard (exe)”。 3、 注意對話框的標題條內容為“MFC AppWizard - Step 1”,沒有指明總共有幾步,這是因為總共的步進數在你決定要創建的MFC工程類型前時未知的。 4、 看一下不同類型工程的選項有何差別:single document (SDI),multiple document (MDI),和 dialog-based。選擇multiple document (MDI)類型,然后單擊Next按鈕。 5、 注意標題條的內容中指定了總共的步進數——“MFC AppWizard - Step 2 of 6”。由于你選擇了MDI類型,這個類型總共有六個步進對話框,每一個步進對話框包含特定的基于文檔的MFC應用程序選項。 6、 單擊Back按鈕,選擇基于對話框的工程類型,然后單擊Next按鈕。這一次標題條的內容指定的步進總數是——“MFC AppWizard - Step 2 of 4”。這說明創建基于對話框的應用程序共有四個步進對話框。 以上是對AppWizards 及其工作原理的討論。在下一部分我們將嘗試創建一個簡單的Custom AppWizard。 |
第二部分 創建一個簡單的AppWizard |
在第一部分中我們介紹了Custom AppWizard的概念及其工作原理。在這一部分,我們將嘗試用Custom AppWizards來創建一個最簡單的Custom AppWizard。 首先,我們先創建一個什么事情也不做的Custom AppWizard,主要是了解它的創建過程和步驟。按Ctrl+N 打開New對話框,新建一個Custom AppWizards工程。填入工程名字后單擊OK,從步進對話框的標題中,我們可以了解到總共有兩個步驟,在第一個步的對話框中包含三個輸入域。 第一個輸入域是新Custom AppWizards的起點。它有三個單選按鈕: |
l Existing project——這個選項是以一個現存的工程作為藍本來創建Custom AppWizards。使用這個選項有兩個缺點。第一,AppWizards創建的是一個已經存在的工程。第二,AppWizards創建的工程文件名和類名必須與現存工程的文件名和類名一致。 l Standard MFC AppWizard steps——這個選項是最常用的選項,它創建的AppWizard模板可用于每一種MFC支持的工程類型。從修改各種MFC工程模板文件的靈活性方面,這個選項也是最靈活的。因為這是最通用的一種定制AppWizard類型,所以我們將以它為例。 l Your own customized steps——這個選項全新定制一組步進對話框和選項。例如,假設你需要一個定制的AppWizard來自動創建一個非MFC應用程序。這時你就得用這個選項創建所有自己定制的對話框。 第二個輸入域讓你命名新建的定制AppWizard。這個名字將被用于顯示在New Project List對話框中. 最后一個輸入域用來指定定制步進的數目或者對話框的數目,它將被添加到新的定制AppWizard中。有時候我們不需要額外的步進對話框,比如我們即將創建的簡單AppWizard就不用任何步進。但在第三部分中,我們將會學習如何定制步進對話框。 接下來,按Next按鈕繼續到定制AppWizard的第二步(對話框),也是最后一步。這個對話框中有兩個域都是自解釋的。第一個域定義新定制的AppWizard是個可執行程序還是一個DLL。第二個域指定語言支持選項。 |
設置工程的缺省選項 |
前面我們講過用AppWizard創建工程時可以有多種類型可以選擇。這一部分我們創建的AppWizard名字叫SDIAutomationWiz,在默認情況下,用這個AppWizard創建的工程類型是支持自動化的SDI工程。 打開工程的New 對話框,在Project List中選擇Custom AppWizard,在Project Name編輯框中輸入SDIAutomationWiz,單擊OK進入第一個步進對話框,選擇“Standard MFC AppWizard steps”,然后指定一個它在Project List中顯示的名字。因為這個AppWizard沒有額外的步進對話框,因此步進數編輯框中填寫0,單擊Next按鈕到下一步。選擇“MFC AppWizard Executable”,語言支持為中文,單擊Finish按鈕,出現確認對話框。單擊OK后便開始創建新的AppWizard工程。 ——定義CCustomAppWiz 類 雖然編譯后的Custom AppWizard文件擴展名都是.awx,但是它實際上就是一個通常我們使用的Windows動態鏈接庫(DLL)文件。如果你打開SDIAutomationWiz.cpp文件,你就會看到如下的DLLMain()函數代碼: // Defining the DLLMain() Function extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) { if (dwReason == DLL_PROCESS_ATTACH) { TRACE0("SDIAUTOMATIONWIZ.AWX Initializing!\n");
// Extension DLL one-time initialization AfxInitExtensionModule(SDIAutomationWizDLL, hInstance);
// Insert this DLL into the resource chain new CDynLinkLibrary(SDIAutomationWizDLL);
// Register this Custom AppWizard with MFCAPWZ.DLL SetCustomAppWizClass(&SDIAutomationWizaw); } else if (dwReason == DLL_PROCESS_DETACH) { TRACE0("SDIAUTOMATIONWIZ.AWX Terminating!\n");
// Terminate the library before destructors are called AfxTermExtensionModule(SDIAutomationWizDLL); } return 1; // ok } 除了常規的CDynLinkLibrary MFC擴展動態鏈接庫例程之外,還有一個對SetCustomAppWizClass()函數的調用。這個函數是從MFCAPWZ.DLL輸出的,用于傳遞定制AppWizard 中CCustomAppWiz派生類的指針。因為MFCAPWZ.DLL通過調用CCustomAppWiz的成員函數來控制所有AppWizard的執行,因此它必須用這個指針來調用CCustomAppWiz派生類中重載的成員函數。 有一點必須牢記在心,那就是盡管你創建了定制的AppWizard,但MFCAPWZ.DLL仍然控制著一切。換句話說,你定制的AppWizard只是用于顯示步進對話框,確定對話框以什么順序顯示,以及設置新工程的模人選項。一些重要的工作,諸如解析模板文件,合并Dictionary中的宏和模板文件中的占為符,創建工程文件等等還是要MFCAPWZ.DLL來做。 前面我們講過,CCustomAppWiz類負責AppWizard與MFCAPWZ.DLL之間的通訊。實際上這種通訊是單邊的。MFCAPWZ.DLL告訴你的CCustomAppWiz對象什么時候需要調用相應的虛擬成員函數。 CCustomAppWiz類中大約有十來個函數,其中只有五個函數是可以看到并使用的常用例程。通過這些函數的命名你基本上就能了解其主要作用。例如,InitCustomAppWiz()函數是進行初始化,包括初始化宏。除此之外,ExitCustomAppWiz()函數的作用是卸載AppWizard。 另外,還有兩個函數用來控制步進對話框的顯示順序。Next()和Back()。不說肯定你也知道,這兩個函數與AppWizard對話框中的Next和Back按鈕是關聯的。 最后一個很重要的函數是CustomizeProject(),一旦程序員完成工程選項的設置,AppWizard便創建工程的make文件并定義工程缺省的debug和release配置。然后AppWizard調用CustomizeProject()函數,以便定制的AppWizard能在存儲工程之前修改make文件設置。 ——宏指令的處理 我們已經知道了宏的初始化是在InitCustomAppWiz()中進行的,我們也知道了CCustomAppWiz類有一個成員變量m_Dictionary,它存儲宏名及其值。因為這個成員變量是CMapStringToString類型的,用標準的MFC映射函數就能get或者set不同的宏,請看下列代碼: // retrieve value for Automation CString strValue; m_Dictionary.Lookup(_T("AUTOMATION"), strValue);
// Include support for context sensitive help m_Dictionary.SetAt(_T("HELP"), strValue.Compare("1")); 現在打開SDIAutomationWizAW.cpp文件,在InitCustomAppWiz()函數末尾敲入: m_Dictionary.SetAt(_T("PROJTYPE_SDI"), _T("1")); m_Dictionary.SetAt(_T("PROJTYPE_MDI"), _T("0")); m_Dictionary.SetAt(_T("AUTOMATION"), _T("1")); 然后構造(build)定制的AppWizard工程。如果沒有出錯的話,AppWizard的.awx文件會被自動拷貝到專門的目錄中,以便MFCAPWZ.DLL能找到它。 現在按下Ctrl+N,新定制的AppWizard應該出現在New對話框的Project清單中。如果你使用新的AppWizard,你會看到工程的默認選項是SDI程序并支持自動化。通過這個簡單的Demo,我們基本上了解了如何通過定制AppWizard來設置默認的工程選項。 如果要分發你創建的AppWizard,只要分發.awx文件就可以了,把它拷到Visual Studio的模板文件目錄即可。 在這一部分,我們定制了一個簡單的AppWizard,通過一個例子示范了如何處理宏字典。在第三部分中,我們將涉及更多定制AppWizard的內容,并且還要制作一個實用價值很高的AppWizard。包括新增加一個步進對話框,獲得新步進對話框中的輸入信息。用這個定制的AppWizard創建的所有工程都會在其“關于”對話框中顯示在步進對話框輸入的信息,并通過靜態控制和圖像建立URL鏈接。此外,用這個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”進入下一個對話框。如圖三:

1、 打開VckbaseWizAw.cpp,在“#include "chooser.h"” 包含語句后面加上“#include "registry.h"” 2、 在InitCustomAppWiz成員函數前面加上如下注冊表鍵值定義: 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())); } } ![]() 圖五 輸入相應的信息后,單擊“Finish”按鈕,顯示確認對話框,你在定制對話框中輸入的信息也應該在此確認對話框中顯示。單擊“OK”按鈕創建工程。然后編譯并運行。對話框中可以見到三個按鈕:“確定”、“取消”、“關于”。單擊“關于”按鈕,彈出對話框如圖六: ![]() 圖六 這就是我們定制的“關于”對話框。這個對話框中有帶URL鏈接的靜態文字、icon和Bitmap圖像。 我真的覺得它很酷! [全文完] |
![]() |
![]() ![]() ![]() |
![]() ![]() #import "devbld.pkg" #include <bldauto.h> #include <blddefs.h> #include <bldguid.h> 在cpp文件中修改CustomizeProject函數 CustomizeProject(IBuildProject* pProject) { using namespace DSProjectSystem; long lNumConfigs; IConfigurationsPtr pConfigs; IBuildProjectPtr pProj; CString sTemp; // Needed to convert IBuildProject to the DSProjectSystem namespace pProj.Attach((DSProjectSystem::IBuildProject*)pProject, true); pProj->get_Configurations(&pConfigs); pConfigs->get_Count(&lNumConfigs); //Get each individual configuration for (long j = 1 ; j < lNumConfigs+1 ; j++) { _bstr_t varTool; _bstr_t varSwitch; IConfigurationPtr pConfig; _variant_t varj = j; pConfig = pConfigs->Item(varj); varTool = "link.exe";//修改鏈接選項 varSwitch = "yourlib.lib"; pConfig->AddToolSettings(varTool, varSwitch, varj); } } ( liuke716 發表于 2003-12-29 14:32:00) |
轉載自:http://www.vckbase.com/document/viewdoc/?id=276
畢業以后想從事計算機的大學生必須去一個網站:http://www.RuPeng.com