MFC開發,最為重要的無非就是用“MFC AppWizard” 對話框做開發了,第一次使用感覺像拆收音機的孩子 —— 左邊是項目類型選擇,右邊是一堆打勾的選項,點完 “完成”,屏幕上就冒出了能直接編譯運行的窗口程序。那時還不知道,這個像 “自動包餃子機” 的工具,后來會被我們改成適配團隊需求的 “專屬模具”。
一、Custom AppWizard 基本操作
剛開始用 AppWizard 時,每次新建項目都要重復選 “單文檔”“支持數據庫”“帶狀態欄”,像每次包餃子都要重新調餡料比例。有次團隊趕一個環境監測軟件的項目,一周內要新建三個類似的子模塊,光是配置項目選項就耗了小半天。也就是從那時候起,我開始琢磨怎么把這些重復步驟 “固化” 成新模板,Custom AppWizard 就成了最佳選擇。
打開 VC++5.0,從 “File” 里選 “New”,在 “Projects” 標簽里找到 “Custom AppWizard”,給它起個名字 —— 比如我們當時給團隊做的 “科研數據工具模板”。這里有個小細節,名字最好帶上團隊或項目特征,后來我們建了好幾個模板,“水質監測專用”“大氣數據采集” 這樣的命名能讓大家一眼就找到對應模板。
下一步會讓你選基于哪個原始模板(就像選基礎面團),MFC 提供了單文檔、多文檔、對話框這幾種基礎類型。我們當時做的監測軟件大多是單文檔帶數據表格的,就選了 “Single Document” 作為基礎。接著彈出的配置頁和普通 AppWizard 一模一樣,從 “Database Support” 里勾選 “Database View Without File Support”,到 “User Interface Features” 里把 “Status Bar” 和 “Tool Bar” 都打上勾,每一個選項都對應著后續開發的基礎需求。
配置完成后,VC++ 會生成一個新的模板項目,里面包含了模板的配置信息和生成邏輯。我們需要把這個模板編譯一下,生成的.dll 文件會被自動放到 VC 的模板目錄里。等下次同事新建項目時,在 “Projects” 列表里就能看到我們做的 “科研數據工具模板”,一點擊,之前配置的選項全都是默認勾選狀態,原本要花 5 分鐘的配置過程,現在 10 秒鐘就能搞定。
這一步就像給自動售貨機裝固定貨道 —— 你提前把大家常買的飲料擺好,別人按一下就能拿到,不用再從一堆選項里翻找。而且這個貨道還能隨時調整,后來我們需要給軟件加打印功能,就在模板里把 “Printing and Print Preview” 勾選上,所有基于這個模板新建的項目,自動就有了打印相關的基礎代碼。
二、AppWizard Components 剖析
后來團隊需要在每個程序里加日志模塊,普通模板滿足不了。這時候就得拆開 AppWizard 的 “五臟六腑”,看看它是怎么生成代碼的。就像想在餃子里加新餡料,得先知道餃子機的餡料輸送管道在哪。
AppWizard 的核心是兩個部分:配置對話框和代碼生成腳本。配置對話框就是我們選選項的那些窗口,它由一系列的對話框資源和對應的處理代碼組成。這些對話框就像點餐時勾選 “加辣”“少冰” 的菜單,每個勾選框背后都對應著一個變量,當我們點擊 “下一步” 時,這些變量的值就會被傳遞給代碼生成腳本。
代碼生成腳本則是根據勾選結果 “炒菜” 的菜譜,它用一種叫 “.awx” 的格式存儲。.awx 文件其實是一種特殊的資源文件,里面包含了代碼模板、條件判斷語句和資源模板。我當時用資源編輯器打開.awx 文件,看到里面像搭積木一樣排列著各種代碼片段 —— 有窗口類的定義、消息映射的實現,還有菜單和工具欄的資源描述。
比如它判斷你選了 “狀態欄”,就會在 MainFrame 類里加m_wndStatusBar的定義,同時在OnCreate函數里加入狀態欄的創建代碼:
if (!m_wndStatusBar.Create(this) ||!m_wndStatusBar.SetIndicators(indicators,sizeof(indicators)/sizeof(UINT))){TRACE0("Failed to create status bar\n");return -1; // fail to create}
而如果選了 “數據庫支持”,它就會引入 ODBC 相關的頭文件,在文檔類里加入CDatabase和CRecordset的對象定義。
這些組件就像裝修時的 “水電預裝模塊”—— 你選了 “裝空調”,工人就會預留管線;選了 “智能家居”,就會提前布好網線。理解了這些,就知道該改哪里能加入我們自己的模塊。比如我們想加日志模塊,就得找到代碼生成腳本里初始化程序的部分,在那里加入日志初始化的代碼;同時在配置對話框里加一個勾選框,讓用戶可以選擇是否啟用日志功能。
當時為了搞清楚這些組件的工作流程,我特意找了個簡單的模板,每勾選一個選項就去看生成的代碼有什么變化,像偵探破案一樣一點點梳理出選項和代碼之間的對應關系。這個過程雖然花了兩天時間,但搞明白之后,后面修改模板就得心應手了。
三、動手修改 AppWizard
早期在開發項目時有這樣的需求,我們需要每個程序都帶 “數據加密” 功能。因為監測數據涉及到環境敏感信息,必須在程序啟動時初始化加密模塊,防止數據泄露。如果讓每個開發人員手動加這段代碼,不僅麻煩還容易出錯,所以我決定修改之前的 Custom AppWizard 模板,把加密初始化的代碼集成進去。
首先找到模板的.awx 文件(通常在 VC 安裝目錄的 Template 文件夾里,路徑大概是 “Microsoft Visual Studio\VC98\Template”),用 VC 打開這個模板項目。在工作區里能看到 “Resource Files” 下面有個 “Custom.awx” 文件,這就是我們要修改的代碼生成腳本。
雙擊打開.awx 文件,里面是按不同代碼模塊分類的腳本。我要找的是應用程序初始化的部分,對應著 CWinApp 派生類的 InitInstance 函數。在腳本里找到相關的代碼塊后,加入了一段判斷和加密初始化代碼(簡化版):
// 在生成CWinApp派生類時添加加密初始化if (m_bNeedEncrypt) // 我們新增的配置項{AddCode("BOOL CMyApp::InitInstance()");AddCode("{");AddCode(" // 初始化加密模塊");AddCode(" EncryptInit();"); // 我們的加密函數AddCode(" // 原有初始化代碼");AddCode(" CWinApp::InitInstance();");AddCode("}");}
這段腳本的意思是,如果用戶勾選了 “啟用數據加密”,生成的程序就會在初始化時調用 EncryptInit 函數。
光有腳本還不夠,得讓用戶能選擇是否啟用這個功能。所以我又在配置對話框里加了個 “啟用數據加密” 的復選框。在模板項目里找到對話框資源(通常是 IDD_STEP1 或者類似的 ID),用對話框編輯器拖了一個復選框進去,設置 ID 為 IDC_ENCRYPT,然后在對應的對話框類里添加了一個成員變量 m_bNeedEncrypt,用來存儲用戶的選擇。這樣當用戶在配置時勾選這個復選框,m_bNeedEncrypt 就會被設為 TRUE,上面的腳本就會生效。
改完這些后,我編譯了模板并測試了一下。新建項目時,配置頁面果然出現了 “啟用數據加密” 的復選框,勾選后生成的代碼里也有了 EncryptInit 的調用。但運行程序時卻崩潰了,調試后發現是沒在代碼里引入加密模塊的頭文件 “Encrypt.h”。就像包餃子時忘了放酵母,面發不起來,代碼缺少必要的頭文件,自然無法正常運行。于是我又回到腳本里,在添加加密初始化代碼的地方,加了一行#include "Encrypt.h",問題才得以解決。
這種 “踩坑” 的經歷反而讓我明白:工具再智能,也需要人懂它的 “脾氣”。每個看似簡單的功能背后,都有一連串的細節需要考慮,而這些細節正是從 “會用工具” 到 “駕馭工具” 的關鍵。
最后小結:
現在回頭看,Custom AppWizard 就像 MFC 時代的 “技術裁縫”。它不只是省時間的工具,更藏著早期程序員對 “復用” 的理解 —— 我們不重復寫代碼,也不重復做配置,把精力留給真正需要創新的地方。
后來到了 Web 時代,腳手架工具代替了 AppWizard,但道理沒變:好的工具永遠是 “可定制的”。就像當年我們在機房里改模板時想的:技術會變,但 “讓工具適應人” 的思路,大概是所有程序員的共同默契。而那些在修改模板時踩過的坑、總結的經驗,也成了后來面對新工具時的底氣 —— 無論工具怎么變,拆解它、理解它、改造它的邏輯,從來都沒有變過。未完待續.........