InstallShield 使用說明



前言
2001-01-18·adding·yesky

 

DOS時代,人們不會忘記,想要製作一個應用程序的安裝往往是通過批處理文件來實現的,這種風格的安裝程序常常令人們樂此不疲。直到UCDOS圖形安裝界面的出現,才讓人感到一些新意,併爲之一振。然而不久,具有易學易用的圖形用戶界面、多任務功能的Windows系統出現了,並大有取代DOS的趨勢。直到Windows95的問世,才使得DOS真正變爲過去,成爲PC機上最流行的操作環境,並且隨着Windows98InternetExplorer集成的新特性的引入,越來越多的程序員已開始致力於Windows應用程序的研究與開發。
  同樣,安裝程序的運行環境也由原來的DOS變成了現在的Windows系統。安裝一個軟件或應用程序已不再僅僅是將相關的文件複製到硬盤中,而是必須允許用戶按自己的願望選擇安裝類型和安裝路徑,並且在不需要的時候,還要能夠將安裝的相關內容全部刪除掉。這種Windows下的安裝程序給人們留下了很深的印象,人們在驚歎Office2000強大的安裝功能和悅目的安裝界面之後,已無法維持對DOS安裝程序的留戀。
  InstallShield恰恰是在這種操作系統不斷髮展的潮流中應運而生的,從InstallShield3.05.5,從普通的安裝程序製作到最新的Windows安裝界面,InstallShield公司已開發出基本各種操作平臺和軟件開發環境的InstallShield產品。InstallShieldfor Microsoft Visual C++6(簡稱InstallShield VC)InstallShield Express CustomEdition for C++ BuilderDelphi(簡稱InstallShield Express C++ BuilderDelphi)就是其中用戶較爲熟悉的產品。
  由於InstallShield功能強大、靈活性好、完全可擴展以及具有強有力的網絡支持,在各種安裝程序開發工具中脫穎而出,成爲目前最爲流行的製作安裝程序的工具軟件。而且,它所內建的腳本語言InstallScript使得用戶可以像其他高級語言那樣靈活地構造出自己的安裝腳本程序來。正是因爲這一點,InstallShield已經成爲目前製作安裝程序的工業標準。用戶所熟悉的VisualStudio 98BorlandC++ Builder(Delphi)以及目前絕大多數的軟件安裝程序均是用InstallShield來製作的。

由於InstallShield5.5還支持VisualBasic 6.0,故本教程以InstallShield5.5專業版爲主,並從應用出發,深入詳實地討論製作一般應用類和數據庫類安裝程序的方法、技巧以及InstallScript語言基礎,且在InstallShield用戶界面函數的基礎上,挖掘其運用能力,最大地發揮InstallShield的定製和擴展潛能,構造出頗具創意、功能完善的安裝界面來。
  本教程主要講述InstallShield開發環境、InstallScript腳本語言基礎、基本安裝程序的建立、安裝界面的設計以及深入安裝程序製作等共五章內容。各章內容並不停留在初學者的水平上,而且在製作安裝程序的每個方面,都給出了更高、更深層次的方法,例如安裝對話框的定製、使用自己的DLL庫、操作註冊表、安裝界面的漢化、使用多媒體、反安裝以及安裝程序的調試等。
  爲了幫助讀者充分掌握InstallShield特性,本書給出大量有用的實例,這些實例均以[Ex_xxxx]命名,並在Windows98InstallShield5.5專業版本調試通過。
  由於在軟件開發環境和操作系統未來發展的幾年裏,Windows98/2000以及VC++C++ BuilderVB等還仍將是主流。在這種契機下,適時地將InstallShield5.5的使用方法和技巧奉獻給大家,必將對廣大的程序員、軟件開發者和愛好者有所幫助。
  由於時間倉促,加之作者水平有限,不當之處在所難免,懇請讀者批評指正。
  感謝天極網及趙家雄、方舟先生,沒有他們此教程不可能在網上出版。感謝一直關心和支持此項工作的家人和朋友們,尤其是我的妻子。

 

 

集成開發環境概述
2001-01-18· adding ·yesky

Windows95/98/NT操作系統中正確安裝了InstallShield5.5專業版後,就可以單擊任務欄的"開始",選擇"程序"中的"InstallShield5.5 Professional Edition",再選擇該程序文件夾下的"InstallShield 5.5 ProfessionalEdition"就能運行InstallShield。第一次運行時,彈出如圖1.1所示的界面。

但當利用ProjectWizard(項目嚮導)或其他工具創建一個安裝項目後,就會出現InstallShield5.5的完整界面,如圖1.2所示。

它是由標題欄、菜單欄、工具欄、項目工作區窗口、文檔窗口、輸出窗口以及狀態欄等組成的。
  標題欄是用來顯示出當前窗口中的文件名,而且一般還有[最小化][最大化][還原]以及[關閉]按鈕,單擊[關閉]按鈕將退出集成開發環境。
  菜單欄包含了集成開發環境中幾乎所有的命令,它爲用戶提供了文檔操作、安裝腳本程序的編譯、調試、窗口操作等一系列的功能。由於工具欄比菜單操作更爲便捷,故常常將一些常用菜單命令也同時安排在工具欄上。
  項目工作區窗口包含用戶安裝項目的一些信息,包括文件組、組件、腳本文件、資源等。在項目工作區窗口中的目錄項或圖標處單擊鼠標右鍵,有時還會彈出相應的快捷菜單,它包含當前狀態下的一些常用操作。
  文檔窗口位於集成開發環境中的右邊,腳本文件、資源文件以及安裝程序所需要的各種資源等都可以通過該窗口顯示出來。
  輸出窗口出現在集成開發環境窗口的底部,它包含了BuildCompile兩個頁面,分別用來顯示建立和編譯過程中的相關信息。
  狀態欄位於集成開發環境的最底部,它用來顯示當前操作狀態、說明、文本光標所在的行列號等信息。

 

菜單欄和工具
2001-01-17· adding·yesky

1.2.1菜單欄
  在集成開發環境界面中,用戶可以看到在它的上方排列着一系列的菜單,而每一個菜單下都有各自的菜單命令。在進一步與集成開發環境打交道之前,先了解各項菜單命令的基本功能是很有必要的,因爲大部分的操作都是通過菜單來完成的。
  InstallShield 5.5的菜單欄中包含了File(文件)Edit(編輯)View(查看)Insert(插入)Project(項目)Build(編譯)Tools(工具)Windows(窗口)以及Help(幫助)等菜單。其中FileEditViewWindowsHelp均與一般Windows應用程序的菜單用法相同。這裏僅對InsertProjectBuildTools菜單作簡單說明。
  Insert菜單
  Insert菜單中的命令主要用於項目及資源的創建和添加,它有三個菜單項:File into Script Files...File into Setup Files...Links into File Groups...,分別表示將某個文件插入腳本文件和安裝文件中以及鏈接到文件組中。
  一般情況,這三個菜單項是被禁用的。當切換到項目工作區窗口的"Scripts"頁面並選定其中的腳本文件目錄項時,菜單項"File into Scripts Files"被激活;當切換到項目工作區窗口的"Setup Files"頁面並選定其中的目錄項時,菜單項"Files into Setup Files"被激活;而當切換到項目工作區窗口的"File Groups"頁面並選中其中的"Links"目錄項時,菜單項"Links into File Groups"被激活。
  Project菜單
  Project菜單中的命令主要用於項目的創建和相關內容的設置,它有三個菜單項:Project Wizard…、Visual Basic Project Wizard…和Setting…,分別用來創建一般安裝項目、VB應用程序安裝項目以及項目相關內容的設置。
  Build菜單
  Build菜單中的命令主要用來進行安裝程序的編譯、連接、調試、運行等操作,它包括這樣的菜單命令:Compile(編譯)Run Setup(運行)Debug Setup(調試)Media(媒介)Settings...( 設置調試和編譯時的參數)

Tools菜單
  Tools菜單中的命令主要是一些用於運行或定製開發環境中的實用工具,如圖1.3所示。

值得一提的是,隨着集成開發環境當前狀態的改變,有些菜單中的菜單命令項還會隨之變化。例如,當文檔窗口沒有任何腳本程序時,許多菜單項都是灰色的,用戶不能使用它們。此外,InstallShiled 5.5與其他Windows應用程序一樣,其菜單系統一般都遵循下列一些相同的規則:
  (1) 打開InstallShiled 5.5"File"菜單,會看到"Open"菜單項文本後有"",若選擇該菜單命令,則彈出通用文件"打開"對話框。因此,菜單文本後有""就表示其執行結果是將彈出相應的對話框。
  (2) "File"菜單文本中,其中"F"字母帶下劃線。它表示該菜單項的助記符是"F",當按住"Alt"鍵不放,再敲擊該字母時,對應的菜單項就會被選中。
  (3) "Open"菜單文本後有"Ctrl+O"字樣,任何時候,先按下"Ctrl"健不放,然後再按"O"鍵就執行"Open"菜單項命令,彈出通用文件"打開"對話框。這表明"Ctrl+O"和該菜單項命令是一致的,"Ctrl+O"稱爲該菜單項的快捷鍵。

1.2.2 工具欄
  儘管菜單命令可以完成各種操作,其相應的快捷鍵也可提高操作的效率。但是,菜單命令的操作相對繁瑣,快捷鍵又需要用戶加以記憶,所以,有時候用起來還略嫌不便。而工具欄是一種圖形化的操作界面,具有直觀和快捷的特點,熟練掌握工具欄的使用後,工作效率將大有提高。
  工具欄是一系列工具按鈕的組合。當鼠標指針停留在工具欄按鈕的上面時,按鈕凸起,主窗口底端的狀態欄上顯示出該按鈕的一些提示信息,並且如果指針停留時間長一些,就會出現一個小的彈出式的"工具提示"窗口,顯示出按鈕的名稱。工具欄上的按鈕通常和一些菜單命令相對應,提供了一種執行經常使用的命令的快捷方法。
  同菜單中的菜單命令項相似,當菜單命令項禁止使用時,相應的工具按鈕也是灰色的,用戶不能使用它們。
  InstallShield 5.5的工具欄如圖1.4所示。

1.3.1 Scripts頁面
  項目工作區窗口的Scripts頁面用來管理安裝程序的腳本文件,在該頁面中各項腳本文件均以樹狀列表的型式顯示出來的。每個目錄項前都有一個圖標,且頂層目錄項前還有一個套在方框中的符號"+"。雙擊頂層目錄項或單擊最前面的"+",則直接打開並顯示該目錄項的所有子項,此時頂層目錄項前的"+"變成"-"號;再雙擊頂層目錄項或單擊最前面的"-",則該項目的所有子項被收縮,只顯示頂層目錄項內容,此時頂層項目前的"-"又變成了原來的"+"號。
  展開頂層目錄項的所有子項,雙擊以.rul爲擴展名的腳本文件項,則在開發環境的右邊的文檔窗口中顯示出該腳本文件的內容。爲了增強安裝程序代碼的可讀性,腳本文件的源代碼內容往往是以不同顏色來顯示的,各種顏色所代表的含義如下所示:
  白底黑字 一般文本
  黑底白字 被選定的文本
  青色底黑色字 文本的行標記
  白底紅字 InstallShield的函數
  白底藍字 InstallScript腳本語言的關鍵字
  白色底品紅色字 常數
  白色底紫紅色字 ""符號括起來的文本內容
  白底綠字 註釋

1.3.2Components頁面
  項目工作區窗口的Components頁面用來管理安裝程序的各項組件。缺省時,一個安裝項目通常有程序文件(Program Files)、示例文件(Example Files)、幫助文件(Help Files)以及共享的DLL文件(Shared DLLs)共四個組件。雙擊某個組件目錄項,則在主界面的右邊窗口中顯示出該組件相關屬性的完整列表,這些屬性都可以方便地進行相應的修改。
1.3.3 SetupTypes頁面
  項目工作區窗口的Setup Types頁面用來管理提供給用戶的安裝類型。缺省時,一個安裝項目通常有Typical(典型) Compact(緊湊)Custom(定製)共三種安裝類型。雙擊某種安裝類型,則在主界面的右邊窗口中顯示出該安裝類型相關的組件。
  在組件中,凡是文件組前面有一個帶鉤號()的圖標,表示該文件組已被選入相應的安裝類型中。反覆雙擊文件組前面的圖標可在"選入""不選"之間進行切換。
1.3.4 SetupFiles頁面
  項目工作區窗口的Setup Files頁面用來管理在安裝過程中所需要的安裝文件,它通常有含有下面一些內容。
(1) Splash Screen(啓動畫面)
  InstallShieldSetup.bmp作爲安裝程序的啓動畫面。若在不同的語系(中文、English)中放置各自的位圖文件,則安裝程序將根據安裝環境的不同語系選用相應的Setup.bmp;若將該文件放置在Language Independent(與語系無關)目錄項中,則不管操作系統是何種語系,都將以此位圖文件作爲程序安裝的啓動畫面。
(2) Language Independent(與語系無關)
  它允許用戶爲各種操作系統或專門爲Windows 95/98/NT操作系統指定相應的安裝文件,這樣不管怎樣的語系,安裝程序都會根據相應的操作系統來揀選相應的文件。(3) (3)其他語系相關的文件
  它允許用戶爲各種操作系統或專門爲Windows 95/98/NT操作系統指定相應的安裝文件,這樣安裝程序會根據相應的語系和語系下的操作系統來揀選相應的文件。
(4) Advanced Files(高級文件)
  在用具體的媒介發佈時,允許用戶在相應的媒介中放置一些非壓縮文件。
1.3.5 FileGroups頁面
  項目工作區窗口的File Groups頁面用來管理安裝項目所需要的文件組。缺省時,安裝程序項目通常有Example Files(示例文件組)Help Files(幫助文件組)

Program DLLs(應用程序所需的DLL文件組)Program Executable Files(應用程序文件組)以及Shared DLLs(共享DLL文件組)共五個文件組。
  雙擊某個文件組,則在主界面的右邊窗口中顯示出該文件組的相關屬性,用戶可以方便地進行修改。
  1.3.6 Resources頁面
  項目工作區窗口的Resources頁面用來管理安裝項目所需要的安裝資源。缺省時,一個安裝項目通常有String Table(字符串表)Registry Entries(註冊項)Shell Objects(外殼對象)共三種資源,其中Shell Objects資源是在Windows 95/98Windows NT 4.0及其以後操作系統中創建被安裝應用程序的程序文件夾(Folder命令)、桌面圖標或相應的快捷方式(Shortcut命令)
  1.3.7 Media頁面
  項目工作區窗口的Media頁面用來管理程序發佈時的媒介。在該頁面中包含Media Build Wizard(媒介創建嚮導)、缺省的媒介以及用戶新創建的媒介等項,單擊"Media Build Wizard"將開始媒介創建嚮導,用來創建新的媒介。
  需要說明的是:在各個頁面中,右擊鼠標時都會彈出相應的快捷菜單,它包含當前狀態下的一些常用操作。

 

集成開發環境的初步實踐
2001-01-18· adding·yesky

前面介紹了關於集成開發環境的一些基本情況,這裏以空類型的安裝項目爲例,進一步說明集成開發環境的使用過程。
  1.4.1 創建一個空的安裝項目
  在InstallShield 5.5中,利用Project Wizard(安裝項目嚮導)和安裝項目模板可以創建Windows應用程序、數據庫應用程序以及其他類型程序的安裝項目。這裏,我們首先對空類型的安裝項目作簡單說明,其他安裝項目類型將在以後的章節中陸續介紹。
  一個空類型的安裝項目包含了一般安裝項目的基本框架,只是安裝腳本文件中沒有相應的安裝程序代碼,因而不執行任何操作。創建一個空類型的安裝項目的最大好處是可以幫助用戶熟悉和掌握InstallScript語言的用法,並由此編制出簡繁隨意的安裝程序來。
  在InstallShield 5.5中,想要創建一個空類型的安裝項目,只需選擇"File"菜單->"New"菜單命令,在"New"對話框中選定"Blank Setup",並單擊[確定]按鈕即可,如圖1.5所示。

此時,InstallShield 5.5自動爲該安裝項目命名爲"Blank Setup",並定位到項目工作區窗口的Scripts頁面,而且還在集成開發環境的右邊窗口中打開相應的腳本文件。

1.4.2 添加代碼
  在空類型安裝項目的腳本文件中,一開始是沒有相應的安裝程序內容的,它需要用戶添加一些代碼,例如:

...
program // 每個安裝腳本程序都是以program開始
SprintfBox(INFORMATION,"問候","%s", "您好!" ); // 消息對話框
endprogram // 每個安裝腳本程序都是以endprogram結束
1.4.3 編譯並運行
  打開Build菜單,選擇Compile菜單項或按快捷鍵Ctrl+F7,系統開始對"Blank Setup"進行編譯,同時在輸出窗口中在線地顯示出編譯過程的情況,當出現
Done - 0 error(s), 0warning(s)
字樣時,表示"Blank Setup"安裝程序可以被運行了。
  在"Build"菜單中選取"Run Setup"菜單命令或按快捷鍵Ctrl+F5,就可以運行"Blank Setup"安裝項目。
  運行剛開始,出現"Setup"對話框,用來顯示準備安裝嚮導的進展情況,如圖1.6



  然後才執行前面添加的程序代碼,其結果如圖1.7所示。

本章着重介紹了InstallShield5.5的集成開發環境,並以空類型的安裝項目爲例簡單地說明了安裝腳本程序的添加、編譯、運行的過程。下一章將討論InstallScript腳本語言的基礎內容。

 

2 InstallScript腳本語言基礎
2001-01-19· adding·yesky

InstallScript是專門用來編寫InstallShield安裝程序的腳本語言。由於InstallScriptC語言極爲相似,因而使得Visual C++用戶編寫安裝腳本程序頗爲得心應手。即使對於沒有任何語言基礎的用戶來說,編寫InstallScript程序也不會覺得無從下手,因爲InstallScript程序結構是非常簡單的。並且,InstallScript爲用戶提供了超過250個的內部函數,從而使得用戶不需要太多的代碼就能編寫出具有專業水準的安裝程序來。
  2.1 InstallScript程序結構
  同其他程序設計語言一樣,InstallScript腳本語言也有自己的程序結構。
  2.1.1 幾個InstallScript程序
  下面先來看看幾個比較簡單的InstallScript程序。
  [Ex_Hello] 一個簡單的InstallScript程序,用來彈出"問候"對話框。
STRING szTitle;
program
szTitle = "問候";
SetDialogTitle(DLG_MSG_INFORMATION, szTitle);
MessageBox("您好!", INFORMATION );
endprogram
   程序中,program...endprogram構成主程序體,每一個InstallScript程序中都必須包含一個且只有一個這樣的主程序體。在主程序體外,只能是變量定義、用戶函數定義以及預處理指令等,而程序體內可以包括若干條語句,每一條語句都由分號""結束。本例中,SetDialogTitleMessageBox都是InstallScript的內部函數,它們分別用來設置對話框的標題和顯示指定的信息文本,INFORMATION是一個系統預定義的常量,szTitle變量是在程序體外定義的字符串變量。
  [Ex_Func] 自已定義一個函數,用來顯示消息對話框。
STRING szTitle; // 定義一個字符串變量
prototype MyMessage(STRING,STRING); // 自定義函數的聲明
program
szTitle = "問候";
MyMessage(szTitle, "您好!");
endprogram
function MyMessage(szTitle,szMessage) /* MyMessage函數體 */
begin
SetDialogTitle(DLG_MSG_INFORMATION, szTitle);
MessageBox(szMessage,INFORMATION );
end;

儘管本例的結果和Ex_Hello示例相同,但它使用了自定義函數MyMessageInstallScript語言規定,一個自定義函數名必須在program關鍵字前面聲明,而函數體代碼的實現代碼必須在endprogram後進行,且每個自定義的函數體都必須以begin開始end結束(注意end後要有分號"")。程序中的"/*...*/"之間的內容或"//"開始一直到行尾的內容是用來註釋的,它的目的只是爲了提高程序的可讀性,對編譯和運行並不起作用。正是因爲這一點,註釋的內容可以用漢字來表示,也可以用英文來說明,只要便於理解就行。
[Ex_Include] 使用包含文件。
#include"Sddialog.h";
STRING szTitle, szMsg,svDir;
program
szTitle ="SdAskDestPath Example";
svDir ="C:\\EXAMPLE\\TARGET";
szMsg = "";
// 獲取用戶指定的安裝路徑
if(SdAskDestPath(szTitle, szMsg, svDir, 0) = NEXT) then
TARGETDIR = svDir;
endif;

// 顯示用戶指定的安裝路徑
SprintfBox(INFORMATION,"SdAskDestPath", "Successful.\n\nThe Target " +
"directory is:" + TARGETDIR);
endprogram
#include"Sddialog.rul"
  該程序是使用Sd(Script Dialog,腳本對話框)對話框函數的一個示例。InstallScript語言規定,在調用Sd對話框函數時,需要在program前加上#include "Sddialog.h"語句,而在endprogram後加#include "Sddialog.rul"。與C語言相似,#include "Sddialog.h"#include "Sddialog.rul"InstallScript的編譯指令,稱爲預處理指令。InstallScript編譯系統會根據預處理指令#include中的文件名,把該文件的內容包含進來。也就是說,實際程序的代碼長度是在原來長度的基礎上增加了Sddialog.hSddialog.rul文件的長度。程序中,SprintfBox參數內容中的"\n"是換行符,即在"Successful."文本後回車換行。

2.1.2InstallScript程序的基本組成
   從上面的幾個示例可以看出,一個InstallScript程序往往由預處理命令、函數、語句、變量以及註釋等幾個基本部分組成的。
  (1) 預處理命令
  在InstallScript程序的一開始經常出現含有以"#"開頭的命令,它們是預處理命令。InstallScript提供了三類預處理命令:宏定義命令、文件包含命令和條件編譯命令。
  (2) 函數
  一個InstallScript程序是由若干個函數組成的。這些函數中,有的是InstallScript系統中所提供的內部函數,有的是用戶根據自己需要自己編制設計的函數(如例Ex_Func中的MyMessage)
  (3) 語句
  語句是組成程序的基本單元,它可以是用來判斷的條件語句,也可以是用來反覆運行的循環語句等。這些語句是組成InstallScript程序中的最重要部分之一。
  (4) 變量
  大多數程序離不開變量。InstallScript變量的類型比較簡單,主要有數值型(NUMBER)、字符串型(STRING)以及鏈表型(LIST)等,例如例Ex_Hello中的szTitle是一個STRING型變量。
  (5) 註釋
  程序的目的不僅在於實現某種功能、解決某個問題,而且還在於數據結構和算法的交流。因此在程序中添加必要的註釋是非常重要的,它能提高程序的可讀性,幫助用戶對程序的理解。
  需要說明的是,InstallScript不支持控制檯的輸入和輸出,數據的輸入和輸出是通過對話框進行的。

2.1.3InstallScript程序的書寫風格
   儘管InstallScript語言比CC++語言容易理解,但對於初學者來說,一開始就養成良好的編程習慣仍然是非常重要和必要的。
  1. 標識符命名
   標識符是用來標識變量名、函數名、結構名、文件名等的有效字符序列。標識符命名的好壞直接影響程序的可讀性,例如a1b1c1d雖然是合法的標識符,但卻是不好的標識符,因爲它不能讓人理解它們所代表的含義。下面幾個原則是命名時所必須注意的:
  (1) 合法性
  InstallScript規定標識符由大小寫字母、數字字符(09)和下劃線組成,且第一個字符必須爲字母或下劃線。任何標識符中都不能有空格、標點符號及其他字符,例如下面的標識符是不合法的:
   93SalaryYouhe.Ding$178#5f68r<D
注意,InstallScript中標識符的大小寫是有區別的。例如,dataDataDaTaDATA等都是不同的標識符。
用戶定義的標識符不能和系統的關鍵字同名。以下是43InstallScript關鍵字:

    abort begin BOOL BYREF
    case CHAR default downto
    else end elseif endfor
    endif endprogram endswitch endwhile
    exit for function GDI
    goto HWND if INT
    KERNEL LIST LONG NUMBER
    POINTER program prototype repeat
    return SHORT step STRING
    switch then to typedef
    until USER while
  需要注意的是,用戶定義的標識符還不能和InstallShield的函數名、系統變量名以及預定義的常量名相同。
  (2) 有效性
雖然,標識符的長度(組成標識符的字符個數)是任意的,但最好不能超過32個,因爲InstallShield的編譯系統只能識別前32個字符,也就是說前32個字符相同的兩個不同標識符被有的系統認爲是同一個標識符。

(3) 易讀性
  在定義標識符時,若能做到"見名知意"就可以達到易讀性的目的。爲了達到這個目的,許多Visual BasicVisual C++Delphi等程序員廣泛使用匈牙利的命名規則來定義標識符,InstallScript也使用這個<
命名規則。
   匈牙利的命名規則是將標識符的類型(小寫)來作爲標識符的前綴。例如前面的szTitle表示一個字符串變量,其中的sz表示STRING變量類型。表2.1列出了常用變量的前綴。除此之外,對於函數名的命名往往使用多個單詞來組成,每個單詞的第一字母都是大寫,例如前面的SdAskDestPath函數名。

 2.縮進和註釋
   縮進是指程序在書寫時不要將程序的每一行都由第一列開始,而且在適當的地方加進一些空行或空格。它同註釋一樣,也是爲了提高程序的可讀性。
  註釋的重要性已在前面論及過,這裏不再重複。但要注意的是:
  (1) 註釋應在編程的過程中同時進行,不要指望程序開發完成後再補寫註釋。那樣只會多花好幾倍的時間,更爲嚴重的是,時間長了以後甚至會讀不懂自己寫的程序。 必要的註釋內容應包含:腳本程序的總體註釋(文件名、作用、創建時間、版本、作者及引用的手冊、運行環境等)、函數註釋(目的、算法、使用的參數和返回值的含義、對環境的一些假設等)及其他的少量註釋。千萬不要陳述那些一目瞭然的內容,否則會使註釋的效果適得其反』些空行或空格。它同註釋一樣,也是爲了提高程序的可讀性。
  註釋的重要性已在前面論及過,這裏不再重複。

2.2 數據類型

程序可以看成是由數據結構和算法組成的。數據結構體現對數據的描述,而算法反映了對數據的操作及處理。任何一門計算機語言都必須包括數據類型、運算符與表達式等內容來定義和實現程序中的數據結構和算法。
  2.2.1 基本數據類型
  InstallScript的數據類型比其他任何高級語言的數據類型要簡單易用,它只有基本類型和結構類型兩類。這裏先討論InstallScript的基本數據類型。
  基本數據類型是InstallScript的內部數據類型,包括CHAR(字符型)NUMBER(數值整型)等,表2.2列出各種基本數據的類型。

   需要說明的是:在InstallScript的數據類型中,除了BOOLHWNDLIST類型不能使用小寫外,其餘的數據類型還有其小寫形式,例如intnumberstring等,用來提供一種方便。但是,InstallScript沒有無符號數值類型以及浮點數值類型。

2.2.2 常量與變量
  根據程序中數據的可變性,數據可以分爲常量和變量兩大類。
  1. 常量
  在程序運行過程中,其值不能被改變的量稱爲常量。常量可分爲不同的類型,如1200-6爲整型常量,‘a’、‘b’爲字符常量。常量一般從其字面形式即可判別。
  InstallScript的常量有整型常量、字符常量和字符串常量等類型。這些常量的含義和C語言基本一致,故這裏不再重複。
  需要說明的是,在InstallScript中還可以用一個標識符表示一個常量。
[Ex_Define] #define定義符號常量。
#define TITLE "問候"
program
SprintfBox(INFORMATION,TITLE,"%s","您好!");
endprogram
  程序中用#define命令行定義TITLE,使其代表字符串常量"問候",此後凡是在程序中出現的TITLE都代表"問候"
  這種代替常量本身的標識符稱爲符號常量。在程序中使用符號常量不僅可以提高程序的可讀性(標識符總比常量本身更具意義),而且修改也極爲方便。
  2. 變量
  變量是指在程序執行中其值可以改變的量。變量的作用是存儲程序中需要處理的數據,它可以放在程序中的任何位置上。但無論如何,在使用一個變量前必須先定義這個變量。
  變量是用下面的格式語句進行定義的:
  類型 變量名錶;
例如:
  NUMBER nNum1;
  NUMBER nNum2;
  NUMBER nNum3;
  BOOL bValidEntry;
  其中,nNum1nNum2nNum3被定義成整型變量,而bValidEntry被定義成布爾型變量。有時,還可以將同類型的變量定義在一行語句中,不過變量名要用逗號(,)分隔。例如上面的變量可這樣定義:
  NUMBER nNum1, nNum2, nNum3;
   BOOL bValidEntry;

在定義字符串常量時,可以指定字符串的長度,例如:
  STRING szUserName[128]; // 指定字符串的長度爲128個字符
  若不指定其長度,則InstallScript自動指定。16位操作系統中,字符串的長度被指定爲512個字符,而32位操作系統中,字符串的長度被指定爲1024個字符。
  在定義變量時,需要注意:
  (1) 不能在主程序體(program...endprogram之間)或函數體內部(begin...end之間)定義變量,變量必須定義在程序體外或函數名與begin關鍵字之間。例如:
  function SdAskDestPath(szTitle, szMsg,svDir, nStyle)
  STRING szDlg, svDirLoc, szTemp;
   INT nId, nTemp;
   HWND hwndDlg;
   BOOL bDone;
  begin
   ...
  end;
(2) 在同一個主程序或同一個函數體中不能有同時出現兩個相同的變量名。
(3) 不能在變量定義的同時,給變量賦初值。
  2.2.3 InstallScript運算符簡介
  和其他的程序設計語言一樣,InstallScript記述運算的符號稱爲運算符,運算符的運算對象稱爲操作數。一個操作數可以是變量、常量或是具體的數值等。對一個操作數運算的運算符稱爲單目運算符,如-a;對二個操作數運算的運算符稱爲雙目運算符,如3+5等。
  InstallScript的運算符分爲以下幾類:
  算術運算符 ( +, -, *, /)
  關係運算符 ( <, >, =, <=, >=, != )
  邏輯運算符 ( &&, ||, ! )
   位運算符 ( &, |, ~, ^, <<, >>)
  賦值運算符 ( = )
  指針運算符 ( *, & )
  分量運算符 ( ., -> )
  下標運算符 ( [ ] )
 字符串運算符 ( ^, +, % )
  其它 ( BYREF運算符 )

2.2.4 算術運算符
  算術運算符包括常用的加減乘除四則運算符以及單目正負運算符,如下所示:
  + (正號運算符,如+4)
  - (負號運算符,如-4)
  * (乘法運算符,如6*8)
  / (除法運算符,如6/8)
  + (加法運算符,如6+8)
  - (減法運算符,如6-8)
InstallScript中算術運算符和數學運算的概念及運算方法是一致的,但要注意以下幾點:
(1) 除法運算
  兩個整數相除,結果爲整數,如7/5的結果爲1,它是將小數部分去掉,而不是四捨五入。但InstallScript不支持浮點運算。
(2) 優先級和結合性
  在一個包含多種算術運算的混合運算中,先乘除後加減的運算規則是由運算符的優先級來保證的。InstallScript將表達式求值中多種運算之間的先後關係(即運算符之間的優先關係)用運算符的優先級表示。在算術運算符中,單目運算符的優先級最高,其次是乘、除,最後是加減。
  優先級相同的運算符,則按它們的結合性進行處理。所謂運算符的結合性是指運算符和操作數的結合方式,它有"從左至右""從右至左"兩種。"從左至右的結合"又稱"左結合",是指運算符左邊的操作數先與運算符相結合,再與運算符右邊的操作數進行運算,如3*5/4的次序是先乘後除;而自右至左的"右結合"的次序剛好相反,它是將運算符右邊的操作數先與運算符相結合,如-i+6相當於(-i)+ 6
  在算術運算符中,除單目運算符外,其餘運算符的結合性都是從左至右的。
(3) 關於書寫格式
  在使用運算符進行數值運算時,若書寫時沒有在雙目運算符兩邊加上空格,則有時編譯系統會做出與自己想象中不同的理解。例如:
  -5*-6-7

  -5 * -6 - -7 // 注意空格
結果是不一樣,前者發生編譯錯誤,而後果的結果是37
  爲了避免上述情況的發生,在書寫時,有時應有意識地加上一些括號。這樣不僅增強程序的可讀性,而且,尤其當對優先關係猶豫時,加上括號是保證正確結果的最好方法。

2.2.5 賦值運算符和賦值表達式
  在InstallScript腳本語言中,賦值符"="是一個雙目運算符,結合性從右至左,其作用是將賦值符右邊操作數的值賦給左邊的操作數。每一個合法的表達式在求值後都有一個確定的值和類型。賦值表達式的值是賦值符右邊操作數的值,賦值表達式的類型是賦值符右邊操作數的類型。例如下面語句:
  STRING szName;
   LONG nValue;
   BOOL bDone;
   HWND hInstance;
   INT iStyle;
   LIST LISTINFO;
  program
   szName = "InstallShield";
   nValue = 15;
   bDone = FALSE;
   hInstance = 0;
   iStyle =DLG_MSG_STANDARD|DLG_CENTERED;
   LISTINFO = ListCreate(STRINGLIST);
   ...

但是,InstallScript不支持多重賦值運算,例如a = b = c 相當於C++a = b ==c。也就是說,若 b 不等於c,表達式爲a=0,若bc相等,則表達式爲a=1

InstallScript往往利用邏輯運算後的結果對程序進行判斷、選取等控制。

 

2.3 邏輯運算和判斷選取控制
  2.3.1 關係運算符
  關係運算是邏輯運算中比較簡單的一種。所謂"關係運算"實際上是比較兩個操作數是否符合給定的條件。若符合條件,則關係表達式的值爲"",否則爲""。在InstallScript編譯系統中,往往將""表示爲TRUE,將""表示爲FALSE。而任何不爲0的數被認爲是""0被認爲是""
  由於關係運算需要兩個操作數,所以關係運算符都是雙目運算符。InstallScript提供了下列6種關係運算符:
  <(小於)<=(小於等於)>(大於)>=(大於等於)= (相等於)!=(不等於)
  其中,前4種的優先級相同且高於後面的兩種,但關係運算符的優先級低於算術運算符。
需要說明的是:
  (1)InstallScript賦值運算符和等於的關係運算符使用同一個"="符號。
  (2)InstallScript不支持賦值和關係運算同在一個表達式中的情形。例如,下面的語句是不允許的:
  if ((listID= ListCreate (NUMBERLIST)) = LIST_NULL)
  then
    . . .
  endif;
  2.3.2 邏輯運算符
  邏輯運算符是用於將多個關係表達式或邏輯量("""")組成一個邏輯表達式。InstallScript提供了下列3種邏輯運算符:
  ! 邏輯非(單目)
  && 邏輯與(雙目)
  || 邏輯或(雙目)
  "邏輯非"是指將""""""""
  "邏輯與"是指當兩個操作數都是""時,結果才爲"",否則爲""
  "邏輯或"是指當兩個操作數中有一個是""時,結果就爲"",而只有當它們都爲""時,結果才爲""
  "邏輯非""邏輯與""邏輯或"的優先級依次從高到低,且"邏輯非"的優先級還比關係運算符高,而"邏輯與""邏輯或"的優先級卻比關係運算符低。

C不一樣,InstallScript對邏輯表達式的值非常敏感,例如下面代碼:

  if (iVar =10) && (MyFunction( ) = 0)
  then
    MessageBox("Thatis so true.", INFORMATION);
  endif;
  只有當&&運算符左邊的結果爲TRUE時,右邊的函數MyFunction纔會被執行。爲了不引起誤解,最好將上述代碼改寫成:
  if (iVar =10) then
   if(MyFunction( ) = 0) then
    MessageBox("Thatis so true.", INFORMATION);
   endif;
  endif;
  2.3.3 if語句
  if語句是用來判定所給定的條件是否滿足,並根據判定的結果("""")決定執行給出的兩種操作之一。
  InstallScript提供了下列5種形式的if語句。
  (1) if-then結構
   if-then結構具有下列形式:
   if (條件表達式) then
     語句
   endif;
  當"條件表達式"表達爲""時,then後面的語句纔會被執行。一個"條件表達式"可以是一個布爾或整型常量、變量、產生布爾或整型結果的表達式以及能返回整型結果的函數。
  例如:
  if(szStringA = "exit") then
    AskYesNo( "Are you sure you want to exit?" , NO );
  endif;
  當字符串szStringA"exit"相等時,函數AskYesNo纔會被執行,否則跳過if-then結構,執行後面的語句。
條件表達式兩邊的圓括號"()"是可選的,但最好能使用,因爲它能提高程序代碼的可讀性。(2) if-then-else結構
  if-then-else結構具有下列形式:
    if (條件表達式) then
      語句1
    else
      語句2
    endif;
  當"條件表達式"表達爲""時,執行then後面的語句1,而當"條件表達式"表達爲""時,執行語句2。例如:
   ifszStringA = "exit" then
    AskYesNo("Are you sure you want to exit?" , NO );
    //szStringA等於"exit"
  else
    MessageBox("Please wait... ", INFORMATION ); // szStringA不等於"exit"
   endif;
  (3) if-then-else的嵌套結構
  在if-then-else語句中以包含一個或多個if-then-else語句稱爲if-then-else語句的嵌套。其一般形式如下:
    if (條件表達式1) then
     if (條件表達式2) then
       語句1 //當條件表達式2TRUE時執行
     else
       語句2 //當條件表達式2FALSE時執行
     endif;
    else
    if (條件表達式3) then
       語句3 //當條件表達式3TRUE時執行
     else
       語句4 //當條件表達式3FALSE時執行
    endif;
    endif;
  例如:
  ifszStringA = "exit" then
   AskYesNo("Are you sure you want to exit?" , NO );
  else
   ifszStringA = "continue" then
    MessageBox("Please wait...", INFORMATION );
   else
    UserErrorHandler;
   endif;
  endif;
  當字符串szStringA"exit"相等時,執行函數AskYesNo;當szStringA"continue"相等時,執行MessageBox,而當szStringA等於其他值時,用戶自定義的UserErrorHandler函數被執行。

(4) elseif結構
  elseif結構具有下列形式
   if (條件表達式1) then
     語句1
   elseif (條件表達式2) then
     語句2
   elseif (條件表達式3) then
     語句3
     ...
   endif;
  例如:
   ifszStringA = "exit" then
     AskYesNo("Are you sure you want to exit?" , NO );
   elseifszStringA = "continue" then
     MessageBox("Please wait...", INFORMATION );
   elseifszStringA = "reboot" then
     gotoStartHere;
   endif;
     ...
   StartHere:
     ...
   代碼中,StartHere是一個語句標號。當字符串szStringA"exit"相等時,執行函數AskYesNo;若不相時等,則將szStringA"continue"比較,相等時執行函數MessageBox;若不相等,再將szStringA"reboot"比較,...
  (5) if goto結構
   ifgoto的結構具有下列形式:
   if (條件表達式) goto 標號;
    當條件表達式爲""時,將流程轉到"標號"所在的位置。這種形式最簡單,例如:
    Name:
    AskText("Companyname:", "", szSrc);
   if (szSrc= "") goto Name;
  一旦szSrc爲空字符串時,就不停地要求用戶輸入相關內容。

2.3.4 switch...endswitch語句
  當程序有多個條件判斷時,若使用if語句則可能使嵌套太多,降低了程序的可讀性。開關語句switch能很好地解決這個問題,它具有下列形式:
  switch ( 表達式 )
     case 常量1 :語句1
     case 常量2 :語句2
       ...
     case 常量n :語句n
     default :語句n+1
  endswitch;
  當表達式的值與case中某個值相等時,就執行該case""號後面的所有語句。若case中所有的值都不等於表達式的值,則執行default:後面的語句,若default不存在,則跳出switch結構。
  這裏的表達式可以是一個常量、變量、算術表達式、邏輯表達式或一個有返回值的函數,但必須用圓括號"()"括起來。case後面只能是一個常數或常量名,不能爲變量名、有返回值的函數名、字符串表涉及的字符串名以及其他類型的表達式等。
  例如:
  STRINGszMsg, svResult;
  NUMBERnvResult;
  program
   GetSystemInfo(VIDEO,nvResult, svResult); // 獲得系統顯卡類型
   switch(nvResult)
      caseIS_UNKNOWN: szMsg = "用戶顯卡類型未知";
      caseIS_EGA : szMsg = "EGA顯卡";
      caseIS_VGA : szMsg = "VGA顯卡";
      caseIS_SVGA : szMsg = "Super VGA (800 x 600) 顯卡";
      caseIS_XVGA : szMsg = "XVGA (1024 x 768) 顯卡";
      caseIS_UVGA : szMsg = "分辨率大於1024 x 768的顯卡";
         default: szMsg = "錯誤";
   endswitch;
    MessageBox(szMsg,INFORMATION);
  endprogram
  每次只有一個case語句塊被執行,執行後,將跳出switch結構,執行endswitch後面的語句,這一點與C語言不同,InstallScripcase語句後不需要break

 

2.4 循環語句

InstallScript語言中,可以用以下幾種形式的語句來實現循環
  (1) goto語句和if語句構成循環
  (2) while..endwhile語句
  (3) repeat..until語句
  (4) for..endfor語句
2.4.1 goto語句和if語句構成循環
  goto語句爲無條件轉向語句,它的一般形式爲:
  goto 語句標號;
  語句標號用標識符表示,它的命名規則與變量名相同,不能用整數來作爲標號。結構化程序設計方法主張限制使用goto語句,因爲濫用goto語句將使程序流程無規律、可讀性差。但也不是絕對禁止使用goto語句。一般來說,可以有兩種用途:一是從循環體中跳到循環體外,另一是與if語句一起構成循環結構。
  例如:
  Name:
  AskText("Companyname:", "", szSrc);
  if (szSrc ="") then
  MessageBox("Pleaseenter the company name.", SEVERE);
  goto Name;
  endif;
2.4.2 while..endwhile
語句
  while..endwhile循環語句具有下列形式:
   while (表達式)
    語句
   endwhile;

語句是此循環的循環體,它可以是一條語句,也可以是多條的複合語句。當"表達式"""時便開始執行while循環體中的語句,然後反覆執行,每次執行都會判斷"表達式"是否爲"",若爲"",則終止循環。
  [Ex_While] 一個簡單的while..endwhile循環程序。
   NUMBERnCount;
   program
    nCount =1;
    while(nCount < 5)
     MessageBox("This is still true.", INFORMATION);
     nCount =nCount + 1;
    endwhile;
   endprogram
  由於nCount的初始值爲1,因此while的表達式爲"",循環體第一次被執行,同時nCount被增加1,當循環4次後,nCount被增加到5,此時while的表達式爲"",結束循環,執行endwhile後面的語句。
  需要說明的是,while..endwhile循環可以使用嵌套,但每一個while循環體都必須以endwhile來結束。在while循環體中不能定義語句"標號"
2.4.3 repeat..until語句
  repeat..until循環語句具有下列形式
   repeat
    語句
   until (表達式) ;
  "語句"是此循環的循環體,它可以是一條語句,也可以是多條的複合語句。當語句執行到until時,將判斷"表達式"是否爲"",若是,則繼續執行循環體,直到下一次表達式等於""爲止。
[Ex_Repeat]repeat ..until循環程序。
   NUMBERnCount;
   program
    nCount =1;
    repeat
     MessageBox("Countis less than 5", INFORMATION);
     nCount =nCount + 1;
    until(nCount = 5);
    endprogram
  同while一樣,在repeat循環體中也不能定義語句"標號"

2.4.4 for..endfor語句
  for..endfor循環語句具有下列形式:
   for X=A toB step C
    語句
   endfor;
 其中,X爲循環控制變量(或稱循環變量),可以用任一簡單變量來表示;A爲循環變量初值;B爲循環變量終值;C爲循環變量的增量或步長。若省略step關鍵字,則增量自動爲1。關鍵字to也可用downto代替,表示從數值從高到低自動減去C的量。
[Ex_For]for..endfor循環程序。
   NUMBERiCount;
   program
    foriCount = 1 to 5
     MessageBox("You will see this 5 times", INFORMATION);
    endfor;
   endprogram
  [Ex_DownFor] 使用downtofor..endfor循環程序。
   NUMBER j;
   program
    for j =20 downto 10 step 5
     MessageBox("You will see this 3 times", INFORMATION);
    endfor;
   endprogram
  同repeat一樣,在for循環體中也不能定義語句"標號"
2.4.5 abortexit
  abort(異常中斷)exit(退出)InstallScript的兩個關鍵字。在安裝程序執行過程中,當用戶按下Esc鍵、F3鍵或單擊安裝對話框中的[Cancel]按鈕時,系統將自動執行abort,其目的是將已安裝的內容從計算機系統中清除掉。任何時候,只要安裝程序遇到abort,都會進行上述的處理。而exit只是中斷安裝程序的執行,因此若用戶在未安裝完之前需要程序中斷,則應在主程序體中使用abort來代替exit。但也應注意,雖然exit還可代替程序中的endprogram,與program一起構成一個主程序體,但最好不要這樣。??palign="right">

 

2.5

在結構化程序設計中,通常需要若干個模塊實現較複雜的功能,而每一個模塊自成結構,用來解決一些子問題。這種模塊化的結構設計思想能很好地發揮"團隊"力量,在代碼修改和重用上,極爲方便和快捷。函數正是結構化設計程序的基本結構。
2.5.1 概述
  InstallScript允許用戶在安裝程序中使用下列三種類型的函數:
  (1) InstallShield內部函數
  InstallShield中定義了250多個的內部函數,包括字符串處理、文件、路徑以及文件夾等操作的函數。除了Sd對話框函數外,其他大多數內部函數可以直接在程序中進行調用。
  (2) 用戶自定義的函數
  自定義的函數是用戶自己構造的函數,它必須在主程序體program關鍵字前聲明,而在endprogram後進行定義,才能在程序中進行調用。
  (3) DLL函數
  Windows動態鏈接庫(DynamicLinking Library,簡稱DLL)提供了一些特定結構的函數,能被應用程序在運行過程中裝入和連接,且多個程序可以共享同一個動態鏈接庫,從而可以大大節省內存和磁盤空間。從編程角度來說,動態鏈接庫可以提高程序模塊的靈活性,因爲它本身是可以單獨設計、編譯和調試的。同大多數編程語言一樣,在InstallShield5.5中也可方便調用DLL函數。
2.5.2 自定義函數的聲明和定義
  在InstallScript腳本語言中,使用用戶自定義函數必須先進行函數的聲明,然後進行函數的定義。
  (1) 自定義函數的聲明
  自定義的函數必須在主程序體program關鍵字前按下列形式進行聲明:
  prototype 函數名(形參類型1,形參類型2...);
  其中,prototypeInstallScript的關鍵字,它通知編譯器該行語句是用來聲明一個自定義函數。"形參類型"是指InstallScript的基本數據類型,如INTSTRINGSHORT等。例如下面的函數聲明都是合法的:
   prototypeFunctionName (INT, STRING, SHORT); // 聲明有三個形參的函數
   prototypeCopyBitmapExample (); // 聲明一個沒有形參的函數
   prototypeFileTransfer (LONG, LONG, LONG, STRING, STRING);// 聲明有五個形參的函數
  函數名是一個有效的InstallScript標識符(注意命名規則),函數名後面必須跟一對圓括號"()",以區別於變量名及其他用戶定義的標識名。函數的形式參數寫在括號內,參數表中參數個數可以是0,表示沒有參數,但圓括號不能省略,也可以是一個或多個參數,但多個參數間要用逗號分隔。

(2) 函數的定義
  函數聲明後,必須在主程序體endprogram後按照下列的形式進行相應的函數定義:
  function 函數名(形參1,形參2...)
    定義函數內部使用的變量;
    begin
     語句
    end;
  函數的函數體由beginend之間的若干條語句組成,用於實現這個函數執行的動作。"函數名"必須和聲明時的函數相同,"形參"應與聲明時的形參類型一一對應,但"形參名"可以任意的有效InstallScript標識符。如果需要在函數中使用其他變量,則這些變量應在begin前進行定義。
  [Ex_MyFunc]使用自定義函數SetupScreen
  prototypeSetupScreen(NUMBER); // 聲明一個函數,只有一個形參
  program
   SetupScreen(WHITE);// 自定義函數在程序體中的調用
  endprogram
  function SetupScreen (nColor) // 函數的定義,注意nColorNUMBER是一一對應的
    numbernDx, nDy; // 函數內部變量的定義
    begin
     GetExtents(nDx, nDy );
     Enable(FULLWINDOWMODE);
     Enable(INDVFILESTATUS);
     Enable(BITMAP256COLORS);
     Enable(DIALOGCACHE);
     SetTitle("Installing " , 24, nColor);
     SetColor(BACKGROUND, BK_BLUE);
     SetColor(STATUSBAR, BLUE);
     SetTitle("Setup", 0, BACKGROUNDCAPTION);
     Enable(BACKGROUND);
     Delay(1);
    end;
   和C語言一樣,不允許在一個函數體中再定義函數。
(3) 函數的調用
  函數調用的一般形式爲: 函數名(實際參數表 );
  所謂"實際參數"(簡稱"實參"),它與"形參"相對應,是實際調用函數時所給定的常量、變量或表達式,且必須有確定的值。需要注意的是:實參與形參的個數應相等,類型應一致,且按順序對應,一一傳遞數據。
  InstallScript中,調用一個函數的方式可以有很多,例如:
  RectangleArea(nLong, nWide);
  // 作爲一個語句,不使用返回值,只要求函數完成一定的操作
  nArea =RectangleArea (nLong, nWide);
  // 將函數的返回值賦予一個變量
  nTotal=RectangleArea (nLong1, nWide1) + RectangleArea (nLong2, nWide2);
  // 將函數的返回值參與運算

2.5.3 BYREF參數和函數的返回值
  在InstallScript的大多數函數中,函數的參數傳遞方式是"按值傳遞"的。所謂"按值傳遞",是指當一個函數被調用時,系統根據實參和形參的對應關係將實際參數的值一一傳遞給形參,供函數執行時使用。函數本身不對實參進行操作,也就是說,即使形參的值在函數中發生了變化,實參的值也不會受到影響。
當然,由於某種原因,用戶有時想要讓形參的改變影響實參,這裏就需要使用BYREF(引用)關鍵字。所謂"引用",簡單地說,它實際上是給一個已知變量起個別名,對引用的操作也就是對被它引用的變量的操作。一個函數能使用引用傳遞的方式是在函數聲明時將形參類型前加上引用關鍵字"BYREF"。例如:
  prototypeStrInvert(BYREF STRING );
  若形參列表中不止一個形參定義成引用形式,則必須在每個引用形參類型前加上引用關鍵字"BYREF"。例如:
  prototypeStrChangeChar( BYREF STRING, CHAR, BYREF BOOL);
  // 注意,第二個形參沒有被變成引用形式。
  下面來看看函數參數的引用傳遞的示例。
  [Ex_ByRef] 函數參數的引用傳遞。
    prototypeSwapString( BYREF STRING, BYREF STRING );
    STRINGmyStr1,myStr2;
     program
      myStr1= "這是第一個字符串";
      myStr2= "這是第二個字符串";
      MessageBox(myStr1,INFORMATION);// 結果顯示"這是第一個字符串"
      SwapString(myStr1,myStr2);
      MessageBox(myStr1,INFORMATION);// 結果顯示"這是第二個字符串"
     endprogram
    functionSwapString(svString1, svString2)
     STRINGsTemp;
     begin
      sTemp =svString1;
      svString1= svString2;
      svString2= sTemp;
     end;
  實際上,如果用戶需要函數改變的數值個數只有一個的話,那麼使用函數的返回值就顯得非常隨意。例如:
    functionRectangleArea (nLength, nWidth)
     INTnVal;
      begin
       nVal =nLength * nWidth;
       returnnVal;
      end;
  其中,關鍵字return負責將後面的值作爲函數的返回值,並將流程返回到調用此函數的位置處。由於return的後面可以是常量、變量或任何合法的表達式,因此函數RectangleArea可以改成下列形式:
    functionRectangleArea (nLength, nWidth)
     begin
      returnnLength * nWidth;
     end;
  需要注意的是,一旦執行return語句後,在函數體內return後面的語句不再被執行。

2.5.4 局部變量和全局變量
  InstallScript中每一個變量必須先定義後使用,若變量是在函數定義時函數名和begin之間定義的變量,則此變量就是一個局部變量,它只能在函數體內使用,而在函數體外則不能使用它。若變量是在主程序體program前定義,則該變量就是一個全部變量,它能被後面的所有函數或語句引用。
  在一個程序中,不能有兩個同名的全局變量,但局部變量在其作用範圍外可以同名,甚至可以和全局變量同名。
  [Ex_Variable]函數的局部變量。
   STRINGszVal; // 定義的全局變量
   prototypeAFunction();
    program
     szVal ="YES"; // 給全局變量賦值,結果爲"YES"
     AFunction();
     MessageBox(szVal,INFORMATION); // szVal結果仍爲"YES"
    endprogram
   functionAFunction()
    STRINGszVal; // 函數內部的局部變量,和全局變量同名
    begin
     szVal ="NO"; // 給局部變量賦值,結果爲"NO"
    end;
  在InstallScript中,函數的形參變量被認爲是局部變量。

2.5.5 使用DLL函數
  出於對InstallShield函數功能的擴展,InstallShield專業版還允許用戶使用外部DLL中的函數,其使用步驟如下:
  (1) 首先使用下列形式在主程序體program前聲明所要使用的DLL函數
    prototype.<函數名>( 形參類型列表 );
  (2) 然後,使用UseDLL函數將DLL文件調入內存;
  (3) 接着,用下列形式調用已聲明過的DLL函數;
    函數名 ( 實參 );
  (4) 最後,使用UnUseDLLDLL文件從內存中釋放出來。
    [Ex_DLL] 使用DLL函數。
#define

DLL_FILE"C:\\EXAMPLE\\DLLS\\MSVC\\MSC\\EXAMPLE\\WINDEBUG\\MYDLL.DLL"
//
聲明在MYDLL.DLL文件中的MydllReturn函數
prototype MYDLL.MydllReturn( INT, POINTER );

 

STRING szDLL, svString;
INT nValue;
POINTER psvString;
NUMBER nResult;
BOOL bDone;
program
 szDLL =DLL_FILE;
 /*--------------------------------------------------------------------------*\
 *
MYDLL.DLL文件調入內存。
 \*--------------------------------------------------------------------------*/
 nResult =UseDLL (szDLL);
 if (nResult= 0) then
  MessageBox("UseDLL successful \n\n.DLL file loaded.", INFORMATION);
 else
  MessageBox("UseDLL failed.\n\nCouldn’t load .DLL file.", INFORMATION);
 abort;
 endif;
 bDone =FALSE;
 while (bDone!= TRUE)
   Disable(BACKBUTTON);
   AskText("Enteran example string.", "Example string.", svString);
   psvString= &svString;
   nValue =StrLength(svString);
   // 調用DLL函數
   MydllReturn(nValue,psvString);
   SprintfBox(INFORMATION,"UseDLL", "MydllReturn() changed the string " +"to:%s",

svString);
   // 由用戶控制while循環的終止
   if(AskYesNo("Do another example?", YES) = NO) then
    bDone =TRUE;
   endif;
  endwhile;
/*--------------------------------------------------------------------------*\
*
MYDLL.DLL文件從內存中釋放出來。
\*--------------------------------------------------------------------------*/
  if(UnUseDLL (szDLL) < 0) then
   MessageBox("UnUseDLLfailed.\n\nDLL still in memory.", SEVERE);
  else
   MessageBox("UnUseDLLsuccessful.\n\n.DLL file removed from memory.",
   INFORMATION);
   endif;
endprogram

 

2.6 字符串操作
  同C語言一樣,InstallScript也有許多字符串操作的運算符及其內部函數。
2.6.1 字符和字符串
  雖然InstallScript支持數組類型,但它只支持一維數組。且這裏的字符數組和字符串的概念很不一樣。例如:
  CHARstr[10];
  program
   str="ABCDE";// 產生編譯錯誤
  endprogram
  但是,STRING類型的字符串概念與C語言一樣。它是一個以'\0'爲終止符的一維字符數組,使用數組下標可以獲得相應的字符。例如:
  [Ex_String] 使用字符串。
   prototypeBlankLeadingZeros(BYREF STRING);
   STRINGszString;
   program
    szString= "00001234";
    BlankLeadingZeros(szString);
    MessageBox(szString,INFORMATION);
   endprogram
   functionBlankLeadingZeros(szString) // 將字符串的前導字符'0'變爲空格。
    INT iVal,iLength;
    begin
     iVal =0; // 字符數組的下標從0開始
     iLength= StrLength (szString);
     while(szString[iVal] = "0") && (iVal <= iLength)
      szString[iVal]= " ";
      iVal =iVal + 1;
     endwhile;
    end;
結果是將"00001234"字符串變爲"1234"

2.6.2 字符串的運算符
  在InstallScript腳本程序中,用戶打交道最多的是字符串的操作。爲此,InstallScript提供了下列的字符串運算符:
   ^ (在一個路徑或文件名後添加另一個路徑)
   + (在一個字符串後添加另一個字符串)
   % (在一個字符串中查找一個子串)
  例如:
   szStringVar= "C:\\MYPATH\\" ^ "YOURPATH\\FILENAME";
  的結果爲
   "C:\MYPATH\YOURPATH\FILENAME"
  但是,如果用戶忘記在C:\\MYPATH後加上反斜槓,即:
   szStringVar= "C:\\MYPATH" ^ "YOURPATH\\FILENAME";
  其結果仍然是上述結果。因爲InstallScript"^"運算符自動添加相應的反斜槓。
  用字符串的"+"可以將上述表達式改爲下列形式:
   szStringVar= "C:\\MYPATH\\" + "YOURPATH\\FILENAME";
  但是,在使用字符串運算符中,不能在運算符的兩邊使用圓括號"()"。例如,下面的表達式是錯誤的:
   szPath =szTestPath ^ (AUTOFILE + ".BAT");
  而應該用下列形式:
   szFile =AUTOFILE + ".BAT";
   szPath =szTestPath ^ szFile;
  需要特別注意的是,字符串運算符"%"是在一個字符串中查找一個子串,而不是C語言的求餘運算符。例如:
   szStringVarA= "This is a sample string.";
   if(szStringVarA % "sample") then
    MessageBox("Operationcomplete",INFORMATION);
   endif;
  其結果是MessageBox被執行。若在查找時不要求大小寫敏感,則上述代碼可改爲:
   szStringVarA= "This is a sample string.";
   if(szStringVarA % "SAMPLE") then
    MessageBox("Operationcomplete", INFORMATION);
   endif;
  需要說明的是,字符串運算符%與內部函數StrFind相似,但StrFind還返回被查找的字符串首字符在字符串中的位置。

2.6.3 字符串和數值轉換
  使用InstallShield的內部函數可以很容易地實現字符串和數值之間的轉換。
  1. StrToNum函數
    StrToNum函數是將字符串轉換成數值。函數原型如下:
     StrToNum(nvVar, szString);
  其中,nvVar是轉換後返回的NUMBER數值,而szString用來指定源字符串。當轉換成功後,函數返回0,否則返回負值。
需要說明的是,在將字符串轉換成數值過程中,可能有下列幾種情況:
   (1) 若字符串中的字符都是由"0"~"9"組成的,則將其轉換成相應的數值。
   (2) 若字符串前面的一個或多個字符由"0"~"9"組成,則只轉換其前面的字符。例如"-123ABC456"被轉換成-123
   (3) 若字符串第一個字符不是"0"~"9"或不是正負號,則不能轉換。
   (4) 若字符串第一個字符是正負號,而第二個字符不是"0"~"9",則不能轉換。
  2. NumToStr函數
   與StrToNum相對應的NumToStr函數是將數值轉換成字符串,其函數原型如下:
     NumToStr(svString, nValue);
   其中,svString是轉換後返回的字符串,而nValue用來指定要轉換的NUMBER數值。
    [Ex_NumToStr]將數值轉換成字符串。
     #defineDISK_DRIVE "C:\\"
      STRINGszDrive, svString;
      NUMBERnSpace, nResult;
      program
       szDrive= DISK_DRIVE;
       nSpace= GetDiskSpace(szDrive);
       nResult= NumToStr(svString, nSpace);
       if(nResult < 0) then
        MessageBox("NumToStrfailed.", SEVERE);
        abort;
       endif;
       SprintfBox(INFORMATION,"NumToStr", "Disk Space: %s", svString);
      endprogram
3. Sprintf
函數
  Sprintf函數是將字符、字符串、數值按一定的格式轉換成另外一個字符串,它和MFCCString類中的Format函數極爲相似。Sprintf函數原型如下:
   Sprintf(svResult, szFormat [,arg] [,...]);
  其中,svResult是返回的字符串,szFormat用來指定格式控制的字符串,arg是變量或常量列表,它的個數不能超過10個。函數成功調用後,返回得到的字符串長度。
  需要說明的是,szFormat字符串所包含的格式字符必須用百分號"%"來引導,由於它的格式與Format函數或Cprintf等函數的含義相同,故這裏不再重複。例如:
  Sprintf (svResult , "%s:%d*%d" ,"屏幕分辨率爲", 640, 480);
  則svResult的值爲:屏幕分辨率爲:640*480”

2.6.4 字符串操作函數
  和C語言一樣,InstallShield也提供了許多字符串函數,如表2.3所示。
2.7 結構體類型和指針
  至此,我們所使用的數據類型都是基本的類型,如INTNUMBERSTRING等。但InstallScript還允許用戶按一定的規則進行數據結構體類型的構造。同時提供指針的概念,方便用戶對變量的地址進行操作。
2.7.1 typedef定義一個結構體
   一個結構體是由多種類型的數據組成的整體。組成結構的各個分量稱爲結構體的數據成員(簡稱爲成員)。結構體是InstallScript提供的構造複雜數據類型的唯一手段。
   1. 定義結構體
     結構體定義的格式爲:
      typedef結構體名
      begin
       成員定義1
       成員定義2
       ...
       成員定義n
      end;
   結構體定義是以關鍵字typedef開始的,結構體名應是一個有效合法的標識符。在結構體中的每個成員都必須通過"成員定義"來確定成員名及其類型。例如:
   typedefEMPLOYEE
   begin
    STRINGszName[50]; // 姓名
    STRINGszDepartment[50]; // 部門
    NUMBERnExtension; // 電話分機號碼
   end;
  其中,EMPLOYEE是自己定義的結構體名,該結構有3個成員變量。一旦結構體類型定義後,就可以定義其結構體變量。例如:
   EMPLOYEEstructEmployee;
  使用結構體類型時要注意:
   (1) 不能用賦值運算符將一個結構內容賦予另一個結構,如newstruct= struct1
   (2) 成員變量類型若是STRING,則必須指定其大小;
   (3) 不能在函數體內部定義一個結構體類型;
   (4) 成員變量類型或結構體本身不能使用BYREF關鍵字,數據的傳遞用指針來進行。2. 結構體變量的引用
  當一個結構體變量定義之後,就可引用這個變量。使用時,遵循下列規則:
  (1) 只能引用結構體變量中的成員變量,並使用下列格式:
    結構體變量名.成員變量名
   例如:
    structEmployee.nExtension= 3057;
  "."是成員運算符,它的優先級是最高的,因而可以把structEmployee.nExtension作爲一個整體來看待,它可以像普通變量那樣進行賦值或各種運算。
  (2) 若成員本身又是一個結構體變量,引用時需要多個成員運算符一級一級地找到最低一級的成員。例如:
    typedefPOINT
     begin
     SHORTnX;
     SHORTnY;
    end;
   typedefRECT
    begin
     POINTptUpperLeft;
     POINTptLowerRight;
    end;
   RECT rc;
   則有:
   rc.ptUpperLeft.nX = 0;

2.7.2 指針
   如果在程序中定義了一個變量,在編譯時系統就會給這個變量分配內存單元,並根據程序中定義的變量類型,分配一定長度的內存空間,每個內存單元中存放着變量的值。爲了便於內存單元的存取,系統爲每一個內存單元分配一個地址。這個"地址"稱爲"指針"
  1. 定義和引用指針變量
  指針變量(pointer)是存放內存地址的變量,一般情況下該地址是另一個變量存儲在內存中的首地址,這時又稱該指針變量"指向"這個變量。例如:
  INT i;
  POINTER p;
  ...
  i = 5;
  p = &i;
  其中,i是一個值爲5的整型變量,p是一個指針變量,&i是取變量i在內存中的地址;於是p的數值就等於變量i在內存中的地址值,因此p是一個指向變量i的指針。

  需要注意的是:一旦一個變量定義後,該變量在內存中的地址也就確定下來,不管以後對該變量如何賦值,在程序運行期間,其內存地址總是固定不變的。
  指針變量和所有變量一樣,遵循先定義後使用的原則。InstallScript中定義一個指針變量是在變量名前加上關鍵字POINTER,若是結構體類型,則按下列格式:
   結構體類型名POINTER 指針變量名1[指針變量名2,...];
   例如
    EMPLOYEEPOINTER pPointerName
  這時,結構體成員變量的引用就可以像下列形式:
    pPointerName->nExtension= 3057;
  程序中,"->"稱爲指向運算符,它的左邊必須是一個指針變量,它等效於指針變量所指向的結構體類型變量。
  在定義一個指針後,系統也會給指針分配一個內存單元,但分配的空間大小都是相同的,因爲指針變量的數值是某個變量的地址,而地址值的長度是一樣的。

InstallScript中有兩個專門用於指針的運算符:
   &(取地址運算符)*(取值運算符)
  運算符"&"只能對變量操作,作用是取該變量的地址;運算符"*"用於指針類型的變量操作,作用是取該指針所指內存單元中存儲的內容。例如:
   EMPLOYEEMyStructure;
   POINTERpNum;
   STRINGsvString;
   ...
   pPointerName= &MyStructure; // 將結構體變量地址賦予結構體指針
   pNum =&nvNumber; // NUMBER變量地址賦予指針變量
   SprintfBox( INFORMATION , "", "%d" ,*pNum );
  2. 函數的指針傳遞
  指針也可作爲函數的形參類型,這樣的形參改變後將影響實參的值。
  [Ex_Pointer] 函數的指針傳遞。
   typedefRECT // 定義一個結構體
   begin
    SHORT sX;
    SHORT sY;
   end;
   RECTRectangle; // 定義一個結構體變量
   RECTPOINTER pRect; // 定義一個結構體指針變量
   prototypeSizeRectangle(RECT POINTER); // 將函數的形參定義成指針
   program
    pRect =&Rectangle;
    SizeRectangle(pRect);// 結果RectanglesXsY分別爲105
    . . .
   endprogram
   functionSizeRectangle(pRectangle)
    begin
     pRectangle->sX= 10;
     pRectangle->sY= 5;
    end;

2.8
  衆所周知,在使用數組存放數據前,必須事先定義好數組的長度。而且,相鄰的數組元素的位置和距離都是固定的,也就是說任何一個數組元素的地址都可以用一個簡單的公式計算出來,因此這種結構可以有效地對數組元素進行隨機訪問。但數組元素的插入和刪除會引起大量數據的移動,從而使簡單的數據處理變得非常複雜、低效。爲了能有效地解決這些問題,一種稱爲"鏈表"的結構類型得到了廣泛的應用。
2.8.1 概述
  鏈表是一種動態數據結構,它的特點是用一組任意的存儲單元(可以是連續的,也可以是不連續的)存放數據元素。鏈表中每一個元素稱爲"結點",每一個結點都是由數據域和指針域組成的,每個結點中的指針域指向下一個結點。
  實際上,鏈表中的每個結點可以有若干個數據和若干個指針。結點中只有一個指針的鏈表稱之爲單鏈表,是最簡單的鏈表結構。
  但在InstallScript中,使用的是和C語言相似的單鏈表結構,並將指針域的操作變成InstallScript內部的工作方式,這使得用戶只需通過相應的函數就可以簡單地向鏈表添加字符串或整型數據。這些函數如表2.4所示。
  從表2.4中可以看出,InstallScript的單鏈表結構分爲兩種類型:一類是由字符串組成的鏈表;另一類是由整型數據組成的鏈表。InstallScript規定:在同一個鏈表中,結點的數據域要麼由字符串組成,要麼由整型數據組成,而不能同時存在這兩種數據類型。

2.8.2 建立鏈表
  在InstallScript中,使用ListCreate函數就可以建立一個單鏈表。這一點要比C語言的鏈表操作簡單許多。例如下面的示例。
  [Ex_CreateList]創建鏈表。
    LISTlistID; // 定義一個鏈表變量
    NUMBERnItem, nvItem;
    program
     listID =ListCreate(NUMBERLIST); // 創建鏈表
     // 檢查鏈表是否被成功創建。
     if(listID = LIST_NULL) then
      MessageBox("不能創建鏈表。",SEVERE);
      abort;
     endif;
     // 向鏈表添加數據
     nItem =1078;
     ListAddItem(listID,nItem, AFTER);
     nItem =304;
     ListAddItem(listID,nItem, AFTER);
     // 返回鏈表當前結點的數據,結果爲304
     ListCurrentItem(listID,nvItem);
     SprintfBox(INFORMATION,"ListCreate", "Current item in list: %d", nvItem);
     // 返回鏈表中第一個結點的數據,結果爲1078
     ListGetFirstItem(listID,nvItem);
     SprintfBox(INFORMATION,"ListCreate", "First item in list: %d", nvItem);
     ListDestroy(listID);// 刪除鏈表並將鏈表從內存中釋放出來。
    endprogram
 
 需要注意的是,當鏈表使用完之後,必須調用ListDestroy來刪除鏈表,以釋放被鏈表所佔用的內存空間。

2.8.3 鏈表元素的基本操作
  InstallScript中的ListAddItemListDeleteItemListFindItemListSetCurrentItemListAddStringListDeleteStringListFindStringListSetCurrentString函數分別對鏈表中的字符串或整型數據的結點進行添加、刪除、查找及修改等操作。
  [Ex_AddAndSet]向鏈表添加結點並進行修改。
   #include"Sddialog.h"
    STRINGszTitle, szMsg;
    LISTlistID;
    NUMBERnItem;
    program
     listID =ListCreate(NUMBERLIST);
     if(listID = LIST_NULL) then
       MessageBox("Unableto create list.", SEVERE);
       abort;
     endif;
     // 添加結點數據
     nItem =1078;
     ListAddItem(listID,nItem, AFTER);
     nItem =1304;
     ListAddItem(listID,nItem, AFTER);
     szTitle= "ListSetCurrentItem Example";
     szMsg ="Elements in listID:";
     // 顯示鏈表內容
     SdShowInfoList(szTitle,szMsg, listID);
     nItem =305;
/*--------------------------------------------------------------------------*\
     * 將當前結點的數據由1304更改成305
\*--------------------------------------------------------------------------*/
     if(ListSetCurrentItem(listID, nItem) < 0) then
       MessageBox("ListSetCurrentItemfailed.", SEVERE);
     endif;
     // 顯示修改後的鏈表內容
     SdShowInfoList(szTitle,szMsg, listID);
     // 刪除鏈表
     ListDestroy(listID);
    endprogram
    #defineSD_SINGLE_DIALOGS 1
    #defineSD_SHOWINFOLIST 1
    #include"Sddialog.rul"
  當然,用戶還可通過ListGetFirstItemListGetNextItem函數來遍歷鏈表的結點內容。
  [Ex_SearchAll]遍歷鏈表的結點內容。
    STRINGszTitle, szMsg;
    LISTlistID;
    NUMBERnItem, nvItem, nResult;
    program
    // 創建一個整型數據鏈表並添加一些結點數據
     listID =ListCreate(NUMBERLIST);
     if(listID = LIST_NULL) then
       MessageBox("Unableto create list.", SEVERE);
       abort;
     endif;
    // 添加結點數據
    nItem =1078;
    ListAddItem(listID,nItem, AFTER);
    nItem =304;
    ListAddItem(listID,nItem, AFTER);
    // 以下是鏈表的遍歷過程
    nResult =ListGetFirstItem(listID, nvItem);
    while(nResult != END_OF_LIST)
       szTitle= "ListGetFirstItem & ListGetNextItem";
      // 顯示nvItem.
      SprintfBox(INFORMATION,szTitle, "%i", nvItem);
      nResult= ListGetNextItem(listID, nvItem);
    endwhile;
    // 刪除鏈表
    ListDestroy(listID);
   endprogram

 

2.9 編譯預處理

InstallScript程序的源代碼中可包含各種編譯指令,這些指令稱爲預處理命令。雖然它們實際上不是InstallScript語言的一部分,但卻擴展了InstallScript程序設計的環境。
  InstallScript提供的預處理命令主要有宏定義命令、文件包含命令和條件編譯命令。這些命令在程序中都是以"#"來引導,每一條預處理命令必須單獨佔用一行。由於它不是InstallScript的語句,因此一般在結尾沒有分號""
2.9.1 宏定義
  在以前的程序中,曾用#define定義這樣的一個符號常量:
  #defineTITLE "問候"
  其中,#define是宏定義命令,它的作用是將字符串"問候"TITLE來代替;TITLE稱爲宏名。再如:
  #defineMAX_SIZE 145
  這樣,將數值145用宏MAX_SIZE來代替。
需要注意的是:
  (1) #defineMAX_SIZE145之間一定要有空格,且一般將宏名定義成大寫,以與普通標識符相區別。
  (2) 宏被定義後,一般不能再重新定義,而只有當使用下列命令纔可以:
    #undef 宏名
  (3) 一個定義過的宏名可以用來定義其它新的宏,但要注意其中的括號,例如:
   #defineWIDTH 80
   #defineLENGTH ( WIDTH + 10 )
    宏LENGTH等價於:
   #defineLENGTH ( 80 + 10 )
    但其中的括號不能省略,因爲當
    var =LENGTH * 20;
   若宏LENGTH定義中有括號,則預處理後變成:
    var = (80 + 10 ) * 20;
   若宏LENGTH定義中沒有括號,則預處理後變成:
    var = 80+ 10 * 20;
   顯然,兩者的結果是不一樣的。
  (4)InstallScript的宏定義不能帶參數,僅限於使用簡單的宏定義。

2.9.2 "文件包含"處理
  所謂"文件包含"是指將另一個源文件的內容合併到源程序中。InstallScript語言提供了#include命令用來實現文件包含的操作,它的格式如下:
  #include"文件名"
  例如:
  // 將系統缺省路徑中的文件包含進來
  #include"SUPPORT.RUL"
  // 包含一個含有變量或自定義函數聲明的文件
  #include"DECLARE.RUL"
  // LIBRARY文件夾中包含一個文件
  #include "..\LIBRARY\WINSUB.H"
  #include"..\LIBRARY\SYSCHK.H"
  // DIALOGS文件夾中包含一個文件
  #include"..\DIALOGS\WELCOME\WELCOME.H"
  #include"..\DIALOGS\REGINS\REGINS.H"
  #include"..\DIALOGS\ICONS\ICONS.H"
   "文件包含"命令是很有用的,它可以節省程序設計人員的重複勞動。但在使用#include命令需要注意:
  (1) 一條#include命令只能包含一個文件,若想包含多個文件須用多條文件包含命令。
  (2) 包含的文件所指明的路徑全名(含文件名)不能越過260個字符。
  (3) 當在文件中使用路徑時,應該用單反斜槓"\"代替雙反斜槓"\\"
  (4) 若文件中不指明具體的路徑,則編譯器將在安裝程序項目文件夾中或InstallShieldinclude文件夾中查找。
  (5) 不能將C語言的.H文件包含進來,因爲編譯器不能識別C語言結構。
2.9.3 條件編譯
  一般情況下,腳本源程序中所有的語句都參加編譯,但有時也希望根據一定的條件去編譯腳本源文件的不同部分,這就是"條件編譯"。條件編譯使得同一腳本源程序在不同的編譯條件下得到不同的目標代碼。
InstallScript提供的條件編譯命令有幾種常用的形式,現分別介紹如下
  (1) 第一種形式
   #ifdef 標識符1
    程序段1
   [#ifndef 標識符1
    程序段2]
   #endif
  其中,#ifdef#ifndef#endif都是關鍵字,"程序段"是由若干條預處理命令或語句組成的。這種形式的含義是:如果標識符已被#define命令定義過,則編譯"程序段1",如果標識符未被#define命令定義過,則編譯"程序段2"
   (2) 第二種形式
   #ifdef 標識符
    程序段1
   [#else
    程序段2]
   #endif
  這種形式是第一種形式的替代,它的含義是:如果標識符已被#define命令定義過,則編譯"程序段1",否則編譯"程序段2"

(3) 第三種形式
   #ifndef 標識符
    程序段1
   [#else
    程序段2]
   #endif
  這與前一種形式的區別僅在於,如果標識符沒有被#define命令定義過,則編譯"程序段1",否則就編譯"程序段2"
  (4) 第四種形式
    #if 表達式1
     程序段1
    [#elif 表達式2
     程序段2
     ...]
    [#else
     程序段n]
    #endif
  其中,#if #elif#else#endif是關鍵字。它的含義是,如果"表達式1"""就編譯"程序段1",否則如果"表達式2"""就編譯"程序段2"...,如果各表達式都不爲""就編譯"程序段n"
  使用InstallScript預處理命令需要注意:
  (1) 預處理命令只能放在主程序體外部。
  (2) #ifdef等預處理命令的同一行上不能添加註釋。
  (3) #ifdef等預處理命令中不能使用像ifwhileswitch等流控制語句。
  (4) #ifdef#ifndef 語句中只能測試整型常量。
  (5) 用戶還可以#error預處理命令來定義自己的錯誤信息。例如:
  #definePRODUCTID 1
   #if(PRODUCTID = 1)
    #definePRODUCTNAME "Lite"
   #elif(PRODUCTID = 2)
    #definePRODUCTNAME "Professional"
   #endif
   #ifndefPRODUCTNAME
    #errorPRODUCTID out of range.
   #endif
   若PRODUCTNAME沒有用#define定義過,則用戶定義的錯誤消息"3out of range."被顯示(PRODUCTID3)

2.10 文件及文件夾操作
  InstallShield提供了許多函數用來對文件進行基本操作以及對文件夾進行創建、刪除和查找等操作。
2.10.1 文件基本操作
  文件基本操作包括複製、刪除、查找、重新命名以及獲取或設置文件的屬性等。
(1) 文件的複製
  InstallShield提供的CopyFileXCopyFile函數用來複制文件的,它們的原型如下:
   CopyFile(szSrcFile, szTargetFile);
   XCopyFile(szSrcFile, szTargetFile, nOp);
  這兩個函數都是用來將文件從源文件夾複製到目標文件夾中。雖然,CopyFile函數不像XCopyFile可以複製源文件夾下所有的子文件夾中的文件,但它可以將複製到目標文件夾中的文件重新命名。顯然,XCopyFile函數對成批覆制文件非常有效,並可根據由nOp指定的方式來操作。nOp可以是下列的預定義值
COMP_NORMAL 正常方式,複製時覆蓋相同的文件
COMP_UPDATE_SAME 它和COMP_UPDATE_DATECOMP_UPDATE_VERSION組合
COMP_UPDATE_DATE 只有當源文件的日期和時間比相同的目標文件新時纔會覆蓋
COMP_UPDATE_VERSION 只有當源文件的版本比相同的目標文件新時纔會覆蓋
SELFREGISTER 複製文件時還進行自我註冊操作
SHAREDFILE 把複製的所有文件當作是共享的
LOCKEDFILE 記錄鎖定的.dll.exe文件以便Windows重啓後更新
EXCLUDE_SUBDIR 複製時不含有子文件夾
INCLUDE_SUBDIR 連同子文件夾中的文件一起復制
當然,這兩個函數都有可能返回下列的值:
  0 成功複製
  COPY_ERR_CREATEDIR目標文件夾不能創建
  COPY_ERR_MEMORY不能爲複製文件進程分配必要的內存
  COPY_ERR_NODISKSPACE目標磁盤中沒有足夠的可用空間
  COPY_ERR_OPENINPUT找不到源文件夾
  COPY_ERR_OPENOUTPUT不能複製函數中指定的文件
  COPY_ERR_TARGETREADONLY目標文件夾寫保護
  -51 自我註冊文件沒有註冊成功(只用於XCopyFile函數)
  其他負數 產生未知的錯誤
  需要說明的是,InstallShield使用系統變量SRCDIRTARGETDIR表示源文件夾和目標文件夾的路徑。由於安裝程序中其他函數也會使用這些變量,因此在調用CopyFileXCopyFile函數前,必須先用VarSave函數將SRCDIRTARGETDIR的當前值保存,然後設置相應的路徑,最後用VarRestore函數恢復SRCDIRTARGETDIR的原來值。
  [Ex_CopyFile]文件複製示例。
   #defineSOURCE_DIR "C:\\Windows"
   #defineTARGET_DIR "D:\\Temp"
   NUMBERnResult;
   program
    VarSave(SRCTARGETDIR); // 將缺省的源文件夾和目標文件夾路徑保存
    SRCDIR =SOURCE_DIR; // 設定源文件夾路徑
    TARGETDIR= TARGET_DIR; // 設定目標文件夾路徑
    // 複製文件,它等效於XCopyFile("*.TXT","", COMP_NORMAL)
    nResult =CopyFile("*.TXT", "*.*");
    if (nResult < 0) then
     MessageBox("不能複製文件!",SEVERE);
    else
     MessageBox("文件複製完畢。",INFORMATION);
    endif;
    VarRestore(SRCTARGETDIR); // 恢復缺省的源文件夾和目標文件夾路徑
   endprogram

(2) 文件的刪除與重新命名
  InstallShield提供的DeleteFileRenameFile函數分別用來文件的刪除與重新命名,它們的原型如下:
   DeleteFile(szFile);
   RenameFile(szFileOld, szFileNew);
  需要說明的是:DeleteFile函數不能刪除系統文件、只讀文件、隱含文件以及網絡上沒有刪除權限的文件。並且該函數使用TARGETDIR作爲其工作路徑。而RenameFile函數是將由SRCDIR指定的源文件夾下的文件重新命名並移至由TARGETDIR指定的目標文件夾中。
  [Ex_Rename] 文件重新命名示例。
   #defineTARGET_DIR "D:\\Temp"
   program
    VarSave(SRCTARGETDIR); // 將缺省的源文件夾和目標文件夾路徑保存
    SRCDIR =TARGET_DIR; // 設定源文件夾路徑
    TARGETDIR= TARGET_DIR; // 設定目標文件夾路徑
    // My.TXT文件重新命名爲MyNew.TXT
    if (RenameFile("My.TXT", "MyNew.TXT") < 0 ) then
     MessageBox("文件不能重新命名!",SEVERE);
    else
     MessageBox("文件已重新命名。",INFORMATION);
    endif;
    VarRestore(SRCTARGETDIR); // 恢復缺省的源文件夾和目標文件夾路徑
  (3) 文件的查找
  InstallShield提供兩個函數用來查找文件,它們是:
   FindFile(szPath, szFileName, svResult);
   FindAllFiles(szDir, szFileName, svResult, nOp);
  其中,szDirszPath用來指定要查找的路徑,szFileName表示要查找的文件名,它可以使用通配符,svResult用來返回查找到的第一個文件。對於FindAllFiles函數來說,還可使用nOp指定查找的方式,當nOpCONTINUE時表示從上一次查找停止的位置處開始查找,當nOpRESET時表示在szDir中從頭開始查找。
  [Ex_FindFiles]查找C:\Windows下的所有.INI文件。
   #defineTARGET_DIR "C:\\Windows"
   NUMBERnResult;
   STRINGsvFileName;
   program
    nResult =FindAllFiles(TARGET_DIR, "*.ini", svFileName, RESET);
    while(nResult = 0)
     MessageBox(svFileName,INFORMATION);
     nResult= FindAllFiles(TARGET_DIR, "*.ini", svFileName, CONTINUE);
    endwhile;
    endprogram
   endprogram

(4) 文件屬性的獲取和設置
  InstallShield提供的SetFileInfoGetFileInfo函數分別用來設置與獲取文件的屬性。SetFileInfo函數的原型如下:
  SetFileInfo(szPathFile, nType, nAttribute, szValue);
  該函數是用來設置由szPathFile指定文件的由szValue指定的時間、日期或用來更改文件的屬性。想要文件的時間和日期同時被改變,用戶必須兩次調用該函數,但若改變文件的其他多個屬性,則只要在nAttribute中使用"|"組合進行一次調用就可實現。其中nType用來指定要更改的文件特徵,它可以是下列值之一:
  FILE_ATTRIBUTE表示一個或多個屬性將被更改
  FILE_DATE 表示文件的日期將被更改
  FILE_TIME 表示文件的時間將被更改
  若當nTypeFILE_ATTRIBUTE時指定的文件屬性(nType爲其它值時,該參數爲0),則szValue"",而nAttribute可以是下列值之一或"|"組合:
  FILE_ATTR_ARCHIVED設置"存檔"屬性
  FILE_ATTR_HIDDEN設置"隱含"屬性
  FILE_ATTR_READONLY設置"只讀"屬性
  FILE_ATTR_SYSTEM設置"系統"屬性
  FILE_ATTR_NORMAL當此值單獨指定時,清除所有的文件屬性,
  另一個函數GetFileInfo是用來獲取一個已存在文件szPathName的屬性、時間、日期和大小。它的原型如下:
  GetFileInfo(szPathName, nType, nvResult, svResult);
其中,nType用來指定獲取文件屬性的特徵,它可以是下列值之一:
  FILE_ATTRIBUTE獲取文件屬性,結果在nvResult
  FILE_DATE 獲取文件日期,格式爲YYYY\MM\DD,結果在svResult
  FILE_SIZE 獲取文件大小,結果在nvResult
  FILE_TIME 獲取文件時間,格式爲HH:MM:SS,結果在svResult
  需要說明的是,用GetFileInfo獲取文件屬性時最好先將nType指定爲FILE_ATTRIBUTE,若返回的nvResult值等於FILE_ATTR_NORMAL,則沒有任何屬性;但若返回的nvResult不等於FILE_ATTR_NORMAL,則需要與系統預定義的文件屬性值進行""(&)操作以確定文件的具體屬性,例如下面的代碼片斷:
  if(nvResult = FILE_ATTR_NORMAL) then
  // 正常文件
  else
  if (FILE_ATTR_HIDDEN& nvResult) then
   // 該文件有"隱含"屬性
  endif;
  if(FILE_ATTR_READONLY & nvResult) then
   // 該文件有"只讀"屬性
  endif;
 endif;
 其中,返回的nvResult值可能是:
   FILE_ATTR_ARCHIVED存檔文件
   FILE_ATTR_DIRECTORY目錄文件
   FILE_ATTR_HIDDEN隱含文件
   FILE_ATTR_READONLY只讀文件
   FILE_ATTR_SYSTEM系統文件
   FILE_ATTR_NORMAL正常文件
   endprogram

2.10.2 文件夾與路徑操作
  用於文件夾與路徑操作的InstallShield函數有很多,表2.5列出所有的相關函數。
  需要說明的是:
  1. 關於DeleteDir函數
  DeleteDir函數能夠刪除由szDir指定的文件夾或文件夾中的所有內容,具體的刪除操作還取決於nFlag的值。若nFlagALLCONTENTS時,則刪除該文件夾所有的內容;若nFlagONLYDIR時,則僅當文件夾中的文件爲空時纔會刪除該文件夾。需要注意的是:
  (1)DeleteDir函數不會將刪除的文件夾放入Windows系統的"回收站",因而一旦文件夾刪除後不能再恢復。
  (2) 該函數不能刪除當前文件夾以及網絡系統中沒有刪除權限的文件。
  (3) 當指定的文件夾下有"只讀""隱含""系統"文件時,該函數不會刪除該文件夾。
  (4) szDir指定的文件夾路徑中的"\"應是"\\",例如:
    DeleteDir("D:\\Temp",ALLCONTENTS);
2.
關於FindAllDirs
  FindAllDirs函數用來查找szDir指定的文件夾或該文件夾下的所有子文件夾,並將查找的結果保存在字符串鏈表listDirs中。函數中的nOp用來指定是(INCLUDE_SUBDIR)(EXCLUDE_SUBDIR)查找所有子文件夾。
  [Ex_FindFolders]查找C盤下的所有子文件夾。
   LISTlistDirs;
   program
    listDirs= ListCreate (STRINGLIST);
    FindAllDirs("C:\\", INCLUDE_SUBDIR, listDirs);
    ListDestroy( listDirs );
   endprogram
  需要說明的是,FindAllDirs函數支持Windows95/98的長文件夾名。
3. 關於GetValidDrivesList
  GetValidDrivesList函數用來獲取系統中有效的驅動器列表,返回的結果取決於下面的nDriveType值:
   FIXED_DRIVE查找硬盤驅動器
   REMOTE_DRIVE查找網絡驅動器
   REMOVEABLE_DRIVE查找軟盤驅動器
   CDROM_DRIVE查找CD-ROM
例如,下面的代碼將查找所有的本地驅動器,並將結果存放在字符串列表中:
   listDirs =ListCreate (STRINGLIST);
   GetValidDrivesList( listDirs , REMOVEABLE_DRIVE , -1 );
   GetValidDrivesList( listDirs , FIXED_DRIVE , 10000 );
   GetValidDrivesList( listDirs , CDROM_DRIVE , -1 );
   ListDestroy( listDirs );
  代碼中,GetValidDrivesList函數最後一個參數用來指定要查找的驅動器所含有的最少的磁盤空間,若此參數小於0,則查找時不會檢測磁盤空間,從而大大加快查找速度,這對查找軟驅和CD-ROM特別有用。

2.11 常用對話框操作()
  作爲與用戶交互的重要手段,對話框是InstallShield安裝程序中最重要也是最主要的用戶界面。在安程序程序運行過程中,對話框通過各種控件(如按鈕、編輯框、列表框、組合框等)來捕捉用戶的輸入信息或數據。
  基於對話框的這種重要性,InstallShield爲用戶提供了大量的對話框函數,這其中包括內建對話框和Sd對話框函數。當然,用戶也可以在安裝程序代碼中定義自己的對話框(4.4節中,將討論如何定製對話框)
2.11.1 內建對話框和Sd對話框
  InstallShield在系統內部定義了一些對話框,這些對話框稱之爲內建對話框,其相應的對話框函數可以在所有版本中使用。但爲了提高對話框的功能和應用能力,作爲補充和拓展,InstallShield還提供了一些高效、適應能力強的腳本對話框(ScriptDialog),由於它們是用InstallScript腳本語言代碼來寫的,因此又稱之爲Sd對話框。
  1. 內建對話框
  InstallShield的內建對話框函數中,除了AskYesNoMessageBoxSprintfBox等少量對話框是調用WindowsAPI函數外,其餘的均是InstallShield自己構造的。表2.6列出了InstallShield全部的內建對話框函數。
  例如下面的代碼,其結果如圖2.1所示。
  program
   Welcome ("歡迎", 0 );
  endprogram

2. Sd對話框
  InstallSheld提供了大量的腳本對話框供用戶使用,腳本對話框是按InstallScript特定的機制來創建的,並提供對話框的腳本語言代碼供用戶揣摩。爲了與內建對話框函數相區別,腳本對話框的函數名都是以Sd爲開頭。表2.7列出了InstallShield全部的Sd對話框函數,
  但使用腳本對話框時一定要注意:
  (1) 與內建對話框函數相比,調用Sd對話框時還需要在安裝腳本程序中添加另外兩條語句,如下所示:
  #include"sddialog.h" // 添加Sd對話框的頭文件
  program
   SdWelcome( "歡迎", "" ); // 調用Sd對話框
  endprogram
  #include"sddialog.rul" // 添加Sd對話框的腳本文件
其結果如圖2.2所示。
(2) 若調用Sd對話框函數時,其字符串實參爲NULL字符串("")時,Sd對話框將該參數使用缺省的字符串文本。這一點,也適用於InstallShield的內建對話框函數。
(3) 用戶可以通過調用DialogSetInfo改變某些對話框中的控件。
(4) Sd對話框函數中,用戶可以將%P插入到字符串中,用於放置產品名稱。缺省時,產品名稱%P表示空字符串,但用戶可以使用SdProductName函數來設定其具體的值。
(5) 爲了提高系統對Setup.rul的編譯速度以及減小編譯文件Setup.ins的字節數,在需要包含Sd對話框腳本文件前,先對SD_SINGLE_DIALOGS進行宏定義,然後再對具體的對話框標識(參見表2.7)進行定義。例如若調用SdShowInfoListSdLicense對話框時,則可有如下定義:
  ...
  endprogram
  #defineSD_SINGLE_DIALOGS 1
  #defineSD_SHOWINFOLIST 1
  #defineSD_LICENSE 1
  #include"Sddialog.rul"
(6)
在所有InstallShield對話框中,若對話框含有[Next][Back]以及[Cancel]按鈕。則單擊[Next][Back]按鈕,對話框函數相應地返回NEXTBACK之值,而單擊[Cancel]按鈕,則將彈出"ExitSetup"對話框,用於確認是否真的退出安裝。
2.11.2 信息顯示對話框
  對話框函數MessageBoxSprintfBoxSdLicenseSdShowInfoListSdShowMsgSdStartCopy分別用來顯示用戶資料、安裝進程、錯誤代碼等信息。
  1. MessageBoxSprintfBox
  MessageBoxSprintfBox都是調用WindowsAPI函數的消息對話框,且SprintfBox還能使用如Sprintf函數的格式控制符。它們的原型如下:
  MessageBox(szMsg, nType);
  SprintfBox(nType, szTitle, szFormat [,arg] [,...]);
  其中,nType用來指定在對話框中顯示的圖標類型,其預定義值的含義如圖2.3所示。
  參數szMsg指定要顯示的消息內容,如果消息太長,則可在消息文本中添加"\n"轉義符來使其換行,szTitle指定對話框的標題。
  [Ex_MessageBox]使用MessageBox
  STRINGszTitle;
  STRINGszMyComp;
  NUMBERnvDx,nvDy;
  program
  szTitle ="我的電腦";
  SetDialogTitle(DLG_MSG_INFORMATION, szTitle); // 設置對話框的標題爲"我的電腦"
  GetExtents(nvDx, nvDy );
  Sprintf(szMyComp,"%s:%dx %d","屏幕分辨率爲",nvDx,nvDy);
   MessageBox(szMyComp,INFORMATION );
  endprogram
  [Ex_SprintfBox]使用SprintfBox
  NUMBERnvDx,nvDy;
  program
   GetExtents(nvDx, nvDy );
   SprintfBox( INFORMATION , "我的電腦", "%s:%d x %d" , "屏幕分辨率爲",nvDx,nvDy);
  endprogram

這兩個示例的結果都是一樣的,如圖2.4所示。顯然,SprintfBox要比MessageBox功能強大得多。
  需要說明的是:缺省時,這兩個對話框函數顯示的對話框中總有一個[確定]按鈕。爲了能發揮WindowsAPIMessageBox的功能,用戶可以在上例的基礎上作如一些修改。
  [Ex_SprintfBoxEx]擴展使用SprintfBoxMessageBox
 #defineMB_OK 0x0000 // 對話框中含有[確定]按鈕
 #defineMB_OKCANCEL 0x0001 // 對話框中含有[確定][取消]按鈕
 #defineMB_ABORTRETRYIGNORE 0x0002 // 對話框中含有[終止][重試][忽略]按鈕
 #defineMB_YESNOCANCEL 0x0003 // 對話框中含有[][][取消]按鈕
 #defineMB_YESNO 0x0004 // 對話框中含有[][]按鈕
 #defineMB_RETRYCANCEL 0x0005 // 對話框中含有[重試][取消]按鈕
 #defineMB_ICONHAND 0x0010 // 對話框中含有X形圖標
 #defineMB_ICONQUESTION 0x0020 // 對話框中含有?形圖標
 #defineMB_ICONEXCLAMATION 0x0030 // 對話框中含有!形圖標
 #defineMB_ICONASTERISK 0x0040 // 對話框中含有i形圖標
 NUMBERnvDx,nvDy;
 program
   GetExtents(nvDx, nvDy );
      SprintfBox (MB_YESNO|MB_ICONQUESTION ,"
我的電腦" ,"%s:%d x %d" , "屏幕分辨率爲",nvDx,nvDy);
 endprogram
  結果在對話框中顯示出[][]按鈕,且圖標變成了帶問號的圖標,如圖2.5所示,其中MB_OK等符號常量的值必須按上述代碼中的值進行定義。
  同樣,若將MessageBox函數中的nType設置成MB_YESNO|MB_ICONQUESTION,也會有類似的結果。
2. SdShowInfoListSdStartCopy函數
  SdShowInfoListSdStartCopy是用來顯示多條信息的對話框。它們的原型如下:
  SdShowInfoList(szTitle, szMsg, listID);
  SdStartCopy(szTitle, szMsg, listData);

其中,szTitle爲對話框的標題,szMsg爲對話框中最前面顯示的消息。ListIDListDataLIST數據類型的鏈表變量。
  [Ex_ShowInfo]使用SdShowInfoListSdStartCopy
  #include"Sddialog.h"
  STRINGszTitle, szMsg;
  STRINGsvReturn, szInfo;
  NUMBERnvReturn;
  LISTlistInfo;
  program
   listInfo =ListCreate(STRINGLIST); // 創建鏈表
   // 檢測系統是否有CD-ROM
   GetSystemInfo(CDROM, nvReturn, svReturn);
   if(nvReturn = TRUE) then
    szInfo ="您的計算機中有一個CD-ROM 驅動器。";
   else
    szInfo ="您的計算機中沒有CD-ROM 驅動器。";
   endif;
   // 將上述信息插入鏈表中
   ListAddString(listInfo,szInfo, AFTER);
   // 檢測當前系統時間
   GetSystemInfo(TIME, nvReturn, svReturn);
   Sprintf(szInfo,"現在的時間是:%s.", svReturn);
   // 將上述結果插入鏈表中
   ListAddString(listInfo,szInfo, AFTER);
   // 檢測系統基本內存大小
   GetSystemInfo(BASEMEMORY, nvReturn, svReturn);
   Sprintf(szInfo,"您的計算機的基本內存大小爲:%ldk ", nvReturn);
   ListAddString(listInfo,szInfo, AFTER);
   szTitle ="SdShowInfoList示例";
   szMsg ="下面的信息和您的計算機有關:";
   SdShowInfoList(szTitle, szMsg, listInfo);
   szTitle ="SdStartCopy示例";
   SdStartCopy(szTitle,szMsg, listInfo);
  endprogram
  #defineSD_SINGLE_DIALOGS 1
  #defineSD_SHOWINFOLIST 1
  #defineSD_STARTCOPY 1
  #include"Sddialog.rul"
結果如圖2.62.7所示。


3 基本安裝程序的建立
2001-01-23· adding·yesky

上一章介紹了InstallScript腳本語言的一些基礎內容,其目的是幫助用戶能夠編制出具有較高水平的安裝程序。當然,建立一個安裝程序無需用戶從頭開始,因爲installshildProject Wizard能快速有效地生成安裝項目所需的程序框架。需要用戶所做的,就是在該框架的基礎上添加或修改一些內容以完善安裝程序功能。
3.1 創建安裝項目
  在installshild 5.5中,Project Wizard用來製作一般應用程序的安裝項目,Visual Basic Project Wizard則還專門爲VB6.0應用程序進行定製。本節着重討論Project Wizard的使用方法。
3.1.1 使用Project Wizard
  運行installshild 5.5後,雙擊Project窗口中的ProjectWizard就可開始安裝項目嚮導。
  (1) 首先,出現如圖3.1所示的"Welcome"對話框,要求用戶輸入應用程序名稱、公司名稱、選擇應用程序所使用的開發環境、應用程序的類型、版本號以及應用程序的可執行文件名,單擊Browse按鈕("..."符號的按鈕)可將磁盤中已有的應用程序的可執行文件名調入。單擊[幫助]按鈕,彈出該對話框的幫助說明。

  當然,用戶不一定現在就在"Welcome"對話框中輸入相應的內容,因爲在installshild的項目工作區窗口中也可以進行上述內容的修改和添加。

(2) 保留缺省值,單擊[下一步]按鈕,出現如圖3.2所示的"Choose Dialogs"對話框。該對話框用來讓用戶從列表中選定安裝過程中所出現的對話框。在對話框列表項前面的方框中有鉤號()的表示被選中,單擊小方框可以在選中和未選中之間進行切換。每次選定對話框列表項時,"Choose Dialogs"對話框的左下角將會顯示相應的對話框模型,單擊[Preview]按鈕還可按正常比例顯示該模型。

  (3) 保留缺省值,單擊[下一步]按鈕,出現如圖3.3所示的"Choose Target Platforms"對話框。該對話框用來讓用戶選擇一個或多個操作系統類型,以決定可以在哪些操作系統中進行安裝。

  (4) 保留缺省值,單擊[下一步]按鈕,出現如圖3.4所示的"Specify Languages"對話框。該對話框用來讓用戶選擇一個或多個語系,以決定創建的安裝項目可以支持哪些語系。需要說明的是,installshild 5.5英文專業版只支持English語系的安裝項目。

(5) 保留缺省值,單擊[下一步]按鈕,出現如圖3.5所示的"Specify Setup Types"對話框。該對話框用來讓用戶選擇一個或多個應用程序的安裝類型,以決定程序安裝時供用戶選擇的安裝類型。

需要說明的是,多個安裝類型列表項的選定操作是通過鼠標來進行的。當用戶按住Shift鍵不放,再單擊鼠標可選定多個連續的列表項,若單擊鼠標前按住的鍵是Ctrl,則可選定多個不連續的列表項。這種操作方式在Windows應用程序中幾乎是一致的。
  (6) 保留缺省值,單擊[下一步]按鈕,出現如圖3.6所示的"Specify Components"對話框。該對話框用來讓用戶添加或刪除安裝項目中的組件,單擊[Add]按鈕將在Components列表中添加一個組件,單擊[Delete]刪除選定的組件。

(7) 保留缺省值,單擊[下一步]按鈕,出現如圖3.7所示的"Specify File Groups"對話框。該對話框用來讓用戶添加或刪除安裝項目中的文件組,單擊[Add]按鈕將在File Groups列表框中添加一個文件組,單擊[Delete]刪除選定的文件組。

 (8) 保留缺省值,單擊[下一步]按鈕,出現如圖3.8所示的"Summary"對話框。該對話框顯示用戶嚮導中選定的信息摘要,若用戶對其中某項設置不滿意,可單擊[上一步]按鈕進行重新選擇。任何時候,單擊[取消]按鈕都會終止創建安裝項目。

 (9)單擊[完成]按鈕,installshield開始創建。稍等片刻,一個名爲"Your Application Name"的安裝項目的框架就創建好了,同時該項目的所有內容都被放在缺省的C:\My Installations\ Your Application Name文件夾中。當然,用戶不必當心創建的安裝項目名是否和以前創建的安裝項目名重名,因爲installshild會自動在重名的項目名後面依次加上"-1""-2"...

3.1.2修改安裝項目屬性
  想要改變上述安裝項目的缺省屬性,可選擇"Project"菜單下的"Settings..."命令。彈出相應的"Project Settings"對話框,如圖3.9所示。

對話框中含有"General"(一般信息)"Information"(項目信息)"Owner"(製造商信息)"Instructions"(說明)"Language"(語系)"Operating Systems"(操作系統)等頁面。
  其中,"Information"頁面可供用戶進行開發環境、應用程序的類型以及應用程序適用的行業的重新選擇。而"Owner"頁面用來指定產品名稱、作者、開發商、Email地址、主頁、公司名稱、版權說明以及版本等信息。
  這裏,我們着重討論"Language""Operating Systems"兩個頁面。任何時候,選擇這兩個標籤總可以進行語系和操作平臺的添加。
 添加語系的操作步驟如下:
 (1) "Project Settings"對話框切換到"Language"頁面(參見圖3.9)
 (2) 單擊[Add...]按鈕可向語系列表中增加新的語系,如圖3.10所示;
 (3) 取消"Show only available languages"選中標記,則語系列表中顯示出所有的語系;
 (4) 選定"Chinese (PRC)",單擊[OK]按鈕,將給此安裝項目添加一個新的語系"Chinese (PRC)"
 添加操作平臺的操作步驟如下:
 (1) "Project Settings"對話框切換到"Operating Systems"頁面;
 (2) 單擊[Add...]按鈕可向操作系統列表中增加新的操作系統,如圖3.11所示;
 (3) 取消"Show only available platforms"選中標記,操作系統列表中顯示出其他一些操作系統;
 (4) 選定某操作系統列表項,單擊[OK]按鈕,將給此安裝項目添加一個新的操作平臺。3.2理解程序框架
  前面創建的安裝腳本程序Setup.rul是用來進行安裝初始化、顯示安裝對話框、安裝所有的數據以及創建程序菜單項和桌面圖標等操作的。
3.2.1 安裝初始化
  下面先來看看安裝腳本程序Setup.rul的部分源代碼:
  #include"sdlang.h" // 預定義語言標識的頭文件
  #include"sddialog.h" // Sd對話框的頭文件
  #defineUNINST_LOGFILE_NAME "Uninst.isu"
  // 以下是安裝過程所需要的自定義函數聲明
  prototypeShowDialogs();
  prototype...
  ...
  // 以下是全局變量的定義
  BOOLbWinNT, bIsShellExplorer, bInstallAborted, bIs32BitSetup;
  STRINGsvDir;
  STRINGsvName, svCompany, svSerial;
  STRINGsvDefGroup;
  STRINGszAppPath;
  STRINGsvSetupType;

  program
   Disable(BACKGROUND ); // 安裝界面不使用背景色
   // 安裝初始化
   CheckRequirements();
   SetupInstall();
   SetupScreen();
   // 顯示安裝過程的對話框
   if(ShowDialogs()<0) goto end_install;
    // 安裝所有的文件
   if(ProcessBeforeDataMove()<0) goto end_install;
   if(MoveFileData()<0) goto end_install;
   if(ProcessAfterDataMove()<0) goto end_install;
     // 安裝註冊
   if(SetupRegistry()<0) goto end_install;
     // 安裝程序菜單項和桌面圖標
   if(SetupFolders()<0) goto end_install;
     end_install:
     CleanUpInstall();
     // 若一個不可恢復的錯誤產生後,則清除已安裝的部分內容
   if(bInstallAborted) then
     abort;
   endif;
  endprogram
  // 以下是自定義函數的代碼
  //

  #include"sddialog.rul" // Sd對話框所必須的包含文件
  從上述代碼過程可以看出,在程序框架中進行安裝初始化的代碼語句爲:
  CheckRequirements();
  SetupInstall();
  SetupScreen();

它們都是自定義函數的調用,其相應的函數代碼如下:

  functionCheckRequirements() // 檢測安裝所需要的環境
   NUMBERnvDx, nvDy, nvResult;
   STRINGsvResult;

   begin
    bIsShellExplorer= FALSE;
    bIsWindowsNT4= FALSE;
    bIsWindowsNT351= FALSE;
    bIsWindows95= FALSE;
    bIsWindows98= FALSE;
    // 測量屏幕分辨率,最小要求爲640x 480
    GetExtents(nvDx, nvDy );
    if (nvDy< 480) then
     MessageBox(@ERROR_VGARESOLUTION, WARNING );
     abort;
    endif;
    // 設置"安裝"操作模式
    bIs32BitSetup= TRUE;
    GetSystemInfo(ISTYPE, nvResult, svResult ); // 獲得操作系統的類型信息
    if(nvResult = 16) then
     bIs32BitSetup= FALSE;// 16位安裝程序
     return0;
    endif;
//
檢測目標操作系統
    GetSystemInfo(OS, nvResult, svResult );
   if(nvResult = IS_WINDOWSNT) then
     // 判定操作系統是WindowsNT 4.0還是WindowsNT 3.51,
    if(GetSystemInfo( WINMAJOR, nvResult, svResult ) = 0) then
     if(nvResult >= 4) then
      bIsShellExplorer= TRUE;
      bIsWindowsNT4= TRUE;
     else
      bIsWindowsNT351= TRUE;
     endif;
    endif;
   elseif(nvResult = IS_WINDOWS9X) then
    bIsShellExplorer= TRUE;
    // 判定操作系統是Windows95還是Windows 98
    GetSystemInfo(WINMINOR, nvResult, svResult);
    if(nvResult < 10) then
     bIsWindows95= TRUE;
    else
     bIsWindows98= TRUE;
    endif;
   endif;
  end;
  functionSetupInstall() // 設置安裝時所需要的參數
   begin
    Enable(CORECOMPONENTHANDLING ); // 可以處理核心組件
    bInstallAborted= FALSE;
    if(bIs32BitSetup) then // 若是32位安裝,將使用長路徑名
     svDir =PROGRAMFILES ^ @COMPANY_NAME ^ @PRODUCT_NAME;
    else
     svDir =PROGRAMFILES ^ @COMPANY_NAME16 ^ @PRODUCT_NAME16;
    endif;
    TARGETDIR= svDir; // 設置目標文件路徑
    SdProductName(@PRODUCT_NAME ); // 設置Sd對話框中產品的名稱
    Enable(DIALOGCACHE ); // 將對話框使用高速緩衝機制,避免閃爍
    return 0;
   end;
  functionSetupScreen() // 設置安裝主界面的外觀
   begin
    Enable(FULLWINDOWMODE ); // 安裝主界面全屏顯示
    SetTitle(@TITLE_MAIN, 24, WHITE ); // 主標題的顏色爲白色、字體大小爲24
    SetTitle(@TITLE_CAPTIONBAR, 0, BACKGROUNDCAPTION ); // 設置標題欄的標題
    Enable(BACKGROUND ); // 可以使用背景顏色
    Delay( 1); // 延時1
   end;
  代碼中,凡是在標識符前有"@"符號的表示取安裝項目中字符串資源中的相關內容。例如@TITLE_MAIN表示取字符串資源中的TITLE_MAIN字段內容。

3.2.2 顯示安裝過程對話框
  顯示安裝過程對話框的函數是自定義函數ShowDialogs,其代碼如下:
  functionShowDialogs()
   NUMBERnResult;
   begin
    Dlg_Start:// 開始的語句標號
    Dlg_SdWelcome:
    nResult =DialogShowSdWelcome(); // 顯示歡迎對話框
    if(nResult = BACK) goto Dlg_Start; // [Back]按鈕,轉到一開始
     Dlg_SdLicense:
     nResult= DialogShowSdLicense(); // 顯示許可協議對話框
    if(nResult = BACK) goto Dlg_SdWelcome; // [Back]按鈕,轉到標號Dlg_SdWelcome
    Dlg_SdRegisterUserEx:
    nResult =DialogShowSdRegisterUserEx(); // 顯示用戶註冊信息對話框
    if(nResult = BACK) goto Dlg_SdLicense;
    Dlg_SdAskDestPath:
    nResult =DialogShowSdAskDestPath(); // 顯示選擇目的位置對話框
    if(nResult = BACK) goto Dlg_SdRegisterUserEx;
    Dlg_SdSetupType:
    nResult =DialogShowSdSetupType(); // 顯示安裝類型對話框
    if(nResult = BACK) goto Dlg_SdAskDestPath;
    Dlg_SdComponentDialog2:
    if((nResult = BACK) && (svSetupType != "Custom") &&(svSetupType != "")) then
     gotoDlg_SdSetupType;
    endif;
    nResult =DialogShowSdComponentDialog2();// 顯示選擇組件對話框
    if(nResult = BACK) goto Dlg_SdSetupType;
    Dlg_SdSelectFolder:
    nResult =DialogShowSdSelectFolder(); // 顯示選擇程序菜單項對話框
    if(nResult = BACK) goto Dlg_SdComponentDialog2;
    return 0;
   end;

  當然,上述的DialogShowSdSelectFolder等顯示對話框的函數也都是自定義函數,用戶可自行查看其代碼。

3.2.3 安裝文件數據
  安裝文件數據是安裝程序所完成的最主要的任務,它不僅將應用程序所需要的全部文件正確地安裝到用戶指定的目的文件夾中,而且還要爲以後反安裝時註冊一些內容。Setup.rul程序中的自定義函數
ProcessBeforeDataMove
MoveFileDataProcessAfterDataMove就是實現上述過程,其代碼如下:
  functionProcessBeforeDataMove() // 在真正數據安裝前所做的必須準備
   STRINGsvLogFile;
   NUMBERnResult;
  begin
  InstallationInfo(@COMPANY_NAME, @PRODUCT_NAME, @PRODUCT_VERSION,

@PRODUCT_KEY );
  // 註冊安裝信息
  svLogFile =UNINST_LOGFILE_NAME; // 反安裝註冊文件
  nResult =DeinstallStart( svDir, svLogFile, @UNINST_KEY, 0 );// 開始註冊反安裝信息
  if (nResult< 0) then
    MessageBox(@ERROR_UNINSTSETUP, WARNING );
  endif;
  szAppPath =TARGETDIR; // 用戶指定的安裝路徑保存在TARGETDIR變量中
  if((bIs32BitSetup) && (bIsShellExplorer)) then // 32位安裝時
  // 以下是反安裝信息的註冊
   RegDBSetItem( REGDB_APPPATH, szAppPath );
   RegDBSetItem(REGDB_APPPATH_DEFAULT, szAppPath ^ @PRODUCT_KEY );
   RegDBSetItem( REGDB_UNINSTALL_NAME, @UNINST_DISPLAY_NAME );
  endif;
  return 0;
  end;
  functionMoveFileData() // 安裝數據
   NUMBERnResult, nDisk;
   begin
    nDisk =1;
    SetStatusWindow(0, "" ); // 設置安裝時的狀態窗口參數
    Disable(DIALOGCACHE );
    Enable(STATUS ); // 顯示進展指示器
    StatusUpdate(ON, 100 ); // 跟蹤顯示安裝的進程
    nResult =ComponentMoveData( MEDIA, nDisk, 0 ); // 安裝數據
    HandleMoveDataError(nResult ); // 處理安裝時產生的錯誤
    Disable(STATUS ); // 關閉進展指示器
    returnnResult;
  end;
  functionProcessAfterDataMove()
   STRINGszReferenceFile, szMsg;
   begin
    // 在反安裝之前,DeinstallSetReference用來檢測相應的文件,
    // 若該文件正在使用,反安裝將不會進行。
    szReferenceFile= svDir ^ @PRODUCT_KEY;
    DeinstallSetReference(szReferenceFile );
    return 0;
   end;

3.2.4 創建程序菜單項和桌面圖標
  Setup.rul代碼中的自定義函數SetupFolders負責創建程序菜單項和桌面圖標,其代碼爲:
  functionSetupFolders()
  NUMBERnResult;
  begin
   nResult =CreateShellObjects( "" ); // 創建Shell對象
   returnnResult;
  end;
  由於安裝項目Resources頁面的"ShellObjects"目錄項中還沒有程序菜單項和圖標相關的內容,因而上述代碼實際上並沒有真的起作用。想要創建程序菜單項和圖標,除了在ShellObjects目錄項中添加相關內容外,還可使用CreateProgramFolderAddFolderIcon等函數。如下面的代碼過程:
  functionSetupFolders()
   NUMBERnResult;
   STRINGsvPath;
   begin
    svPath =TARGETDIR ^ "MySDI.exe"; // 應用程序的路徑全名
    LongPathToQuote( svPath , TRUE ); // 使用長路徑名
    // 創建程序菜單
    AddFolderIcon( "" , "我的單文檔應用程序" , svPath , "" ,"" , 0 , "" , REPLACE );
    // 創建桌面圖標
    AddFolderIcon( FOLDER_DESKTOP , "我的單文檔應用程序" , svPath ,
            "", "" , 0 , "" , REPLACE );
    file://nResult= CreateShellObjects( "" ); // 創建Shell對象
    return 0;file://nResult;
   end;
  同樣,若只在"開始"->"程序"中創建一個程序文件夾,內有"我的單文檔應用程序""ReadMe"程序項,則有下列代碼:
  function SetupFolders()
   NUMBERnResult;
   STRINGsvPath;
   begin
     //創建程序文件夾
    CreateProgramFolder( SHELL_OBJECT_FOLDER );
    svPath =TARGETDIR ^ "MySDI.exe"; // 應用程序的路徑全名
    LongPathToQuote( svPath , TRUE ); // 使用長路徑名
    // 在程序文件夾下創建程序菜單項
    AddFolderIcon( SHELL_OBJECT_FOLDER , "我的單文檔應用程序" , svPath ,
            "", "" , 0 , "" , REPLACE );
    svPath ="C:\\Windows\\NotePad.exe" + TARGETDIR ^ "ReadMe.txt";
    LongPathToQuote( svPath , TRUE ); // 將路徑名兩邊插入引號
    AddFolderIcon( SHELL_OBJECT_FOLDER , "ReadMe" , svPath ,
           "", "" , 0 , "" , REPLACE );
    file://nResult= CreateShellObjects( "" );
    return 0;file://nResult;
   end;

3.3 添加和修改
   上述程序框架在編譯運行後,不會有任何文件數據被複制,因爲該安裝項目還只是一個空的框架。所以,它還需要用戶通過InstallShield的開發環境對相關文件、文件組、組件、程序菜單項和圖標進行添加或修改等操作,並且還需在理解程序框架的基礎上,添加一定的代碼來完善安裝程序的功能,如使序列號有效等。
   爲敘述方便起見,這裏以前面用Project Wizard創建的"Your ApplicationName"安裝項目爲例。
3.3.1 文件、文件組和組件的修改
  在InstallShield中,一個組件包含若干個文件組,一個文件組包含若干個文件。並且,用戶可以通過InstallShield的開發環境中的相關屬性使它們互相關聯起來,如圖3.12所示。
  根據圖3.12所示的關聯屬性,我們可以作下列一些修改。
  1. 刪除不要的組件
  打開"YourApplication Name"安裝項目,將項目工作區窗口切換到Components頁面,選定組件項"ExampleFiles",右擊鼠標,從彈出相應的快捷菜單中選擇"Delete"菜單命令,刪除該組件。按類似的操作,刪除"HelpFiles"組件。這樣,安裝項目中只剩下"ProgramFiles""SharedDLLs"組件。
   2. 刪除不要的文件組
  將項目工作區窗口切換到FileGroups頁面,選定文件組項"ExampleFiles",右擊鼠標,從彈出相應的快捷菜單中選擇"Delete"菜單命令,刪除該文件組。按類似的操作,刪除"HelpFiles""ProgramDLLs"文件組。這樣,安裝項目中只剩下"ProgramExecutable Files""SharedDLLs"文件組。
  3. 向文件組中添加文件
  將項目工作區窗口切換到FileGroups頁面,展開所有的文件組項。選定"ProgramExecutable Files""Links"項,右擊鼠標,從彈出相應的快捷菜單中選擇"InsertFiles"菜單命令。這裏以VisualC++創建的單文檔應用程序的MySDI.exe爲例,將MySDI.exe(Release版本)文件調入。還有,需要在"SharedDLLs"文件組中將Windows\System文件夾下的mfc42.dllmsvcrt.dll調入。

4. 向組件中添加文件組
  將項目工作區窗口切換到Components頁面,選定組件項"ProgramFiles",雙擊右邊窗口中的"IncludeFile Groups"字段,彈出如圖3.13所示的屬性對話框。
  在該對話框中單擊[Add]按鈕,又彈出一個對話框,從該對話框的文件組列表中選定"ProgramExecutable Files",單擊[OK]按鈕。然後再單擊屬性對話框的[確定]按鈕,文件組"ProgramExecutable Files"就和組件"ProgramFiles"關聯起來。類似的,將"SharedDLLs"文件組與"SharedDLLs"組件相關聯。
  需要說明的是,組件中的字段"Overwrite"屬性起着非常關鍵的作用,因爲它決定了在文件複製時的覆蓋特徵。缺省時是"ALWAYSOVERWRITE",它的意思是:總是將源文件覆蓋機器中已有的文件。這樣就有可能將一些根本不能工作的老版本文件覆蓋機器中已有新版本文件。爲此,我們需要將組件的"Overwrite"值進行更改,尤其是"SharedDLLs"組件。
  雙擊"SharedDLLs"組件的"Overwrite"字段,彈出如圖3.14的屬性對話框。在該對話框的組合框中選擇要覆蓋的特性類型,通常我們選擇如圖3.14所示的類型,並進行如圖3.14所示的設置。這樣,只有源文件比用戶機器中的相同文件具有新的時間和高的版本時才覆蓋原文件。
3.3.2 添加Shell對象
  爲了能通過自定義函數SetupFolders中的CreateShellObjects函數創建程序菜單項或桌面圖標,我們需要在工作區窗口Resources頁面的ShellObjects目錄項中添加相關內容。
  需要說明的是:在Windows系統中,Shell通常指資源管理器和程序管理器的外殼。展開Shellobjects下的所有目錄項,當用戶右擊某個目錄項時,還將彈出相應的快捷菜單。通過它們用戶可以在資源管理器外殼(用於Windows95/98WindowsNT 4.0及其以後操作系統中)下創建程序文件夾(Folder命令)或快捷方式(Shortcut命令),在程序管理器外殼(用於Windows 3.1Windows NT3.51操作系統中)下創建程序組(Group命令)或圖標(Icon命令)
  1. 只在"程序"菜單中添加"我的單文檔應用程序"菜單項
其步驟如下:
  (1) InstallShield集成開發環境中,將工作區窗口切換到Resources頁面,並展開ShellObjects目錄項中所有的子項,如圖3.15所示。
  (2) 選定"StartMenu"下的"Programs"子項,右擊鼠標,從彈出的快捷菜單中選擇"New"->"Shortcut"(快捷方式)菜單命令,此時會在"Programs"目錄項下添加一個名爲"NewShortcut-1"
  (3) 選定剛纔的"NewShortcut-1",右擊鼠標,從彈出的快捷菜單中選擇"Rename",將"NewShortcut-1"改爲"App"。單擊"App",將在右邊窗口中顯示其屬性內容,如圖3.16所示。
  (4) 雙擊右邊窗口中的字段名,如"ShortcutText",則彈出如圖3.17所示的屬性對話框。單擊[>>]按鈕將從字符串資源中選擇一個字符串作爲快捷方式的名稱。
    (5) 在這個對話框中,用戶可以更改所有"Shortcut"屬性,其含義和結果如表3.1所示。
3.4 選擇媒介發佈
  當安裝項目的所有的內容被編譯後,若沒有任何錯誤,我們就可以通過MediaBuild Wizard創建適當的媒介來發布安裝程序。
3.4.1 使用Media Build嚮導
  一般來說,對於大型應用程序往往選擇CD_ROM媒介來發布,而對於小型應用程序則往往選擇3.5英寸軟盤(1.44MB)來發布。創建媒介的過程如下:
  (1) 用下面的三種方式之一啓動MediaBuild Wizard
   選擇"Build"菜單"->"MediaBuild Wizard"命令;
   單擊InstallShield開發環境工具欄上的"MediaBuild"工具按鈕。
   將InstallShield項目工作區窗口切換到"Media"頁面,單擊"MediaBuild Wizard"項,或右擊鼠標,從快捷菜單中選擇"MediaBuild Wizard..."命令。
  (2) MediaBuild Wizard啓動後,出現"MediaName"對話框。用戶在"MediaName"編輯框中可指定一個新的媒介名稱。
  (3) 保留缺省值,單擊[下一步]按鈕,出現"DiskType"對話框。在磁盤類型列表中,用戶可以選擇不同的媒介類型。其中,"singledisk image"用於將文件打包在同一張磁盤中,可進行二次壓縮。
  (4) 保留缺省值,單擊[下一步]按鈕,出現"BuildType"對話框。選中"FullBuild"項將全部創建所需要的文件數據,其中包括壓縮應用程序的所有文件,創建CAB文件及Setup.exe文件等。選中"QuickBuild"項,將測試和檢查安裝程序是否按預期的方式進行。單擊[Advanced...]按鈕,則彈出相應的高級屬性對話框,允許用戶設置口令、文件的時間/日期、媒介創建的目標位置等屬性。
  (5) 保留缺省值,單擊[下一步]按鈕,出現"TagFile"對話框。它用來輸入公司名稱及其他與應用程序相關的信息,這些信息將保存在Data.tag文件中。
  (6) 保留缺省值,單擊[下一步]按鈕,出現"Platforms"(操作平臺)對話框。
  (7) 保留缺省值,單擊[下一步]按鈕,出現"Summary"(摘要)對話框。這裏列出前面用戶指定的選項,若用戶不滿意還可單擊[上一步]按鈕重新進行選擇。
  (8) 單擊[完成]按鈕,InstallShield將根據用戶的選擇創建相應的媒介。這時用戶還可看到一個"BuildingMedia"對話框。一旦媒介創建完畢,用戶可按[Finish]按鈕結束MediaBuild Wizard

3.4.2 發送到實際的媒介中
  當一個媒介創建完成,並且安裝項目被正確編譯後,就可將其發送到實際的媒介中。這裏以剛纔創建的"NewMedia"爲例,說明發送的過程。
  (1) InstallShield項目工作區窗口切換到"Media"頁面,將會看到剛纔的"NewMedia",選定此項並右擊鼠標,從彈出的快捷菜單中選擇"SendMedia To ..."命令。
  (2) 彈出"SelectType"對話框。它用來指定將媒介文件複製到硬盤還是軟盤中。
  (3) 選中"Copyeach ’disk’ in the media to a removable disk"(複製到軟盤)項,單擊[下一步]按鈕,彈出"Select RemovableDrive"對話框。若有一個以上的軟驅,用戶可從組合框進行選擇。
  (4) 單擊[下一步]按鈕,彈出"Status"(狀態信息)對話框。單擊[Start]按鈕,系統將根據需要提示用戶插入一張格式化過的空白軟盤,並開始複製相關文件數據。
  (5) 一旦文件數據複製完成後,出現"Status"對話框。單擊[Close]按鈕關閉對話框,完成媒介發送過程。
  若用戶覺得上述過程比較麻煩,可直接將C:\MyInstallations\Your Application Name\Media\New Media\Disk Images\disk1文件夾中的內容複製到軟盤中。需要注意的是,若創建的媒介數據不止一張軟盤時,系統會將數據依次存入disk1disk2disk3...等文件夾中。
  本章從應用角度對基本安裝程序的建立過程作深入細緻的討論,使用戶不需要太多的腳本程序編寫就可對一般應用程序進行包裝發佈。但是,若僅僅掌握上述技巧則遠遠不夠,因爲一個出色的安裝軟件還應有其獨到之處,如果沒有創意,那隻能是一種重複勞動。因此,從下一章開始起,我們將就安裝界面的定製話題作進一步探討。

 

4 安裝界面的設計
2001-01-24·adding·yesky

前面討論的對話框在安裝程序中起着非常重要的作用,它們不僅可以獲得用戶選定的安裝路徑、需要安裝的組件及程序菜單項,而且還可以用於密碼和序列號的檢測等。但這些對話框是爲最後的文件數據安裝作準備的,因此從另一個角度來說,用戶的最終評價除了取決於對話框的界面效果外,在很大程度上還取決於最後的安裝主界面的好壞。當然,爲了使安裝界面具有一定的"個性",用戶還需要在InstallScript語言的基礎上,挖掘其運用能力,最大地發揮InstallShield的潛能。
4.1
  與一般Windows應用程序界面相比,安裝程序的界面(尤其是安裝主界面)要簡潔得多。正是因爲這個原因,我們在設計安裝界面時,尤其要考慮美學方面的要求。
4.1.1 主界面元素
  InstallShield5.5提供的圖形界面元素包括:彈出窗口、消息文本、對話框、圖標、位圖、聲音以及影像等。同樣,組成安裝主界面的元素一般也不會超出這些類型。4.1是一個常見的安裝主界面:
它含有這樣的一些元素:
  (1) 主標題
  在安裝主界面中,主標題往往是安裝的應用程序或軟件的名稱。一般情況下,主標題的字體是最大的。
  (2) 背景
  安裝主界面的背景往往決定了界面視覺效果的基調,它可以是用戶指定的漸變顏色或是純色。當然也可以用一張圖片作爲其背景。
  (3) 廣告牌
  InstallShield允許用戶在文件複製過程中發佈廣告牌。所謂廣告牌,是指這樣的一些位圖或元文件圖像,它們是用來顯示一切用於溝通、廣告、培訓、激發興趣以及引起注意的文字或圖片。
  (4) 進展指示器
  進展指示器,顧名思義,它是用來顯示文件安裝的進度。它有三種類型,一種是沒有[取消]按鈕的舊式指示器;另一種是隻有[取消]按鈕,但沒有邊框和標題欄的指示器;還有一種是帶有[取消]按鈕的進展對話框。圖4.1的進展指示器是最後一種的進展對話框。
  (5) 信息指示器
  信息指示器用來反映安裝過程中三個方面的信息,一是文件已複製到目的位置的百分比,另一是每張安裝盤的進展情況,最後是目的驅動器中所含有的可用空間大小。如圖4.2所示,當目的驅動器中可用的空間大小低於總容量的5%,最右下角的"Low"將變成紅色。
4.1.2 主界面操作函數
  InstallShield爲用戶提供了一些函數用來設置安裝主界面的特性以及將界面元素在主界面窗口中的放置位置。表4.1列出這些函數的形式及其功能說:
  其中Enable函數是最經常使用的,它是用來激活並顯示某個界面元素。其原型如下:
  Enable(nConstant);

其中,nConstant表示要激活的界面元素,它可以是下列值之一:
  BACKBUTTON 表示[上一步](Back)按鈕,缺省時該按鈕是被激活。
  BACKGROUND 當安裝處在窗口模式下時,顯示安裝界面的背景窗口。
  BITMAP256COLORS激活位圖的256色調色板
  DEFWINDOWMODE設置安裝界面的背景窗口是一個常規的、可調整大小及帶標題欄的窗口。當BACKGROUNDEnable後,設置的窗口立即有效。
  FEEDBACK_FULL表示信息指示器。
  FULLWINDOWMODE將安裝界面的背景窗口最大化。
  HOURGLASS 將鼠標指針變成"沙漏型"的等待光標。
  INDVFILESTATUS在進展指示器中顯示被傳送的文件全名。
  NEXTBUTTON 表示[下一步](Next)按鈕,缺省時該按鈕是被激活。
  STATUS 激活標準進展指示器,信息指示器(若沒有被禁用)也會被激活。
  STATUSDLG 激活對話框形式的進展指示器,信息指示器(若沒有被禁用)也會被激活。
  STATUSOLD 激活舊式的、不帶[取消]按鈕的進展指示器,信息指示器(若沒有被禁用)也會被激活。
  例如:若要指定安裝主界面的背景是一個最大化的窗口,則有下列代碼:
   Enable (BACKGROUND );
   Enable (DEFWINDOWMODE );
  需要說明的是,在與Enable相對的Disable函數中,形參nConstant的取值和Enable函數中的nConstant是基本相同的。
  4.1.3 界面設計的常用原則
  界面設計中常遵循的原則有:對比原則、協調原則、平衡原則、樂趣原則等。運用這些原則可以加強界面的氣氛、增加吸引力、突出重心、提高美感。其中,樂趣原則的重要性非同小可。所謂"樂趣"原則是指對用戶的愉悅心情的考慮,通俗來說就是提供以用戶爲中心,提高其舒適感。爲了遵循這個原則,用戶需要在比例、強調、凝聚、擴散以及形態等多個方面加以考慮。
  1. 比例
  黃金分割點(0.618),也稱黃金比例,是界面設計中非常有效的一種方法。在設計物體的長度、寬度、高度及其型式和位置時,如果能參照黃金比例來處理,就能產生特有的穩定和美感。
  2. 強調
  在單一風格的界面中,加入適當的變化,就會產生強調的效果。強調可打破界面的單調感,使界面變得有朝氣,例如,界面皆爲文字編排,看起來索然無味,如果加上插圖或照片,就如一顆石子丟進平靜的水面,產生一波一波的漣漪。

3. 凝聚與擴散
  人們的注意力總會特別集中到事物的中心部分,這就構成了視覺的凝聚。一般而言,凝聚型看似溫柔,也是許多人所喜歡採用的方式,但容易流於平凡。離心型的佈局,可以稱爲擴散型是具有現代感的編排型式。
  4. 形態的意象
  由於計算機屏幕的限制,一般的編排形式總是以四邊形爲標準形,其他各種形式都屬於它的變形。四角皆成直角,給人以很規律、表情少的感覺,其他的變形則呈現出形形色色的感覺,譬如成爲銳角的三角形有銳利、鮮明感,近於圓的形狀有溫和柔弱之感。相同的曲線也有不同的表情,例如用儀器畫出來的圓,有硬質感,而徒手畫出來的圓就有柔和的圓形曲線之美。
  5. 變化率
  在界面設計中,必須根據內容來決定標題的大小。標題和正文大小的比率就稱爲變化率。變化率越大,界面越活潑,變化率越小,界面格調越高。依照這種尺度來衡量,就很容易判斷界面的效果。
  6. 規律感
  具有共同印象的形狀反覆排列時,就會產生規律感。不一定要用同一形狀的東西,只要具有強烈印象就可以了。三四次的出現就能產生輕的規律感。有時候只反覆使用兩次特定的形狀,也會產生規律感。規律感在設計一個軟件時,可以使用戶很快地熟悉系統,掌握操作方法。這一點,相信用戶從微軟的Windows軟件中可以得到啓發。
  7. 導向
  依眼睛所視或物體所指的方向,使界面產生一種引導路線,稱爲導向。設計者在設計界面時,常利用導向使整體畫面更引人注目。一般來說,用戶的眼光會不知不覺地鎖定在移動的物體上,即使物體是在屏幕的角落,畫面的移動都會讓目光跟它移動的方向。瞭解了這一點,設計者就可以有意識地將用戶的目光導向到希望用戶注意的信息對象上。
  8. 空白區
  速度很快的說話方式適合體育新聞的播報,但不適合做典禮的節目主持人,原因是每一句話當中的空白量太少。界面設計的空白量問題也很重要,無論排版的平衡感有多好,讀者一看界面的空白量就已給它打好分數了。所以,千萬不要在一個界面上放置太多的信息對象,以至界面擁擠不堪。沒有空白區,就沒有界面的美。空白的多寡對界面的印象有決定性的影響。空白部分加多,會使格調提高並且穩定界面;空白較少,會使人產生活潑的感覺。設計信息量很豐富的雜塊界面時,用較多的空白顯然就不適合。

4.2 應用多媒體
  爲了不使安裝主界面過於單調,許多優秀軟件(Windows 98VisualStudio 98)的安裝界面中還用廣告牌的形式來發布相關信息,讓用戶更多地瞭解自己的產品;甚至還播放背景音樂或影像來消除用戶在安裝過程中長時間等待的不滿情緒。這種以多媒體的形式來豐富安裝主界面的特色,是InstallShield5.5中的顯著特點。
  4.2.1 字體
  字體是文字顯示的外觀形式,它包括了文字的字樣、風格和尺寸等多方面的屬性。適當地選用不同的字體,可以大大地豐富文字的外在表現力。例如,把文字中某些重要的字句用較粗的字體顯示,能夠體現出突出、強調的意圖。
    字體的字樣是字符書寫和顯示時表現出的特定模式,例如,對於漢字,通常有宋體、楷體、仿宋、黑體、隸書以及幼圓等多種字樣。字體風格主要表現爲字體的粗細和是否傾斜等特性。字體的尺寸是用來指定字符所佔區域的大小,通常用字符高度來描述。字體尺寸可以取毫米或英寸作爲單位,但爲了直觀起見,也常常採用一種稱爲""的單位,一點約摺合爲1/72英寸。對於漢字,還常用""數來表示字體尺寸,初號字最大,以下依次爲小初、一號、小一、二號、小二??,如此類推,字號越大,字體尺寸越小。
  InstallShield中,字體的應用範圍非常有限,用戶只能通過SetFontSetTitle設定安裝主界面中主標題的字體類型和大小。例如:
 SetFont ( FONT_TITLE ,STYLE_BOLD|STYLE_ITALIC|STYLE_SHADOW , "華文新魏" );
 SetTitle ("InstallShield漢化補丁", 28 , WHITE);
 // 設定主標題的內容爲"InstallShield漢化補丁"、大小爲28點、顏色爲白色
 其中SetFont是用來設置要顯示字符串的文本字體,它的原型如下:
 SetFont(nItemID, nFontStyle, szFontName);
 參數nItemID用來指定要設置字體的項目,目前只能爲FONT_TITLE,它表示主標題。nFontStyle用來指定字體的風格,它可以是下列的值之一:
  STYLE_NORMAL正常字體風格,它不能與其他值進行"|"組合
  STYLE_BOLD 粗體
  STYLE_ITALIC斜體
  STYLE_SHADOW文字帶陰影
  STYLE_UNDERLINE文字帶下劃線
  szFontName用來指定字體的字樣名。若指定的字樣名沒有找到,則使用缺省的"Arial"字樣。當函數返回0時表示字體設置成功,否則字體設置失敗。
  需要說明的是,在安裝Word2000後,Windows可使用的字體類型增加了很多,尤其是中文字體。但是,用戶最好能使用基本的中文字體,如宋體、楷體、黑體以及仿宋體,以適應不同的安裝環境。

4.2.2 顏色
  Windows提供了一種"設備無關"的顏色接口,使得應用程序只需提供"絕對的"顏色值,系統就會將該代碼以合適的顏色或顏色組合映射到計算機的顯示器上。同樣,也可以在InstallShield安裝腳本程序中使用RGB函數來指定某一個顏色值
  NUMBERnColor;
  ...
   nColor =RGB(128, 0, 255);
其中,RGB函數用來創建一個用於SetColorSetTitle函數中的顏色值。它的原型如下:
   RGB(constRed, constGreen, constBlue);
  參數constRedconstGreenconstBlue用來指定RGB中紅色、綠色和藍色分量值,取值範圍都是爲0-255,函數成功調用後返回相應的顏色值。
  但是,這一顏色值在系統不同的顏色模式下,其實際的顏色會有所不同。例如,在256色顏色模式下,由於實際顯示的顏色不會超過256色,因此係統會用最接近的顏色和用戶指定的顏色相匹配,從而造成了顏色的失真現象。爲了有效地解決這一問題,下面的建議值得參考:
  (1) 儘量使用Windows的標準色。例如對於256色模式,有下列20種標準色:
  RGB( 0, 0,0) 黑色
  RGB( 0, 0,255) 藍色
  RGB( 0,255, 0) 綠色
  RGB( 0,255, 255) 青色
  RGB( 255,0, 0) 紅色
  RGB( 255,0, 255) 品紅色
  RGB( 255,255, 0) 黃色
  RGB( 255,255, 255) 白色
  RGB( 0, 0,128) 暗藍色
  RGB( 0,128, 0) 暗綠色
  RGB( 0,128, 128) 暗青色
  RGB( 128,0, 0) 暗紅色
  RGB( 128,0, 128) 暗紫色
  RGB( 128,128, 0) 橄欖色
  RGB( 128,128, 128) 暗灰色
  RGB( 192,192, 192) 亮灰色
  RGB( 192,220, 192) 淡綠色
  RGB( 166,202, 240) 天藍色
  RGB( 255,251, 240) 乳白色
  RGB( 160,160, 164) 中灰色
  (2) 儘量使用純色,尤其是用於安裝主界面的背景顏色。這些純色是InstallShield中預先定義的一些值,並可從SetColor函數得到這些純色的應用
   SetColor(nObject, nColor);
  該函數用來爲指定的對象設定顏色。其中nObject用來指定要設定顏色的對象,它只能是下列值之一:
  BACKGROUND 表示安裝主界面的背景
  STATUSBAR 表示進展指示器中的進展條
  參數nColor用來指定要設定的顏色。它除了可以是用RGB定義的顏色外,還可使用下面的顏色:
  BK_BLUE 漸變的藍背景色
  BK_GREEN 漸變的綠背景色
  BK_MAGENTA 漸變的紫背景色
  BK_RED 漸變的紅背景色
  BK_YELLOW 漸變的黃背景色
  BK_SOLIDBLUE用於背景的純藍
  BK_SOLIDGREEN用於背景的純綠
  BK_SOLIDMAGENTA用於背景的純紫
  BK_SOLIDRED用於背景的純紅
  BK_SOLIDYELLOW用於背景的純黃
  BK_SMOOTH RGB定義的顏色進行"|"組合,用於產生漸變的背景色。
  GREEN 產生綠色的進展條
  RED 產生紅色的進展條
  BLUE 產生藍色的進展條
  MAGENTA 產生紫色的進展條
  YELLOW 產生黃色的進展條
  (3) 如果安裝界面一定要使用256色以上的顏色模式,那麼可通過GetSystemInfo(COLORS, 0, "")獲得當前系統使用的顏色數來確定是否繼續安裝。

4.2.3 位圖
  InstallShield5.5支持的圖像格式文件有兩種類型。一種是BMP,它是一些和顯示像素相對應的位陣列;另一種是WMF,它是一種標準型的圖元文件,也是一種設備無關的圖像文件,它將圖像以圖形對象(線、圓弧、多邊形)而不是用像素的形式來存儲的。
  在InstallShield5.5中,位圖不僅可以作爲廣告牌,而且可以作爲組件項前面的圖標以及對話框左邊的位圖標籤。
  1. 組件項前面的圖標
  下面的過程將使一個位圖成爲組件項前面的圖標:
  (1) 用圖像編輯軟件創建一個位圖,如圖4.3所示。
 該位圖實際上是由多個連續的16x 16的圖像組成的,其背景色一定要設置成紫色(RGB值爲255,0,255)
  (2) 將該位圖保存在View.bmp文件中。
  (3) 啓動InstallShield5.5,用ProjectWizard創建一個安裝項目。
  (4) 切換到項目工作區窗口的"SetupFiles"頁面,選定"LanguageIndependent\Windows 95/98 & NT 3.51/4.0"項。
  (5) 在右邊的屬性窗口中,右擊鼠標,從彈出的快捷菜單中選擇"InsertFiles..."命令。
  (6) 通過彈出的對話框將剛纔保存的View.bmp文件調入。
  (7) 將項目工作區窗口切換到Media頁面。單擊MediaBuild Wizard項,創建新的媒介。
  (8) 編譯並運行。在"SelectType"對話框中選擇"Custom"安裝類型,按[Next>]按鈕彈出如圖4.4所示的"SelectComponents"對話框界面。

需要說明的是,組件圖標可應用於ComponentDialogSdComponentDialogSdComponentDialog2SdComponentDialogAdvSdComponentMult對話框中。
  2. 一般位圖的顯示
  當然,若用戶想要將位圖顯示在屏幕的任何位置,則可使用PlaceBitmap函數,其原型如下:
   PlaceBitmap(szName, nID_BITMAP, nDx, nDy, nDrawOp);
  該函數是將一張圖像插入到安裝界面中。這個圖像來自於由szName指定的BMP文件、WMF文件或DLL文件。若szName指定的文件是DLL,則還需要用nID_BITMAP指定相應的位圖ID號。參數nDxnDy用來指定與安裝界面窗口邊框的水平和垂直偏移量,參數nDrawOp用來表示以下的操作方式:
  BITMAPICON 表示位圖有透明部分。用戶可以將此方式與其他操作進行"|"組合,但除TILEDFULLSCREENFULLSCREENSIZE外。當szName指定一個圖元文件或是一個24位位圖,則BITMAPICON不起作用。
  TILED 平鋪位圖
  FULLSCREENSIZE將圖像放大至整個安裝窗口
  CENTERED 居中位圖
  LOWER_LEFT 將位圖放置在窗口的左下角
  LOWER_RIGHT將位圖放置在窗口的右下角
  UPPER_LEFT 將位圖放置在窗口的左上角
  UPPER_RIGHT將位圖放置在窗口的右上角
  REMOVE 清除以前放置在窗口中的圖像

需要說明的是:
  (1) 應明確圖像文件路徑的影響。用於顯示在安裝界面的圖像文件的路徑,用戶可以在上述函數的szName中指定,例如D盤中存在MyImage.bmp文件,則szName應爲"D:\\MyImage.bmp"。雖然此時運行一般不會有問題,但將安裝程序發佈後問題就來了,因爲用戶的機器上不會有MyImage.bmp文件。爲此我們需要將要使用的所有文件(包括圖像文件)添加在InstallShield集成開發環境的"SetupFiles"頁面中,此時szName應爲:
   szName =SUPPORTDIR ^ "MyImage.bmp";
  (2) szName中也可使用分號";",用來設置相應的透明色。例如:
   szName =SUPPORTDIR ^ "TransImage.bmp;255, 128, 64";
其中,255, 128,64分別表示RGB顏色中的紅、綠、藍顏色分量值,用來指定這種顏色是透明的。
  (3) 若圖像保存在DLL文件中時,用戶不但用szName指定DLL文件全名,而且還要在nID_BITMAP指定相應的位圖ID號,這裏的ID號是DLL中已定義的。例如,若創建的DLL文件是MyImage.DLL,文件中包含一個位圖(ID號爲1000),當該文件調入安裝項目後,則有下列代碼:
  PlaceBitmap( SUPPORTDIR ^"MyImage.Dll" , 1000 , 0, 0, CENTERED );
圖像保存在DLL文件的方法有很多,例如用戶可以用VisualC++ 6.0修改_IsRes.DLL或利用InstallShield專業版提供的定製對話框的項目來構造編譯。但不管是怎樣的方法,其最後的DLL文件名一定不能是_IsRes.DLL_IsUser.DLL,否則相應的圖像不會顯示。
  (4) 當在PlaceBitmap函數中使用REMOVE操作方式之前,可以用nID_BITMAP指定一個位圖ID號,這樣就不需要再指定相應的圖像文件名。例如:
  PlaceBitmap( "D:\\CIBA2Ks.bmp" , 10 , 0, 0, CENTERED );
  elay(1);
  PlaceBitmap( "" , 10 , 0, 0, REMOVE );
  (5) 爲了使顯示的圖像更具特色,用戶可以在PlaceBitmap函數調用前,用SetDisplayEffect函數設置圖像顯示效果,它的原型如下:
  SetDisplayEffect(nEffect);
  其中,nEffect可以下列值之一(但這些效果對PlaceBitmap操作方式BITMAPICONBITMAPFULLSCREENBITMAPTILE不起作用)
  EFF_FADE 淡入淡出效果
  EFF_REVEAL 中心展開效果
  EFF_HORZREVEAL水平展開效果
  EFF_HORZSTRIPE水平條紋顯示效果
  EFF_VERTSTRIPE垂直條紋顯示效果
  EFF_BOXSTRIPE盒式條紋顯示效果
  EFF_NONE 取消任何效果
  3. 對話框的位圖標籤
   更改對話框的位圖標籤有兩種方法,一是使用VisualC++修改_IsRes.Dll中的對話框資源,另一是使用DialogSetInfo函數。由於第一種方法在本章的最後一節中還將具體介紹,故這裏僅討論第二種方法。
   函數DialogSetInfo是用來更改一些Sd對話框中的指定的元素,例如對話框中的位圖標籤、複選框的風格、硬盤空間大小的單位等。該函數往往應用於SdComponentDialogAdvSdComponentDialog2SdComponentMult對話框函數中。它的原型如下:
  DialogSetInfo(nInfoType, szInfoString, nParameter);
  其中參數nInfoType用來指定要更改的元素,它可以是下列值之一
  DLG_INFO_USEDECIMAL顯示的內存大小使用十進制
  DLG_INFO_KUNITS空間大小的單位使用KB
  DLG_INFO_ALTIMAGE更改對話框中的位圖標籤
  DLG_INFO_CHECKSELECTION更改複選框的風格
  參數szInfoString用來當nInfoTypeDLG_INFO_ALTIMAGE時指定的位圖文件名,該位圖的大小最好爲120x 260像素,否則超出該範圍的位圖被裁剪,小於該範圍的位圖被缺省的背景色(RGB(0,128,028))填充。
  參數nParameter用來當nInfoTypeDLG_INFO_CHECKSELECTION時,指定下列的值:
  CHECKBOXWindows 3.1的複選框
  CHECKBOX95Windows 95的複選框
  CHECKLINE 選中項
  CHECKMARK 選中標記

若當nInfoTypeDLG_INFO_ALTIMAGE時,則該參數可以是:
   -1 將位圖填入_isres.dll相關的資源中
   TRUEszInfoString必須指定顯示的位圖
  若當nInfoTypeDLG_INFO_KUNITSDLG_INFO_USEDECIMAL時,則該參數可以是
   TRUE 表示nInfoType設置有效
   FALSE 表示nInfoType設置無效,使用缺省的風格

  例如,下面的過程將使所有的對話框的位圖標籤改爲類似如圖4.5所示的外觀:
  (1) 用圖像編輯軟件創建一個大小爲120x 260的位圖。將該位圖保存在Bmp.bmp文件(也可是其他文件名)中。
  (2) 啓動InstallShield5.5,用ProjectWizard創建一個安裝項目。
  (3) 切換到項目工作區窗口的"SetupFiles"頁面,選定"LanguageIndependent\Windows 95/98 & NT 3.51/4.0"項。
  (4) 在右邊的屬性窗口中,右擊鼠標,從彈出的快捷菜單中選擇"InsertFiles..."命令。
  (5) 通過彈出的對話框將剛纔保存的Bmp.bmp文件調入。
  (6) 打開Setup.rul文件,在主程序體的最前面添上下列語句:
    DialogSetInfo( DLG_INFO_ALTIMAGE , SUPPORTDIR ^"Bmp.bmp" , TRUE );
  (7) 將項目工作區窗口切換到Media頁面。單擊MediaBuild Wizard項,創建新的媒介。
  (8) 編譯並運行。
  4.2.4 聲音和影像
  爲了豐富產品展示的表現力,提高安裝界面的專業水準,InstallShield還提供了PlayMMedia函數來播放MIDIWAVE聲音和AVI影像,其函數原型如下:
  PlayMMedia(nType, szFileName, nOperation, nReserved);
  其中,nType表示播放的媒體類型,它可以是MMEDIA_WAVE(MIDI音樂)MMEDIA_MIDI(WAVE音樂)MMEDIA_AVI(AVI影像)szFileName指定要播放的聲音或影像文件全名,若媒體文件調入安裝項目後,則可使用系統變量SUPPORTDIR來指明相應的路徑;nReserved目前保留,只能爲0nOperation表示媒體播放的方式,它可以是下列值:
  MMEDIA_PLAYSYNCH同步播放。它意味着只有當媒體播放完畢後,才執行下一步操作。
  MMEDIA_PLAYASYNCH異步播放。它意味着媒體是在後臺播放的,但爲了確保媒體播放結束,還必須指定MMEDIA_STOP操作。
  MMEDIA_PLAYCONTINUOUS循環播放。它只能和MMEDIA_PLAYASYNCH進行"|"組合。
  MMEDIA_STOP停止播放。
  顯然,若要在安裝進行過程中進行媒體的後臺連續播放,則可有下列代碼
PlayMMedia ( MMEDIA_WAVE , "C:\\Windows\\Media\\TheMicrosoft Sound.wav" ,
      MMEDIA_PLAYASYNCH|MMEDIA_PLAYCONTINUOUS, 0 );
   ...
PlayMMedia ( MMEDIA_WAVE , "C:\\Windows\\Media\\TheMicrosoft Sound.wav" ,
      MMEDIA_STOP, 0 );

4.3 界面的效果設計
  通過上面的介紹,相信用戶對安裝界面的設計有一定的瞭解,這裏再對一些常用的界面效果的設計方法和技巧作進一步的討論。
4.3.1 設置主界面的背景效果
  安裝主界面的背景可以有三種效果:一般背景色、漸變背景色以及位圖背景。
  1. 背景色
  在設置安裝主界面的背景色之前首先要調用Enable函數顯示安裝界面的背景,然後調用SetColor函數設置相應的背景色,若在該函數中指定BK_SMOOTH特性時,則產生垂直的顏色漸變效果,其中SetColor指定的顏色是界面最上邊的顏色,而最下面的顏色是黑色RGB(0,0, 0)例如:
  program
   Enable (BACKGROUND );
   Enable (FULLWINDOWMODE );
   SetColor (BACKGROUND , BK_RED|BK_SMOOTH );
   Delay( 3);
  endprogram
  其結果如圖4.6所示。需要說明的是,若不指定任何背景色,則安裝界面使用缺省的深青色RGB(0, 128, 128)
 2. 位圖背景
  如果想要用位圖作爲安裝界面的背景,用戶首先要創建一個BMPWMF圖片,然後調用PlaceBitmap函數將圖片顯示在安裝界面上。但爲了圖片能覆蓋整個背景,用戶還必須在該函數中指定FULLSCREENSIZEFULLSCREEN(若圖片大小超過背景)TILED特性。例如下面的代碼:
  program
   Enable (BACKGROUND );
   Enable (FULLWINDOWMODE );
   PlaceBitmap( "C:\\Windows\\安裝程序.bmp", 0 , 0 , 0 , FULLSCREENSIZE );
   Delay( 3);
  endprogram
其結果如圖4.7所示。
4.3.2 使用進展指示器
  在說明進展指示器的使用方法之前,我們先來看看ProjectWizard產生的一段代碼:
  functionMoveFileData()
   NUMBERnResult, nDisk;
   begin
    nDisk =1;
   SetStatusWindow( 0, "" );
    Disable(DIALOGCACHE );
    Enable(STATUS );
   StatusUpdate( ON, 100 );
    nResult =ComponentMoveData( MEDIA, nDisk, 0 );
   HandleMoveDataError(nResult );
    Disable(STATUS );
   returnnResult;
 end;
  顯然,上述代碼中的SetStatusWindowEnable以及StatusUpdate函數與進展指示器密切相關。其中,Enable用於激活和顯示不同類型的進展指示器,相關的參數值可以是STATUSDLG(標準類型的進展對話框)STATUS(只有[取消]按鈕的進展條)STATUSOLD(沒有[取消]按鈕的進展條)以及INDIVFILESTATUS(在進展指示器中顯示被傳送的文件全名)SetStatusWindowStatusUpdate分別用於設置進展指示器的相關信息及確定是否跟蹤文件複製進程,它們的原型如下:
   SetStatusWindow(nPercent, szString);
  該函數是爲進展指示器指定開始或當前的百分比以及在進展條上方顯示的當前文本信息。參數nPercent指定進展指示器的百分比大小(0100)。若不改變當前的百分比,則將此參數設定爲-1。參數szString指定在進展條上方顯示的當前文本信息。若指定組件的"StatusText"屬性後,則當調用ComponentMoveData函數時,這個信息內容還會自動被更改,
   StatusUpdate(bLink, nFinalPercent);
  該函數使用或禁止跟蹤文件傳送進程。一旦bLink設爲ON,當文件傳送時,進展條自動顯示出相應的百分比,直到nFinalPercent設定的最後百分比值爲止。參數bLink用來指定是使用(ON)或禁止(OFF)跟蹤文件複製進程。nFinalPercent用來指定跟蹤文件複製進程後要達到的最終百分比。若設定的最終百分比比當前的值要小,則進展條不會被更新。

需要說明的是:
  (1)StatusUpdate函數只對文件傳送函數CopyFileXCopyFile以及ComponentMoveData有效。
  (2) 用戶可以用SetStatusWindow函數來代替StatusUpdate進行文件傳送進程的人工跟蹤,例如下面的代碼:
  // 假設安裝項目中只有一個名爲"Component"的組件,該組件下包含一個文件組"FileGroup"
  NUMBERnTotalSize, nFileSize, nPercent;
  NUMBERnFileResult, nvResult;
  LISTlistFile;
  STRINGsvFile, svStatus, svResult;
  program
   nPercent =0;
   Enable (BACKGROUND );
   Enable (FULLWINDOWMODE );
   PlaceWindow( BILLBOARD , 0 , 0 , CENTERED );
   Enable(STATUSDLG );
   Enable( INDVFILESTATUS);
   PlaceWindow( FEEDBACK , 50 , 50 , LOWER_LEFT );
   PlaceWindow( STATUSDLG , 50 , 50 , LOWER_RIGHT );
   SetStatusWindow( nPercent , "準備複製文件...");
   VarSave(SRCTARGETDIR);
   TARGETDIR= "D:\\Temp";
   // 獲取所有組件中的文件字節總數
   nTotalSize= ComponentTotalSize ( MEDIA , "" , TRUE , TRUE );
   listFile =ListCreate( STRINGLIST );
   // 獲取與"Component"組件相聯的文件組中的所有文件
   ComponentFileEnum( MEDIA , "Component" , "File Group\\*.*" , listFile ,INCLUDE_SUBDIR );
   // 獲取"Component"組件的狀態文本信息
   ComponentGetData( MEDIA , "Component" , COMPONENT_FIELD_STATUS , nvResult , svStatus);
   nFileResult= ListGetFirstString( listFile, svFile );
   while (nFileResult != END_OF_LIST )
    // 獲取每個文件的字節大小
    ComponentFileInfo( MEDIA , "Component" , svFile ,COMPONENT_INFO_ORIGSIZE , nFileSize ,              svResult);
    // 計算當前的百分比
    nPercent= nPercent + 100 * nFileSize / nTotalSize;
    // 設置進展提示器的相關信息
    SetStatusWindow( nPercent , svStatus );
    nFileResult= ListGetNextString( listFile, svFile );
   endwhile;
   ListDestroy(listFile );
   VarRestore(SRCTARGETDIR);
   SetStatusWindow( 100 , " 文件複製完畢...");
   Delay ( 3);
  endprogram

  如果用戶執意人工處理組件的相關數據,雖然不失爲一種方法,但總沒有函數ComponentMoveData來得簡單,且該函數是唯一能自動更新信息批示器的函數。
  (3) 另外,用戶還可使用SetDialogTitleSetColor函數來改變進展對話框的標題和進展條的顏色。例如
  Enable(STATUSDLG );
  SetDialogTitle( DLG_STATUS , "複製文件");
  SetColor (STATUSBAR , BK_GREEN );
  需要說明的是,在調用Enable(STATUSDLG)前,使用Disable(FEEDBACK_FULL)函數可以使信息指示器隱藏。

4.3.3 展示廣告牌
  廣告牌是在文件傳送過程中顯示的一系列圖片(圖片的格式可以是位圖BMP或圖元WMF),它能起到溝通、廣告、訓練、促進、激發、娛樂以及其他引入注目的作用。
  在InstallShield5.5中,廣告牌是在調用ComponentMoveData函數進行文件傳送時自動展示的,用戶既不需要在文件傳送過程中激活廣告牌,也不需要指定廣告牌顯示時間的長短以及廣告牌與廣告牌的時間間隔。用戶需要做的僅僅是構造用於廣告牌的一系列圖片,然後將這一系列圖片正確命名,最後將其放入項目工作區窗口的"SetupFiles"頁面下的相應的語系和操作平臺中。
  顯然,正確命名用於廣告牌的圖片文件名是保證InstallShield自動展示廣告牌的關鍵。InstallShieldBMPWMF格式的圖片給出了不盡相同的命令規則。
  對於BMP格式的廣告牌而言,InstallShield允許用戶使用兩套圖片,其中一套用於低屏幕分辨率(640x 480),此時的圖片必須以Bbrdln.bmp命名,其中n199的連續數字,另一套適用於任何屏幕分辨率,這時的圖片必須以Bbrdn.bmp命名,其中n199的連續數字。用戶可以只指定Bbrdn.bmp系統的廣告牌,但當這兩套圖片同時放入"SetupFiles"頁面中時,安裝程序會自動根據用戶機器的屏幕分辨率自動揀選。
  對於WMF格式的廣告牌而言,InstallShield不支持低分辨率的廣告牌,也就是說,用戶只能使用一套適用於所有分辨率的廣告牌,這時的圖片必須以Bbrdn.wmf命名,其中n199的連續數字。儘管如此,用戶還可在文件傳送前使用SizeWindow函數調整WMF圖片顯示的大小,以滿足低分辨率屏幕的需求。
  使用廣告牌時,需要注意:
  (1) 在同一個安裝項目中,不能同時存在BMPWMF格式的廣告牌。
  (2) 儘管可以指定多達99張廣告牌圖片,但是用戶也應該充分考慮到文件傳送所需要的時間,因爲文件傳送完後廣告牌不再自動顯示。也就是說,用戶準備了25張圖片,但當文件傳送過程只持續了20秒時,則可能只有前面的10張圖片被顯示。
  (3) 在文件傳送過程中,由於InstallShield不會自動關閉MWF廣告牌的顯示,這時的廣告牌的隱藏必須通過後一張的覆蓋才能達到,因此在製作MWF廣告牌時,應將所有圖片設置成一樣的大小。
  4.3.4 改變窗口狀態
  我們有時需要對某個窗口的大小和位置進行改變,例如信息指示器和進展提示器的缺省位置不能令人滿意,尤其是在屏幕分辨率爲800x 600以上的情況下。這時,就要使用PlaceWindowSizeWindow函數,它們的原型如下
   PlaceWindow(nObject, nDx, nDy, nCorner);
  該函數用來改變包括廣告牌在內的窗口的位置。其中參數nObject用來指定要改變位置的窗口對象,它可以是下列值之一:
  ASKOPTIONSAskOptions對話框
  ASKPATHAskPath對話框
  ASKTEXTAskText對話框
  BACKGROUND 背景窗口
  BILLBOARD 廣告牌
  ENTERDISKEnterDisk對話框
  FEEDBACK 信息指示器
  MMEDIA_AVI 播放下一個AVI文件的窗口
  STATUS 進展指示器
  TATUSDLG 對話框風格的進展指示器
  參數nDx表示與nCorner指定邊框的水平距離(以像素爲單位)。例如,若nObject被指定爲FEEDBACK,則可使用CENTERED表示水平居中。所有窗口對象的缺省水平位置都爲居中。nDy表示與nCorner指定邊框的垂直距離(以像素爲單位)。例如,若nObject被指定爲FEEDBACK,則可使用CENTERED表示水平居中。所有窗口對象的缺省垂直位置都爲居中。nCorner用來指定被nDx nDy參考的邊角,它可以是下列值之一:
  LOWER_LEFT 表示左下角
  LOWER_RIGHT表示右下角
  UPPER_LEFT 表示左上角
  UPPER_RIGHT表示右上角
  CENTERED 居中
  SizeWindow(nObject, nDx, nDy);
  該函數用來改變指定窗口的大小。參數nObject用來指定要改變大小的窗口對象,它可以是下列值之一:
  BACKGROUND 背景窗口
  METAFILEWMF格式的廣告牌
  MMEDIA_AVI 播放下一個AVI文件的窗口
  參數nDxnDy用來指定窗口的水平和垂直像素大小。
  需要說明的是:
  (1) 由於上述兩個函數都跟屏幕的分辨率有關,因此在使用這兩個函數前最好調用GetExtents函數測試一下屏幕的分辨率。其函數原型如下:
  GetExtents(nvDx, nvDy);
  (2) 在改變窗口位置時,居中可以使屏幕分辨率的影響減小到最低程度,但在設置時,要注意PlaceWindow函數的最後三個參數都要使用CENTERED例如:
  PlaceWindow( FEEDBACK , CENTERED , CENTERED , CENTERED );
  PlaceWindow( STATUSDLG , CENTERED , CENTERED , CENTERED);
  (3)PlaceWindow函數中的nDxnDy兩個參數和nCorner緊密相關,且nCorner表示背景窗口的邊角。例如,若將信息指示器置於背景窗口的左下角,且距左下角的水平距離爲50像素,垂直距離爲20像素,則有下列代碼:
  PlaceWindow( FEEDBACK , 50 , 20 , LOWER_LEFT );
  (4) 對於有些窗口,需要用Enable函數來激活。例如,雖然EnablePlaceWindow函數出現的先後次序對結果沒有影響,但最好將Enable函數放在前面。

4.3.5 啓動畫面
  許多大型應用程序的安裝程序都有啓動畫面,如VC++等軟件。通過啓動畫面,除了可以顯示安裝的應用程序名稱和版權等提示信息外,還豐富應用程序安裝畫面,給安裝程序增添了許多動態特性和專業規範。
  在InstallShield中,安裝程序的啓動畫面是自動顯示的。用戶只需將名爲Setup.bmp位圖文件放置到安裝項目"SetupFiles"頁面中的"SplashScreen\Language Independent"目錄下,然後創建新的媒介即可。此時安裝程序運行後,用戶指定的啓動畫面將自動居中顯示。但當用戶使用ProjectWizard創建一個安裝項目時,InstallShield將使用缺省的啓動畫面,如圖4.8所示。
  需要說明的是:
   (1)InstallShield允許用戶構造的啓動畫面的顏色數最多爲256色,但同時也帶來另外一個問題。若安裝時系統的顏色數爲16色,則256色的啓動畫面將無法顯示。爲了能有效解決這一問題,InstallShield允許還允許用戶構造另一個16色的安裝啓動畫面,並以Setup16.bmp命名,InstallShield在安裝時將自動根據系統的顏色數進行揀選。當然,若用戶不想這麼做,那麼只要構造一個名爲Setup.bmp,顏色不超過16的安裝啓動畫面就可以了。
  (2) InstallShield專業版中,若在發佈給用戶的安裝初始化文件Setup.ini中的[Startup]下指定AppName=MyApplication值,則在開始的安裝進程消息對話框中顯示出用戶的產品名"MyApplication"

4.4 對話框界面的定製
  由於對話框是安裝界面中最重要的組成部分,因而InstallShield提供非常多的對話框函數來滿足了用戶製作安裝程序的需要。但這些對話框比較通用,沒有個性,且對話框中的字體、文本語言對普通用戶來說,並不盡人意,因爲我們總希望對話框上的文字是"簡體中文,宋體,9"。好在InstallShield的專業版中提供了相應的定製接口,這使得我們創建具有個性化界面的對話框成爲可能。
4.4.1 使用對話框定製模板
  InstallShield5.5正確安裝後,會在"\Examples\CustomDialog\VC++ 4 Project"文件夾中複製一些用於對話框定製的文件,它們主要有resource.h(資源頭文件)Sdrc.h(Sd對話框資源頭文件)_Isuser.RC(資源文件)_Isuser.mdp(項目文件),這些文件構成了用VisualC++定製對話框的模板。當用VisualC++對對話框資源更改後,我們就可以在VisualC++進行編譯,產生_Isuser.dll;再將_Isuser.dll調入InstallShield集成開發環境SetupFiles頁面中的指定的language(s)operatingsystem(s)項目中,就可以用腳本語言進行編程了。
  具體步驟如下:
  (1) 新建一個文件夾(MyDialog),將"\CustomDialog\VC++ 4 Project"內容複製到該文件夾中。
  (2) 打開Visual C++6.0,將MyDialog中的項目文件_Isuser.mdp調入。注意,_Isuser.mdp是低版本的項目文件,VisualC++ 6.0會將其自動轉換。
  (3) 將項目工作區窗口切換到Resource頁面,展開Dialog資源,雙擊Dialog項目下的DLG_TEMPLATE,結果如圖4.9所示。

 (4) 利用Visual C++的對話框編輯器作如下修改,結果如圖4.10所示:
  將對話框的標題改爲"對話框定製",字體設置爲"宋體,9"ID號值爲13029;並作如下修改:
將按鈕[Back]的標題改爲"<上一步(&B)",將按鈕[Next]的標題改爲"下一步(&X)>",將按鈕[Cancel]的標題改爲"取消"
  向對話框添加"靜態文本"控件,標題爲"撥號方式:"
  向對話框添加兩個單選按鈕,標題分別爲"音頻""脈衝"ID號值分別爲110100
  向對話框添加"靜態文本"控件,標題爲"通信串行口:"
  向對話框添加一個列表框,ID號值設爲330
  需要說明的是,用VisualC++向對話框添加控件時,它會自動給控件定義一個ID標識符。若在其屬性對話框中將此ID標識符後面添上"=XXX",就能將該標識符賦予相應的數值,例如圖4.11是將上述添加的列表框的ID號值設爲330
  而且,爲了能在對話框中顯示InstallShield內部定義的位圖,用戶必須在對話框中添加一個靜態文本控件,並使其標題內容爲"@10550,10551;1;1;0,128,128;0,128,128"。另外,對話框的ID值一般應大於13000(至少應大於12033),這是因爲InstallShield定義的對話框ID值最大爲12033。而控件的ID值雖沒有什麼限制,不過若查看一下Sdrc.h文件,也許會給您帶來一點啓發和幫助。
  (6) 編譯,結果在MyDialog\Release文件夾中產生_Isuser.dll
  (7) 關閉Visual C++,打開InstallShield5.5
  (8) 創建一個BlankSetup類型的安裝項目(設其缺省的項目名爲BlankSetup-1)
  (9) 將項目工作區窗口切換到SetupFiles頁面,將剛纔編譯產生的_Isuser.dll調入到LanguageIndependent項下的OperatingSystem Independent中。
  (10) 至此,對話框的模板創建並調入完畢。

4.4.2 定製對話框的腳本程序過程
  一旦對話框模板創建完畢,用戶就可以在腳本安裝程序中進行測試。我們按下列步驟進行:
  (1) InstallShield項目工作區窗口切換到Scripts頁面。選定最上一層的"’BlankSetup-1’ Script Files"項,右擊鼠標,從彈出的快捷菜單中選擇"Insert Files..."命令,將MyDialog文件夾中的resource.h調入。
  (2) 單擊"resource.h",在InstallShield右邊的文檔窗口中出現該文件內容。將resource.h文件的內容改寫:
  #defineDLG_TEMPLATE 13029 // 定義的對話框ID,與創建的對話框模板ID相同
  #defineID_PULSE 100 // 定義的控件ID,與"脈衝"單選按鈕ID相同
  #defineID_TONE 110 // 定義的控件ID,與"音頻"單選按鈕ID相同
  #defineID_COMPORT 330 // 定義的控件ID,與列表框ID相同
  #ifdefAPSTUDIO_INVOKED
  #ifndefAPSTUDIO_READONLY_SYMBOLS
  #define_APS_NO_MFC 1
  #define_APS_NEXT_RESOURCE_VALUE 101
  #define_APS_NEXT_COMMAND_VALUE 40001
  #define_APS_NEXT_CONTROL_VALUE 1000
  #define_APS_NEXT_SYMED_VALUE 101
  #endif
  #endif
  (3) 單擊"Setup.rul"項,打開Setup.rul文件,添加下列內容:
   #include"Resource.h"
   // 定義[Back][Next][Cancel]按鈕的標識值,這些值應與對話框模板中相應的標識值相同
   #defineSD_PBUT_BACK 12
   #defineSD_PBUT_CONTINUE 1
   #defineSD_PBUT_EXITSETUP 9
   BOOLbDone;
   STRINGsvComPort;
   NUMBERnCmdValue, nPulseState, nToneState, nDial9State;
   LISTlistID;
   program
   // 創建顯示在列表框中的鏈表
   listID =ListCreate(STRINGLIST);
   ListAddString(listID,"COMM1:", AFTER);
   ListAddString(listID,"COMM2:", AFTER);
   ListAddString(listID,"COMM3:", AFTER);
   ListAddString(listID,"COMM4:", AFTER);
   // 註冊對話框
   EzDefineDialog("MYCOMDIALOG", "", "", DLG_TEMPLATE);
   // 顯示對話框
   bDone =FALSE;
   while(bDone=FALSE)
   nCmdValue= WaitOnDialog("MYCOMDIALOG");
   switch(nCmdValue)
   case DLG_INIT:
   CtrlSetState("MYCOMDIALOG",ID_TONE, BUTTON_CHECKED);
   CtrlSetList("MYCOMDIALOG",ID_COMPORT, listID);
   // 處理用戶單擊[Next]按鈕的消息
   caseSD_PBUT_CONTINUE:
   CtrlGetCurSel("MYCOMDIALOG",ID_COMPORT, svComPort);
   nPulseState= CtrlGetState("MYCOMDIALOG", ID_PULSE);
   nToneState= CtrlGetState("MYCOMDIALOG", ID_TONE);
   bDone =TRUE;
   // 處理用戶單擊[Cancel]按鈕的消息
   caseSD_PBUT_EXITSETUP:
   bDone =TRUE;
   // 處理用戶關閉對話框的消息
   caseDLG_CLOSE:
     bDone =TRUE;
     // 處理用戶選中單選按鈕的消息
   case ID_PULSE:
    nPulseState = CtrlGetState("MYCOMDIALOG", ID_PULSE);
     if(nPulseState = BUTTON_CHECKED) then
      CtrlSetState("MYCOMDIALOG",ID_TONE, BUTTON_UNCHECKED);
      CtrlSetState("MYCOMDIALOG",ID_PULSE, BUTTON_CHECKED);
     else
      CtrlSetState("MYCOMDIALOG",ID_TONE, BUTTON_CHECKED);
      CtrlSetState("MYCOMDIALOG",ID_PULSE, BUTTON_UNCHECKED);
     endif;
   caseID_TONE:
     nToneState= CtrlGetState("MYCOMDIALOG", ID_TONE);
     if(nToneState = BUTTON_CHECKED) then
       CtrlSetState("MYCOMDIALOG",ID_PULSE, BUTTON_UNCHECKED);
       trlSetState("MYCOMDIALOG",ID_TONE, BUTTON_CHECKED);
     else
       CtrlSetState("MYCOMDIALOG",ID_TONE, BUTTON_UNCHECKED);
       CtrlSetState("MYCOMDIALOG",ID_PULSE, BUTTON_CHECKED);
     endif;

// 處理對話框錯誤消息
    caseDLG_ERR:
     MessageBox("內部的對話框錯誤!",SEVERE);
     bDone =TRUE;
    ndswitch;
   endwhile;
   // 結束對話框
   EndDialog("MYCOMDIALOG");
   // 將對話框和鏈表從內存中釋放出來
   ReleaseDialog("MYCOMDIALOG");
   ListDestroy(listID);
  endprogram
  (4) 將項目工作區窗口切換到Media頁面。單擊MediaBuild Wizard項,創建新的媒介。
  (5) 編譯並運行,結果如圖4.12所示。
  從以上的腳本程序,我們可以總結出其一般的代碼過程:
  (1) 定義對話框和控件的ID號,其值應與創建的對話框模板中相應的ID一致;
  (2) 使用EzDefineDialog函數來註冊對話框,以便InstallShield能識別它。當然,我們也可以使用DefineDialog函數,但EzDefineDialog最簡單,其原型如下:
  EzDefineDialog(szDialogName, szDLL, szID, nID);
  其中,參數szDialogName用來指定一個唯一的對話框名稱;szDLL用來指定含有註冊對話框的DLL文件名。若此參數爲NULLInstallShield將在_isuser.dll_isres.dll尋找相應的對話框。szID用來指定對話框的標識符名稱,若對話框的標識是一個數值,則此參數爲NULLnID用來指定對話框的標識符數值,若對話框的標識是一個字符串,則此參數爲0。函數返回0時表示註冊成功,返回DLG_ERR_ALREADY_EXISTS表示用戶在腳本程序試圖註冊一個已經存在的對話框,返回DLG_ERR表示一個不確定的錯誤產生了。
  (3) 用循環語句不斷顯示對話框,並處理對話框和控件的消息,其中WaitOnDialog用於定製對話框的顯示,其原型如下:
   WaitOnDialog(szDlgName);
  其中,參數szDialogName用來指定要顯示的對話框名稱。當對話框返回:
   dialogcontrol ID 表示接收到WM_COMMAND消息的控件ID
   DLG_CLOSE 表示對話框即將關閉
   DLG_ERR 表示產生錯誤
   DLG_INIT 表示對話框初始化消息
  (4) EndDialog函數結束對話框的顯示。
  (5) ReleaseDialog函數釋放對話框(以及鏈表)所佔用的內存空間
4.5 界面的漢化
  前面的對話框定製方法讓我們真正領略了InstallShield的可擴展的能力,但這種方法只能用於InstallShield的專業版,而對於其InstallShieldVisual C++6版本(若其他版本)的用戶來說,則只好忍痛割愛了。然而對原有的對話框界面進行定製卻是常常需要的,雖然,我們不一定非要改得面目全非,但至少需要將對話框中的英文變成中文。這就是我們常說的"漢化"
  軟件漢化的方法和工具有很多,但這不是本節所要討論的內容。本節所要闡述的是如何用VisualC++使InstallShield所創建的安裝程序的所有界面變成中文,並就漢化後的相關文件如何製作成補丁安裝程序作一些探討。
  4.5.1 概述
  軟件中文化是一項由來已久的傳統,由於自身的語言文化環境的影響,使得絕大數用戶都傾向於使用中文軟件;對於英文軟件來說,即便是非常優秀,用戶仍然感覺不愉快,原因是不習慣。爲了使優秀的英文軟件中文化,許多程序員及愛好者想出許多快速有效的、令人稱奇的漢化方法。但我們不提倡在無授權協議的情況下直接對軟件本身進行漢化,這是因爲漢化的本身是對軟件進行修改,有時不經意地就會違反了《知識產權保護條例》、《版權、著作權法》以及《專利法》等法規。
  4.5.2 Visual C++漢化
  從定製對話框的過程得到啓發,我們可以用VisualC++打開InstallShield5.5的對話框資源進行修改,這一點,用戶可以從InstallShield5.5的幫助說明中得到證實。
  InstallShield的對話框和Sd對話框資源都是存放在_IsRes.Dll文件中(位於"\Redistributable \Compressed Files\0009-English\Intel 32"文件夾),只要利用VisualC++對話框編輯器對資源中的文本內容進行修改即可起到漢化的目的。但是,用VisualC++漢化後的DLL(EXE)文件只能在WindowsNT中才能存盤,而在Windows95/98中卻不能保存。因此,下面的步驟是在WindowsNT中進行的:
  (1) 首先將原來的_IsRes.Dll複製到另外一個文件夾中,這裏假定爲D:\MyDLL
  (2) Windows NT中,啓動VisualC++ 6.0,選擇"File"菜單->"Open"命令,彈出通用"打開"對話框,打開D:\MyDLL中的_IsRes.Dll文件,注意一定要將"Openas"屬性設爲"Resource",如圖4.15所示。
(3) 這時,_IsRes.Dll的資源就被打開了,如圖4.16所示。其中的對話框資源Dialog是我們最關心的。
  (4) 展開"Dialog"項,可以看到InstallShield定義的所有對話框。
  選定所有的對話框,如圖4.17所示,並右擊鼠標,從彈出的快捷菜單中選擇"Properties..."命令,在彈出的屬性對話框中將所有的對話框語系由"English[U.S.]"改爲"Chinese[P.R.C]"。如圖4.18所示。
  (5) 雙擊某個對話框,則顯示其對話框模板。分別將對話框字體改爲"宋體,9",對話框和所有控件的標題改成漢字。
  (6) 同樣,用戶可以將所有的對話框模板作上述修改。
  (7) 其他的資源如BitmapString TableVersion等也可作類似的修改,這裏不再闡述。
  (8) 保存修改,退出VisualC++。至此,_IsRes.Dll漢化完畢。

4.5.3 漢化補丁安裝程序製作
  類似的,用戶還可以用上述方法再對位於"\Redistributable \Compressed Files\0009-English\Intel 32"文件夾中的IsUninst.Exe以及"\Redistributable \Uncompressed Files \0009-English \OS Independent"文件夾中的_setup.dll這兩個文件進行漢化。這樣,就保證了InstallShield製作的安裝程序界面是中文的。
  爲了讓您的朋友們分享您的成果,用戶可以將這三個文件(_IsRes.DllIsUninst.Exe_setup.dll)做成補丁安裝程序的形式發佈給您的朋友們。當然,在製作此類安裝程序時還應掌握一些技巧。
  1.檢測InstallShield的存在性
  檢測硬盤中是否存在InstallShield,只需判斷是否存在InstallShield的運行文件Ide.exe即可。當然,我們還應該考慮下列一些情況:
  (1) 查找到的Ide.exe的版本可能不是InstallShield5.5版。這種情況必須調用VerGetFileVersion來檢測其版本號是否爲"5.50.136.0"
  (2) 在硬盤中可能安裝多個不同版本的InstallShield,即Ide.exe不止一個。這種情況需要將所有查找到的Ide.exe文件用鏈表的形式保存下來,然後一個一個地進行版本的檢測。
  具體代碼如下:
  // 查找到的InstallShield安裝路徑由svPath參數返回
  functionExistsInstallShield(svPath)
  ISTlistDrive, listIDE;
  UMBERnResult, nFind, nStrLen;
  STRINGsvString, svSubStr, szFileName, svResult, svVersionNumber;
  BOOL bFind;
  begin
  bFind =FALSE;
  szFileName= "Ide.exe";
  listDrive =ListCreate ( STRINGLIST ); // 創建存放硬盤驅動器號的鏈表
  listIDE =ListCreate ( STRINGLIST ); // 創建存放Ide.exe不同版本的路徑鏈表
  GetValidDrivesList( listDrive , FIXED_DRIVE , 10000000 );
  nResult =ListGetFirstString ( listDrive , svString );
  // 查找硬盤中存在的所有Ide.exe
  while(nResult != END_OF_LIST)
   svString =svString + ":";
   nFind =FindAllFiles ( svString , szFileName , svResult , RESET );
   while(nFind = 0)
    ListAddString( listIDE , svResult , AFTER );
    nFind =FindAllFiles ( svString , szFileName , svResult , CONTINUE );
   ndwhile;
   nResult =ListGetNextString ( listDrive , svString );
  endwhile;
  nResult =ListGetFirstString ( listIDE , svString );
  // 判斷所有Ide.exe中是否存在版本號"5.50.136.0"的文件
  while(nResult != END_OF_LIST)
   VerGetFileVersion( svString , svVersionNumber );
   if(svVersionNumber = "5.50.136.0") then
    bFind =TRUE;
    nStrLen =StrLength(svString);
    StrSub(svSubStr,svString, 0, nStrLen - 15);
    StrRemoveLastSlash( svSubStr );
    svPath =svSubStr;
    gotoEndFind;
   endif;
   nResult =ListGetNextString ( listIDE , svString );
  endwhile;
  EndFind:
  ListDestroy(listDrive);
  ListDestroy(listIDE);
  returnbFind;
  end;

如果用戶覺得上述查找的方法所花費的時間太長,可以試試下面的方法,它是檢測Windows系統的"開始"->"程序"菜單項中是否存在InstallShield程序項。具體代碼如下:
  functionExistsInstallShieldFast(svPath)
   STRINGszFolderName, szItemName, svCmdLine, svWrkDir, svIconPath;
   STRINGsvShortCutKey;
   NUMBERnvIconIndex, nvMinimizeFlag, nResult, nStrLen;
   BOOLbFind;
   begin
    bFind =FALSE;
    szItemName= "InstallShield 5.5 Professional Edition";
    szFolderName= FOLDER_PROGRAMS^szItemName;
    nResult =QueryProgItem ( szFolderName , szItemName , svCmdLine , svWrkDir ,
              svIconPath, nvIconIndex , svShortCutKey , nvMinimizeFlag );
    if(nResult = IS_ITEM) then
      bFind =TRUE;
      nStrLen= StrLength(svCmdLine);
      StrSub(svPath,svCmdLine, 0, nStrLen - 15);
      StrRemoveLastSlash( svPath );
    endif;
   returnbFind;
  end;
  代碼中,函數QueryProgItem就是用來查找某個程序項或文件夾的,它的原型如下:
    QueryProgItem(szFolderName, szItemName, svCmdLine, svWrkDir, svIconPath,
    nvIconIndex,svShortCutKey, nvMinimizeFlag);
 其中,szFolderName用來指定包含程序項或目錄的文件夾名稱。它也可以是下列值之一:
   FOLDER_DESKTOP桌面文件夾
   OLDER_STARTMENU"開始"的文件夾
   FOLDER_STARTUP"啓動"菜單文件夾
 而szItemName用來指定要檢測的程序項或目錄名稱。svCmdLine用來返回程序項的可執行文件的命令行內容,或者是快捷方式的目標內容,或者是程序目錄的路徑全名。svWrkDir用來返回程序項的工作目錄的路徑全名。svIconPath用來返回.ico.exe文件的路徑全名。nvIconIndex用來返回用於程序項的圖標索引。svShortCutKey用來返回程序項的快捷鍵。nvMinimizeFlag用來返回程序項的運行方式,它可能是NULLRUN_MINIMIZED,分別表示常規窗口和窗口最小化。函數返回IS_ITEM表示szItemName是一個程序項或快捷方式,而返回IS_FOLDER則表示szItemName是一個程序文件夾,返回<0的值則表示不能查找。
  當然,最好的方法是通過註冊表來查找,在下一章將討論這方面的內容。
  2.複製並備份原來的文件
  安裝漢化補丁最終目的是將相關文件複製到系統中,爲此我們需要注意以下幾個方面:
  (1) 檢測漢化補丁是否經安裝過;
  (2) 將原來的文件備份;
  (3) 在創建新的媒介後,需要將漢化相關補丁文件複製到發佈的安裝媒介中,爲避免漢化後的_IsRes.DllIsUninst.Exe_setup.dll文件與安裝程序本身的衝突,最好將這三個文件的文件名作適當的更改,如在文件名前加"P"等。

具體代碼如下:
  functionInstallChineseFiles( szPath )
   STRINGszSrcDir1,szSrcDir2;
   NUMBERnResult;
   begin
   szSrcDir1= szPath ^ "Redistributable\\Compressed Files\\0009-English\\Intel32";
  szSrcDir2 =szPath ^ "Redistributable\\Uncompressed Files\\0009-English" +"\\OS                   Independent";
   VarSave(SRCTARGETDIR); // 將缺省的源文件夾和目標文件夾位置保存
   SRCDIR =szSrcDir1; // 設定缺省的源文件夾位置
   TARGETDIR= szSrcDir1; // 設定缺省的目標文件夾位置
   // 重新命名相關文件,以作爲備份
   nResult =RenameFile ( "_IsRes.Dll" , "_IsRes.bak" );
   if(nResult< 0 ) goto EndInstall;
   RenameFile( "IsUninst.Exe" , "IsUninst.bak" );
   SRCDIR =szSrcDir2;
   TARGETDIR= szSrcDir2;
   RenameFile( "_setup.dll" , "_setup.bak" );
   VarRestore(SRCTARGETDIR); // 恢復缺省的源文件夾和目標文件夾位置
   VarSave(SRCTARGETDIR);
   TARGETDIR= szSrcDir1;
   // 複製相關文件,注意必須要XCopyFile中指定COMP_UPDATE_DATE參數,
  // 這樣只有當複製文件比原來的文件的日期和時間新時,纔會複製,避免了重複安裝。
   nResult =XCopyFile ( "P_IsRes.Dll" , "" , COMP_UPDATE_DATE );
   nResult =XCopyFile ( "PIsUninst.Exe" , "" , COMP_UPDATE_DATE );
   if(nResult = 0) then
   SRCDIR =TARGETDIR;
   RenameFile( "P_IsRes.Dll" , "_IsRes.Dll" );
   RenameFile( "PIsUninst.Exe" , "IsUninst.Dll" );
   VarRestore(SRCTARGETDIR);
  endif;
  TARGETDIR =szSrcDir2;
  nResult =XCopyFile ( "P_setup.dll" , "" , COMP_UPDATE_DATE );
  if (nResult= 0) then
    SRCDIR =TARGETDIR;
    RenameFile( "P_setup.dll" , "_setup.dll" );
  endif;
  VarRestore(SRCTARGETDIR);
  EndInstall:
   if(nResult < 0) then
     MessageBox( "已經安裝過InstallShield的漢化補丁!", SEVERE );
   endif;
   returnnResult;
  end;

3.完整的安裝程序
  完整的安裝程序如下:
  prototypeExistsInstallShieldFast(BYREF STRING);
  prototypeInstallChineseFiles( STRING );
  STRINGsvPath;
  BOOLnvSetup, nvDelete, bHasSetup;
  program
  SetDialogTitle( DLG_MSG_SEVERE , "安裝失敗" );
  SetDialogTitle( DLG_MSG_INFORMATION , "安裝信息" );
  if(ExistsInstallShieldFast(svPath) = FALSE) then
 MessageBox ("安裝InstallShield5.5漢化補丁之前,應先安裝InstallShield" ,SEVERE );
    exit;
  endif;
  if (InstallChineseFiles(svPath)!= 0) then
    MessageBox( "安裝InstallShield5.5的漢化補丁失敗!", SEVERE );
  else
    MessageBox( "已成功安裝InstallShield5.5的漢化補丁!", INFORMATION );
  endif;
  endprogram
  functionExistsInstallShieldFast(svPath)
    ...
  begin
   // 同前
  end;
  functionInstallChineseFiles( szPath )
     ...
   begin
     // 同前
    end;
  至此爲止,用戶可以爲一個Windows一般應用程序建立一個頗具創意的安裝程序。但是,我們還沒有涉及到數據庫應用程序、VisualBasic應用程序以及其他複雜的應用程序的包裝,下一章將討論這一方面的內容。

 

5 深入安裝程序製作
  前面的幾章內容基本滿足了製作一般安裝程序的需求,但對於稍稍複雜的應用軟件來說,其安裝程序的構造有時變得異常困難,例如一個ODBC數據庫應用程序安裝後,其數據源若由安裝程序自動定義,則成爲一個技術難題。當然,InstallShield5.5的相關模板爲用戶的安裝程序提供了必要的結構框架,提高了製作安裝程序的效率,並且InstallShield5.5中的某些機制使得用戶很容易地對安裝組件和安裝類型進行控制。除此之外,安裝中還常常涉及到反安裝、多個安裝程序及調試等一些深入話題。
 5.1 反安裝
  當安裝在計算機中的應用程序或軟件不再需要時,用戶很希望能將它們完全刪除掉,這個過程稱爲反安裝。隨InstallShield5.5一起發行的UnInstallShield是一個非常不錯的反安裝工具應用程序,它雖然很小巧,但其功能一點不遜色;而且,當用InstallShield項目嚮導創建的安裝程序進行應用程序安裝後,UnInstallShield將自動跟蹤並記錄安裝內容,從而使得反安裝完全徹底地刪除應用程序。
  5.1.1 概述
  Windows98中,對一個應用程序進行反安裝,可按下列步驟進行:
  (1) 選擇桌面的"開始"菜單->"設置"->"控制面板"命令,打開"控制面板"窗口。
  (2) 找到"添加/刪除程序"項,雙擊鼠標左鍵(傳統方式)
  (3) 彈出"添加/刪除程序 屬性"對話框,在應用程序列表中選擇指定的應用程序名,如"YourApplication Name"
  (4) 彈出"ConfirmFile Deletion"詢問對話框,單擊[]按鈕進行下一步。
  (5) 單擊[]按鈕,系統將自動進行該應用程序的刪除,結果如圖5.1所示。
    (6) 單擊[OK]按鈕,關閉對話框。
  上述刪除應用程序的過程是自動執行的,但事實上,UnInstallShield運行時還需要一個可執行文件和一個反安裝註冊文件。一般地,IsUninst.exeUnInstallShield的可執行文件。當發佈用InstallShield項目嚮導創建的安裝程序時,IsUninst.exe被自動壓縮到_sys1.cab文件中。而當程序安裝後,IsUninst.exe還會被自動複製到Windows文件夾中。
  UnInstallShield的反安裝註冊文件是在每次進行應用程序安裝時由InstallShield自動創建的,用來記錄安裝過程中與反安裝相關的事件。表5.1列出了可以被UnInstallShield記錄的函數名。
  實際上,只有運行安裝腳本程序中的DeinstallStart函數後,反安裝註冊纔會真正開始。一般來說,缺省的註冊文件名爲DeisLxx.isu,其中"xx"是從1開始的順序號,若當前應用程序文件夾中沒有任何反安裝註冊文件,這時的註冊文件名爲DeisL1.isu
  5.1.2 IsUninst.exe命令行參數
  UnInstallShield的可執行文件IsUninst.exe可以使用一些命令參數,如表5.2所示。
  需要說明的是,IsUninst.exe和指定的命令行參數之間一定要有空格,並且若文件的路徑名中有空格,則必須在文件路徑名的兩邊用雙引號括起來。

5.1.3 建立反安裝
  在安裝腳本程序中建立一個完整的反安裝功能,一般要進行下列步驟:
  (1) 先調用InstallationInfo函數(必需的)DeinstallStart函數作準備;
  (2) 調用DeinstallStart(必需的)創建反安裝註冊文件並在註冊表中進行註冊;
  (3) 調用RegDBSetItem(必需的)在控制面板中"添加/刪除"對話框中的應用程序列表中添加反安裝的應用程序名,並在註冊表中進行註冊;
  (4) 爲了便於用戶的反安裝操作,還應將應用程序的反安裝命令放入程序文件夾中。
  上述過程中的InstallationInfoDeinstallStart以及RegDBSetItem函數的原型如下:
   InstallationInfo(szCompany, szProduct, szVersion, szProductKey);
  該函數是用來爲DeinstallStart函數指定相應的公司名稱、產品名稱、版本號以及應用程序的可執行文件名。參數szCompany指定公司的名稱,InstallShield使用szCompany參數在註冊表中的[HKEY_LOCAL_MACHINE]\Software下創建一個\鍵。szProduct指定產品的名稱,InstallShield使用szProduct參數在註冊表中的[HKEY_LOCAL_MACHINE]\Software\ 下創建一個\鍵。szVersion用來指定版本號,InstallShield使用szVersion參數在註冊表中的[HKEY_LOCAL_MACHINE]\Software\\下創建一個\ 鍵。szProductKey用來指定應用程序的可執行文件名,InstallShield使用szProductKey參數在註冊表中的[HKEY_LOCAL_MACHINE]\Software\Microsoft\Windows\CurrentVersion\AppPaths下創建相應的應用程序路徑,但這個應用程序路徑鍵只有通過調用函數RegDBSetItem纔會真正被創建。
   DeinstallStart(szDefLogPath, svLogFile, szKey, lStyle);
  該函數是用來在註冊表中創建相關的應用程序反安裝鍵以及對反安裝註冊文件進行初始化。參數szDefLogPath用來指定反安裝註冊文件放置的路徑全名,不要包含文件名。svLogFile用來指定反安裝註冊文件的文件名。若此參數爲"",則使用缺省的反安裝註冊文件名。szKey用來指定要創建的應用程序反安裝的鍵名。lStyle參數必須爲0
   RegDBSetItem(nItem, szValue);

該函數是用來在應用程序路徑鍵名或應用程序反安裝鍵名下設置相關的鍵值。參數szValue用來指定相應的鍵值。nItem用來指定要設置的鍵名,它可以是下列值之一:
  REGDB_APPPATH在應用程序路徑鍵名下設置[Path] 鍵值。
  REGDB_APPPATH_DEFAULT在應用程序路徑鍵名下設置[DefaultPath] 鍵值。
  REGDB_UNINSTALL_NAME在反安裝鍵名下設置[DisplayName] 鍵值。
按照上述的步驟,我們可以用下列的簡單示例來實現應用程序的反安裝功能。
  [Ex_UnInstall]實現應用程序的反安裝功能。
  // 定義所需要的符號常量
  #defineAPPBASE_PATH "我的目錄"
  #defineCOMPANY_NAME "我的公司"
  #definePRODUCT_NAME "我的應用程序"
  #definePRODUCT_VERSION "6.0"
  #definePRODUCT_KEY "test.exe"
  #defineDEINSTALL_KEY "Test_DeinstKey"
  #defineUNINSTALL_NAME "我的應用程序6.0"
  #definePROGRAM_FOLDER_NAME "我的應用程序"
  STRINGszMsg, svTarget, svResult, svUninstLogFile, svFolder, szProgram;
  program
  start:
   // 選擇安裝目的位置
   szMsg ="選擇一個目的位置.";
   svTarget =TARGETDISK ^ APPBASE_PATH;
   if(AskDestPath("", szMsg, svTarget, 0) = BACK) then
    goto start;
   endif;
 InstallationInfo(COMPANY_NAME,PRODUCT_NAME, PRODUCT_VERSION, PRODUCT_KEY);
   DeinstallStart(svTarget,svUninstLogFile, DEINSTALL_KEY, 0);
   RegDBSetItem(REGDB_UNINSTALL_NAME,UNINSTALL_NAME);
   // 在程序菜單中添加反安裝菜單項
   svFolder =PROGRAM_FOLDER_NAME;
   CreateProgramFolder(svFolder);
   szProgram= UNINST;
   szProgram= szProgram + " -f" + svUninstLogFile;
   AddFolderIcon(svFolder,"unInstallShield", szProgram, WINDIR,"", 0, "",REPLACE);
 ndprogram
  該程序在Windows98中運行後,結果爲:
  (1) "開始"->"程序"菜單中,創建了"我的應用程序"菜單,該菜單中包含了"UnInstallShield"命令,其鏈接的內容爲"C:\WINDOWS\IsUninst.exe-fC:\我的目錄\DeIsL1.isu"
  (2) 控制面板的"添加/刪除程序 屬性"對話框的應用程序列表中含有"我的應用程序6.0"
  (3) 缺省安裝後,應用程序的工作文件夾爲"C:\我的目錄",該文件夾中存在一個反安裝註冊文件DeIsL1.isu

5.2 安裝類型和組件控制
缺省的安裝程序項目一般都包括Compact(緊湊)Typical(典型)Custom(定製)三個安裝類型,以及每個安裝類型都有"ProgramFiles""ExampleFiles""HelpFiles""SharedDLLs"等組件。這些安裝類型和組件的控件既可以在集成開發環境中直接操作,也可以通過編程來控制。
  5.2.1 安裝類型控制
  在InstallShield5.5中,只有SdSetupTypeEx可以使用除CompactTypicalCustom之外的多個安裝類型,並且允許用戶對安裝類型進行定製。例如下面的過程:
  (1) 創建一個BlankSetup類型的安裝項目(設其缺省的項目名爲BlankSetup-1)
  (2) 將項目工作區窗口切換到SetupTypes頁面。
  (3) Setup Types頁面中,選定最上一層的"’BlankSetup-1’ Setup Types"項,並右擊鼠標,從彈出的快捷菜單中選擇"New Setup Type"命令。
  (4) 這時,安裝類型中添加了一個"NewSetup Type 1"類型。
  (5) 選定新添加的安裝類型項,右擊鼠標,從彈出的快捷菜單中選擇"Rename"命令。並將"NewSetup Type 1"改爲"Minimum"
  (6) 依次右擊各個安裝類型,從彈出的快捷菜單中選擇"Properties"命令。分別將它們的屬性改成下列結果
   安裝類型Description Display Name
   Minimum 最小安裝,大約需要50M硬盤空間。 最小
   Compact 緊湊安裝,大約需要80M硬盤空間。 緊湊
   Typical 典型安裝,大約需要120M硬盤空間(一般用戶的安裝方式) 典型
   Custom 定製安裝,最多需要150M硬盤空間(高級用戶的安裝方式) 定製
  (7) 將項目工作區窗口切換到Media頁面。單擊MediaBuild Wizard項,創建新的媒介。
  (8) 打開腳本安裝程序文件Setup.rul添加下列代碼:
    #include"sddialog.h"
    STRINGszTitle,szMsg,svSetupType;
    program
     Disable( BACKBUTTON );
     szTitle= "安裝類型";
     szMsg ="點取您希望的安裝類型,然後按[Next]按鈕";
     SdSetupTypeEx( szTitle , szMsg , "" , svSetupType , 0 );
    endprogram
    #include"sddialog.rul"
  (9) 編譯並運行。結果如圖5.2所示。
5.2.2 組件控制
  對組件的一般操作過程如下:
  (1) 創建一個BlankSetup類型的安裝項目(設其缺省的項目名爲BlankSetup-2)
  (2) 將項目工作區窗口切換到Components頁面。
  (3) 這時,安裝類型中已添加了一個"NewComponent 1"類型,選中並刪除它。
  (4) Components頁面中,選定最上一層的"’BlankSetup-2’ Components"項,並右擊鼠標,從彈出的快捷菜單中選擇"Component Wizard ..."命令。
  (5) 這時,彈出如圖5.3所示的對話框,在推薦的組件名列表框中選定"SharedFiles"項,並單擊[下一步]按鈕。
  (6) 彈出如圖5.4所示的對話框,保留缺省值,單擊[完成]按鈕後,系統就會添加一個名爲"SharedFiles"組件。
    (7) 重複上述操作,再爲安裝項目添加三個組件。這樣,連同原來缺省的Component組件在內一共有四個組件,名稱分別是"SharedFiles""HelpFiles""SampleFiles""NewComponent 0"
  (8) 選定"SampleFiles",並右擊鼠標,從彈出的快捷菜單中選擇"Rename"命令。並將"SampleFiles"改爲"Examples"。類似的,將"NewComponent 0"改爲"Executable"
  (9) 依次單擊各個安裝組件,分別將它們的屬性改爲如表5.3所示的結果。表中,NewFile Group 1NewFile Group 2NewFile Group 3是在安裝項目的FileGroups頁面中創建的新的文件組。
  (10) 將項目工作區窗口切換到Media頁面。單擊MediaBuild Wizard項,創建新的媒介。
  (11) 打開腳本安裝程序文件Setup.rul添加下列代碼
   #include"sddialog.h"
   STRINGszTitle, szMsg, szDir;
   program
    szTitle ="選擇組件";
    szMsg ="選擇您需要安裝的組件,清除不需要的組件。";
    szDir ="C:\\Program Files\\Temp";
    SdComponentDialog2( szTitle , szMsg , szDir , "" );
 
  endprogram
   #include"sddialog.rul"
 
 (12) 編譯並運行。結果如圖5.5所示。
從上面的示例過程可以看出: 通過InstallShield的開發環境對組件的操作,我們可以容易地實現組件的選擇以及相關文件的傳送,這是一種最穩妥的方法。當然,我們也可在安裝腳本程序中直接對組件進行添加、設定等操作。例如下面的代碼過程,其結果和圖5.5是一樣的。
  [Ex_ScriptComponent]直接用腳本程序對組件進行操作。
  #include"sddialog.h"
  #defineCOMP1 "必須的可執行文件"
  #defineCOMP1SIZE 7005184
  #defineCOMP1DESC "該組件包含該程序所必須的可執行文件"
  #defineCOMP2 "共享文件"
  #defineCOMP2SIZE 123904
  #defineCOMP2DESC "選中此組件,將安裝該程序的共享文件"
  #defineCOMP3 "幫助文件"
  #defineCOMP3SIZE 134144
  #defineCOMP3DESC "選中此組件,將安裝該程序的相應的幫助文件"
  #defineCOMP4 "示例文件"
  #defineCOMP4SIZE 82944
  #defineCOMP4DESC "選中此組件,將安裝該程序的VisualC++ 6.0的示例文件"
  #defineDESTDIR "C:\\Program Files\\Temp"
  #defineSDCOMPTITLE "選擇組件"
  #defineSDCOMPMSG "選擇您需要安裝的組件,清除不需要的組件。"
  STRINGszDir;
  NUMBERnData;
  program
   // 定義一個新的media名稱
   MEDIA ="MyComponent";
   // 添加第一個組件
   ComponentAddItem(MEDIA, COMP1, COMP1SIZE, TRUE );
   // 設置第一個組件的相關屬性
   ComponentSetData( MEDIA, COMP1, COMPONENT_FIELD_DESCRIPTION, nData, COMP1DESC );
   ComponentAddItem(MEDIA, COMP2, COMP2SIZE, TRUE );
   ComponentSetData( MEDIA, COMP2, COMPONENT_FIELD_DESCRIPTION, nData, COMP2DESC );
   ComponentAddItem(MEDIA, COMP3, COMP3SIZE, TRUE );
   ComponentSetData( MEDIA, COMP3, COMPONENT_FIELD_DESCRIPTION, nData, COMP3DESC );
   ComponentAddItem(MEDIA, COMP4, COMP4SIZE, TRUE );
   ComponentSetData( MEDIA, COMP4, COMPONENT_FIELD_DESCRIPTION, nData, COMP4DESC );
   szDir =DESTDIR; // 設置目的文件夾
   SdComponentDialog2( SDCOMPTITLE, SDCOMPMSG, szDir, "" );
   // 恢復原來的media
   MEDIA ="DATA";
   endprogram
   #include"sddialog.rul"

上述程序中,ComponentAddItemComponentSetData是用於組件添加和設定的兩個主要函數,它們的原型如下:
   ComponentAddItem(szMedia, szComponent, nDataSize, bSelected);
  該函數用來創建一個只適用於script-created組件系列的組件。參數szMedia用來指定script-created組件系列的媒介名。若指定的媒介名不存在,則將自動創建。szComponent用來指定要添加的組件,不能爲NULLnDataSize用來指定該組件所包含的文件字節數。bSelected用來指明該組件的缺省選定狀態。爲TRUE時,表示選中;爲FALSE時,表示未選中。
   ComponentSetData(szMedia, szComponent, nInfo, nData, szData);
  該函數用來設置某個組件的相關屬性,這些屬性和InstallShield開發環境中的組件屬性基本一一對應。參數szMedia用來指定script-created組件系列的媒介名。szComponent用來指定要設置的組件。nData用來指定設置的數值。szData用來指定設置的文本。nInfo用來指定要設置的屬性類型,它可以是下列值之一:
  COMPONENT_FIELD_DESCRIPTION組件的描述
  OMPONENT_FIELD_FTPLOCATION組件的FTP地址
  COMPONENT_FIELD_HTTPLOCATION組件的HTTP地址
  COMPONENT_FIELD_VISIBLE組件是否在選擇組件對話框中顯示出來。相應的nDataTRUE(顯示)FALSE(不顯示)
  COMPONENT_FIELD_SELECTED組件的選中狀態。相應的nDataTRUE(選中)FALSE(未選中)
  COMPONENT_FIELD_MISC組件的其他文本
  COMPONENT_FIELD_DISPLAYNAME組件的顯示名稱
  需要說明的是,在安裝初始化時,系統變量MEDIA的缺省值被設置成"DATA",若用戶用script-created機制創建組件,並更改了MEDIA的值,則在調用ComponentMoveDataCreateShellObjectsCreateRegistrySet函數之前,必須將MEDIA的值重新設置成原來的"DATA"

5.3 製作數據庫應用程序的安裝
  在製作安裝程序時,如果一般應用程序的運行環境只是需要一些相應的DLL文件,那麼這種類型的安裝程序是非常容易製作的。例如,一般的VisaulBasic 6.0應用程序程序只需要在Windows操作系統的System目錄中安裝Msvbvm60.dllComCat.dll文件就可以運行了。但是,若是一個數據庫應用程序,安裝程序的構造就變得複雜了,因此我們不僅需要考慮所使用的數據庫技術和方式,而且還要考慮採用的數據庫開發軟件環境。好在InstallShield提供了ODBC-DAO-RDOOLE DBAccessPowerBuilder等安裝程序模板,使得問題變得簡單許多。
  5.3.1 概述
  用數據庫方式來管理人們日常生活中大量的信息已變得越來越重要,並涌現出許多數據庫管理系統,如PowerBuilderMicosoftAccessMicosoftSQL ServerOracleServerSybaseSQL ServerMicosoftVisual Foxpro等。儘管這些系統能出色地勝任數據庫的管理,但卻不能開發出其它功能強大的Windows應用程序。而許多開始工具如VisualC++就能將關係數據庫與面向對象的編程方法有機地結合起來,使得數據庫處理和應用程序開發都能很好地進行。
  一般地,這些開發工具通常爲用戶提供了ODBC(OpenDatabase Connectivity,開放數據庫連接)DAO(DataAccess Objects,數據訪問對象)OLE DB(OLEData BaseOLE數據庫)三種數據庫方式,使用戶的應用程序從特定的數據管理系統(DBMS)脫離出來。
  ODBC提供了應用程序接口(API),使得任何一個數據庫都可以通過ODBC驅動器與指定的DBMS相聯。用戶的程序就可以通過調用ODBC驅動管理器中相應的驅動程序達到管理數據庫的目的。作爲MicrosoftWindows Open Standards Architecture (WOSAWindows開放式服務體系結構)的主要組成部分,ODBC一直沿用至今。
  DAO類似於用MicrosoftAccessMicrosoftVisual Basic編寫的數據庫應用程序,它使用Jet數據庫引擎形成一系列的數據訪問對象:數據庫對象、表和查詢對象、記錄集對象等。它可以打開一個Access數據庫文件(MDB文件),也可直接打開一個ODBC數據源以及使用Jet引擎打開一個ISAM(被索引的順序訪問方法)類型的數據源(dBASEFoxProParadoxExcel或文本文件)
  OLE DB試圖提供一種統一的數據訪問接口,並能處理除了標準的關係型數據庫中的數據之外,還能處理包括郵件數據、Web上的文本或圖形、目錄服務(DirectoryServices),以及主機系統中的IMSVSAM數據。OLE DB提供一個數據庫編程COM(組件對象模型)接口,使得數據的使用者(應用程序)可以使用同樣的方法訪問各種數據,而不用考慮數據的具體存儲地點、格式或類型。這個COM接口與ODBC相比,其健壯性和靈活性要高得多。但是,由於OLEDB的程序比較複雜,因而對於一般用戶來說,應使用基於OLEDB的高層訪問接口ADO(ActiveXData Object),它對OLEDB的接口作爲封裝,從而使程序開發得到簡化。

 5.3.2 使用InstallShield安裝模板
  InstallShield提供一定數量的安裝程序模板,這些模板是一些完整的、可運行的安裝項目,用戶可以將其直接複製或在此基礎上作進一步的修改。
  InstallShield5.5版本提供瞭如下的安裝程序模板:
  1. Template One
   該模板是用於Windows9598Windows NT4.0的最基本的安裝程序模板,用戶通過少量的修改可滿足一般安裝程序的需要。以此模板創建的安裝程序執行下列過程:
  (1) 顯示缺省的安裝啓動畫面,用戶通過修改Setup.bmp來改變顯示的內容;
  (2) 調用SdWelcome函數,顯示相關歡迎信息;
  (3) 調用SdAskDestPath函數獲取用戶指定的安裝路徑;
  (4) 進行反安裝與安裝相關操作;
  (5) 在系統"開始"->"程序"文件夾中創建程序文件夾及其快捷方式,並調用ShowProgramFolder函數顯示相關的內容;
  (6) 調用MessageBox函數提示安裝結束。
  2. Template Two
  該模板比TemplateOne更完善,它的功能和"ProjectWizard"非常相似。
  3. MFC Template
   該模板用於基於MFCWindows 9598Windows NT4.0的基本應用程序模板。以此模板創建的安裝程序執行與TemplateOne相似的過程,不同的是它使用自帶的MFC運行庫Mfc42.dllMsvcrt.dll,當然用戶可以將本地最新的運行庫複製到\TemplateData\MFCTemplate Data相關目錄下來更新。
  4. Visual Basic Template
   該模板用於基於VB5.0/6.0Windows 9598Windows NT4.0的基本應用程序的完整模板,安裝程序運行過程和"ProjectWizard"是一致的。若用戶想要用此模板安裝VB6.0應用程序,則需要將"WindowsSystem Self-reg Shared Files"文件組中的Msvbvm50.dll更換成Msvbvm60.dll即可。
  除了上述應用程序基本模板之外,InstallShield5.5還對最新的數據庫方式提供了許多模板,它們是AccessTemplate(支持MicrosoftAccess97)ADOTemplate(全面支持ADO,安裝ADOOLE DB提供者以及OLE DB)BDE 4.51Template(支持BorlandDatabase Engine 4.51)BDE5 Template(支持BorlandDatabase Engine 5)ODBC-DAO-RDOTemplate(全面支持DAORDO以及ODBC,包括桌面數據庫驅動程序、SQLOracle驅動程序以及相應的DSN)OLE DBTemplate(支持OLEDB 1.5)PowerBuilderTemplate(全面支持PowerBuilder6.0,包括運行庫文件、ODBC3.5)
  儘管InstallShield5.5有很多上述這樣的模板,但它們的使用方法卻是基本一樣的。例如下面的過程是用ODBC-DAO-RDOTemplate創建VC6.0數據庫應用程序的安裝項目,已知:Access2000製作的數據庫student.mdb,庫中包含一張名爲xs的數據表,要求ODBC數據源名爲"我的數據庫"

(1) 啓動InstallShield5.5
  (2) 單擊"New"工具按鈕或選擇"File"->"New"菜單命令,彈出相應的對話框,切換到"Template"頁面,如圖5.6所示;
  (3) 在左邊的模板列表框中選定"ODBC-DAO-RDOTemplate",單擊[Properties]按鈕可以查看該模板有相關屬性信息;
  (4) 單擊[確定]按鈕後,IntallShield就會自動爲用戶創建一個名爲"ODBC-DAO-RDOTemplate"的安裝項目;
  (5) 切換到"FileGroups" 頁面,展開"ProgramFiles"項,選定"StaticFile Links",刪除右邊窗口中的所有文件,並右擊鼠標,從彈出的快捷菜單中選擇"InsertFiles"命令,將數據庫文件"student.mdb"VC6.0應用程序(Release)"Ex_ODBC.exe"調入;
  (6) 按相同的方法,將"WindowsSystem Self-reg Files"下的鏈接文件改爲"Msvcrt.dll",並將其"Self-Registered"屬性改爲"No",將"WindowsSystem Self-reg Share Files"下的鏈接文件改爲"Mfc42.dll"
  (7) 切換到"Resource"頁面,展開"ExplorerShell"的所有子項,選定"App",將其"Target"屬性改爲"\Ex_ODBC.exe"
  (8) 切換到"SetupFiles" 頁面,展開"English"項,選定"Windows95/98 & NT 3.51/4.0",雙擊右邊窗口的"_Drivers.ini"文件,這樣該文件就會被打開。
  (9) "_Drivers.ini"內容移到最後,找到[SampleAccess Data Source]部分,其中關鍵字:
  Description顯示在ODBC管理器中的DSN描述
  Driver DSN相關的驅動文件名
  DSN 數據源名
  DBQ DSN相關的數據源的完整路徑和數據庫文件
  ranslationDLLDSN相關的形實轉換DLL
  TranslationNameTranslationDLL相關的註冊表鍵名
  ISODBCDriverDesc與驅動相關的註冊表鍵名
  ISODBCComponentNameDSN相關的安裝組件名
  ISODBCDSNType要安裝的數據源類型,當爲user時表示缺省的用戶數據源類型,當爲system時爲系統數據源類型。
  在以上的關鍵字中,Description=,Driver=, DSN=, DBQ= 是必須的。
  (10) [SampleAccess Data Source]內容改變如下:
    Description=用於VC++的數據源
    ISODBCDriverDesc=MicrosoftAccess Driver (*.mdb)
    Driver=\odbcjt32.dll
    DSN=我的數據庫
    DefaultDir=
    DBQ=\student.mdb
    UID=
    SODBCUninstDSN=TRUE
    ISODBCComponentName=ISODBCDrivers\Microsoft Access Driver
  (11)將項目工作區窗口切換到Media頁面,單擊MediaBuild Wizard項,創建新的媒介。
  (12)編譯並運行。
  需要說明的是,這些數據庫模板的目的是將數據庫應用程序及其所有的運行環境全部包裝,這是一個最完美的做法;但同時也帶來一個新的問題,那就是由於數據庫運行環境的版本變化,原有的安裝模板不再適用。爲此需要用戶及時訪問http:// www.installshield.com/support/is5以獲得最新的安裝模板。

5.3.3 使用註冊表
  InstallShield5.5爲用戶許多操作註冊表的函數,如RegDBSetDefaultRootRegDBCreateKeyExRegDBSetKeyValueExRegDBDeleteKey等,它們的原型如下:
   RegDBSetDefaultRoot(nRootKey);
  該函數用來設置InstallShield操作的缺省根鍵名。參數nRootKey用來指定要設置的根鍵名,它可以是HKEY_CLASSES_ROOTHKEY_LOCAL_MACHINE(WindowsNT 4.0除外)HKEY_CURRENT_USERHKEY_USERS
   RegDBCreateKeyEx(szKey, szClass);
  該函數用來在註冊表中創建一個主鍵。參數szKey用來在註冊表的根鍵下,指定要創建的鍵名,可以使用兩個反斜槓"\\"來表示子鍵的層次。szClass用來指定一個與該鍵相關的類名(高級用戶使用)
   RegDBSetKeyValueEx(szKey, szName, nType, szValue, nSize);
  該函數用來在註冊表中設置指定鍵名下的鍵值。參數szKey用來指定要設置鍵值的鍵名,可以使用兩個反斜槓"\\"來表示子鍵的層次;szName用來指定在szKey下與設置的值相關聯的值名;若要設置缺省的值,將szName置爲NULLszValue用來指定要設置的值;nSize用來指定要設置值的字節大小;nType表示設置的數值類型,它可以是下列值之一:
   REGDB_STRING單行字符串文本
   REGDB_STRING_EXPAND含有類似"%MYPATH%"的字符串
   REGDB_STRING_MULTI多行字符串文本
   REGDB_NUMBER數字
   REGDB_BINARY二進制數值
  RegDBSetKeyValueEx相對應的函數是RegDBGetKeyValueEx,它是用來在註冊表中獲取指定鍵名下的鍵值。
  RegDBDeleteKey(szSubKey);
  該函數在註冊表中刪除由szSubKey指定的一個鍵。
  1. 通過註冊表獲取應用程序安裝信息
  在第四章第五節中,介紹了兩種方法來檢測InstallShield5.5專業版的安裝路徑。但如果用註冊表操作函數就顯得非常容易,如下面的代碼
  #defineTITLE "RegDBGetAppInfo"
  STRINGszStrName, svStrValue, szMsg;
  NUMBERnvSize, nvType;
  program
   // Set theroot key.
   RegDBSetDefaultRoot(HKEY_LOCAL_MACHINE);
   szStrName= "Software\\InstallShield\\InstallShield Professional\\5.5\\Main";
   if(RegDBGetKeyValueEx (szStrName, "Path", nvType, svStrValue, nvSize)< 0) then
     MessageBox ("Failed to get application information value.", SEVERE);
      abort;
   else
    if(nvType = REGDB_STRING) then
     szMsg ="The InstallShield Installed Path is : %s ";
     SprintfBox(INFORMATION, TITLE, szMsg, svStrValue);
    endif;
   endif;
  endprogram

2. 通過註冊表配置ODBC
  除了前面ODBC-DAO-RDO的安裝模板可以進行ODBC數據源的設置外,直接操作註冊表也能達到同樣效果。
  例如下面的過程是利用MFCTemplate製作VC6.0數據庫應用程序的安裝項目(條件同前):
  (1) 啓動InstallShield5.5
  (2) 單擊"New"工具按鈕或選擇"File"->"New"菜單命令,彈出相應的對話框,切換到"Template"頁面,選定"MFCTemplate",單擊[確定]按鈕後,IntallShield就會自動爲用戶創建一個名爲"MFCTemplate"的安裝項目;
  (3) 切換到"FileGroups" 頁面,展開"ProgramFiles"項,選定"StaticFile Links",刪除右邊窗口中的所有文件,並右擊鼠標,從彈出的快捷菜單中選擇"InsertFiles"命令,將數據庫文件"student.mdb"VC6.0應用程序(Release)"Ex_ODBC.exe"調入;
  (4) 切換到"Resource"頁面,展開"ExplorerShell"的所有子項,選定"App",將其"Target"屬性改爲"\Ex_ODBC.exe"
  (5) 切換到"Scripts"頁面,打開"setup.rul"腳本文件,在
   prototypeCheckRequirements ();
   語句的下一行添加下列語句:
   prototypeSetupRegistry();
  (6) 在腳本文件的後面添加該自定義函數代碼:
  functionSetupRegistry()
   NUMBERnResult;
   STRINGszPath, szKeyName;
   begin
    RegDBSetDefaultRoot( HKEY_CURRENT_USER );
    szKeyName= "Software\\ODBC\\ODBC.INI\\我的數據庫";
    if(RegDBKeyExist(szKeyName)<0)then
     RegDBCreateKeyEx(szKeyName,"");
    else
     RegDBDeleteKey(szKeyName);
     RegDBCreateKeyEx(szKeyName,"");
    endif;
   szKeyName= "Software\\ODBC\\ODBC.INI\\ODBC Data Sources";
   if(RegDBKeyExist(szKeyName)<0)then
    RegDBCreateKeyEx(szKeyName,"");
   endif;
   RegDBSetKeyValueEx( szKeyName, "我的數據庫", REGDB_STRING ,"Microsoft Access Driver (*.mdb)" , -1);
  szPath =TARGETDIR^"student.mdb";
  szKeyName ="Software\\ODBC\\ODBC.INI\\我的數據庫";
  RegDBSetKeyValueEx( szKeyName, "DBQ" , REGDB_STRING , szPath , -1);
  RegDBSetKeyValueEx( szKeyName, "Description" , REGDB_STRING , "用於VC++的數據源" ,-1);
  RegDBSetKeyValueEx( szKeyName, "Driver" , REGDB_STRING ,WINSYSDIR+"\odbcjt32.DLL" , -1);
  RegDBSetKeyValueEx( szKeyName, "DriverID" , REGDB_NUMBER , "25" , -1);
  RegDBSetKeyValueEx( szKeyName, "SafeTransactions" , REGDB_NUMBER ,"0" , -1);
  RegDBSetKeyValueEx( szKeyName, "UID" , REGDB_STRING ,"" , -1);
  RegDBCreateKeyEx("Software\\ODBC\\ODBC.INI\\我的數據庫\\Engines","");
  RegDBCreateKeyEx("Software\\ODBC\\ODBC.INI\\我的數據庫\\Engines\\Jet","");
  szKeyName ="Software\\ODBC\\ODBC.INI\\我的數據庫\\Engines\\Jet";
    RegDBSetKeyValueEx (szKeyName, "Driver" , REGDB_STRING ,WINSYSDIR+"\odbcjt32.DLL" , -1);
  RegDBSetKeyValueEx( szKeyName, "ImplicitCommitSync" , REGDB_STRING , "" ,-1);
  RegDBSetKeyValueEx( szKeyName, "Threads" , REGDB_NUMBER , "3" , -1);
  RegDBSetKeyValueEx( szKeyName, "UserCommitSync" , REGDB_STRING , "Yes" , -1);
 end;
  (7)將項目工作區窗口切換到Media頁面,單擊MediaBuild Wizard項,創建新的媒介。
  (8)編譯並運行。

5.4 多個安裝程序
  需要多個安裝程序的情況是比較多的,如"金山詞霸"的安裝。啓動多個安裝程序可就下面兩種情況而有不同的方法。
  1. InstallShield構造的兩個安裝項目
  在這種情況下,用戶首先指定其中一個爲主安裝項目,另一個爲次安裝項目,並且次安裝項目已被編譯過,能完全正確運行。這時,我們按下列步驟進行:
  (1) 啓動InstallShield5.5,將主安裝項目調入。
  (2) 切換到項目工作區窗口的"SetupFiles"頁面,將次安裝項目要發佈的安裝媒介\Dsik1\Disk2...等文件夾下的文件全部調入用戶指定的\目錄項中。
  (3) 在主安裝項目的腳本程序中,添加下列語句:
     DoInstall(SUPPORTDIR ^ "Setup.ins", "", WAIT );
  其中,DoInstall就是用來啓動另一個安裝程序的,其函數原型如下:
     DoInstall(szInsFile, szCmdLine, lWait);
  該函數是用來運行另一個安裝程序。參數szInsFile用來指定用戶要運行的被正確編譯過的腳本文件.ins的文件全名;szCmdLine用來指定InstallShield命令行內容;lWait表示運行的操作方式,它可以是下列值之一
   NOWAIT 兩個安裝程序同時運行
   WAIT 在次安裝程序運行完成後,才進行主安裝項目的下一步操作
  該函數返回1時表示成功調用了次安裝程序,並將流程返回到調用DoInstall的下一個語句中。若InstallShield找到了要調用的安裝腳本文件,但卻不能啓動它,那麼流程仍就被返回,並且此函數返回1。返回-2時表示InstallShield沒有找到要調用的安裝腳本文件。返回其他負數則表示產生不可預知的錯誤。
  (4) 將項目工作區窗口切換到Media頁面,單擊MediaBuild Wizard項,創建新的媒介。
  (5) 編譯並運行。
  需要說明的是,如果被調用的次安裝程序中也有DoInstall的調用,那麼就形成了嵌套,從而可以啓動許許多多的安裝程序。

2. 由第三方提供的安裝程序包
  如果要執行的另一個安裝程序是第三方提供的安裝程序,它可能只有一個可執行的文件。這時就要使用LaunchAppAndWaitLaunchApp函數來啓動,它們的原型如下:
   LaunchAppAndWait(szProgram, szCmdLine, lWait);
   LaunchApp(szCommand, szCmdLine);
  這兩個函數都是用來啓動一個應用程序,它們唯一的區別是LaunchApp函數直到已啓動的應用程序被關閉或中斷後,流程纔會被返回,而LaunchAppAndWait可以立即返回流程。參數szProgramszCommand都是用來指定要啓動的應用程序文件全名,但對於szProgram來說,若應用程序沒有指定路徑和文件擴展名,LaunchAppAndWait不會啓動該應用程序,而對於szCommand來說,若沒有指定應用程序的路徑,LaunchApp將在當前目錄、Windows目錄、Windows系統目錄以及其他PATH環境變量指定的目錄中進行查找。szCmdLine用來指定應用程序執行時的命令行參數,若沒有,則將此參數設爲NULLlWait用來指定流程的返回方式,它可以是NOWAIT(立即返回)WAIT(直到應用程序關閉或中斷纔將流程返回)
   如果需要啓動的應用程序需要的DLL文件不在Windows系統目錄中,則需要調用ChangeDirectory函數來改變調用DLL的目錄,例如下面的過程是通過LaunchAppAndWait函數執行另一個安裝程序:
   (1) 啓動InstallShield5.5,將主安裝項目調入。
   (2) 切換到項目工作區窗口的"SetupFiles"頁面,將次安裝項目要發佈的安裝媒介下的文件全部調入用戶指定的\目錄下。
   (3) 在主安裝項目的腳本程序中,添加下列語句:
  NUMBERnResult;
  STRINGszDir;
  program
   ...
   szDir =SUPPORTDIR;
   StrRemoveLastSlash(szDir);
   ChangeDirectory(szDir);
   LaunchAppAndWait( SUPPORTDIR ^"Setup.exe " , "" ,WAIT);
  endprogram
  (4) 將項目工作區窗口切換到Media頁面,單擊MediaBuild Wizard項,創建新的媒介。
  (5) 編譯並運行。

5.5 安裝程序的調試
InstallShield 5.5開發環境中集成了功能強大的調試工具,利用它們可以調試InstallScript安裝程序,並且能設置和管理斷點、查看全局和局部變量的值等。
  5.5.1 調用前的準備
  在InstallShield中,調試一個安裝程序一般分爲兩步。首先,修正在編譯時產生的不正確的語法和拼錯的關鍵詞等錯誤,直至編譯通過爲止;然後,再用調試器檢測和修正邏輯錯誤以及在循環、判斷等運行時產生的錯誤。
  爲了能有效地進行安裝程序的調試,下面的內容應該要掌握。
  1. 調試器能檢測出的錯誤類型
  InstallShield開發環境集成的調試器能調試出下列三種類型的錯誤:
   1) 語法錯誤
   這是最簡單的一種錯誤類型,通常包括拼寫錯誤、標點符號錯誤、語句結構及其用法錯誤,如if語句中忘記了endif。這類錯誤一般都能被編譯器發現並加以標記。
   2) 運行錯誤
   顧名思義,這類錯誤是在安裝程序運行時產生的錯誤,它通常有幾種表現形式。例如,安裝程序試圖在一個不存在的文件夾中複製文件,內存不夠、目標磁盤已滿或損壞等。
   3) 邏輯錯誤
  這類錯誤要比語法錯誤和運行錯誤更具隱蔽性。例如,假如用戶在腳本中有一條創建路徑的語句,該語句涉及到兩個或以上的變量。一旦創建的路徑不正確,您能確定是哪個變量產生錯誤呢?
  2. 在不同的機器中調試錯誤
  爲了能在不同的操作系統中(Windows 3.1Windows NT3.51)保證安裝程序能正確運行,我們需要在不同的機器中進行調試。通常按下列步驟進行:
  (1) 在安裝有InstallShield5.5的機器中編譯並建立用戶的安裝程序。
  (2) 選擇"Build"->"DebugSetup"菜單命令,在調試器被啓動前,系統會產生Setup.dbg,一時調試器被啓動,立即退出它。
  (3) 在另外一個機器上,複製下列文件:
  媒介文件 \My Installations\\Media\\Disk Images文件中。
  Setup.dbg \MyInstallations\\Script Files文件中。
  Isdbgi51.dll(32Intel) 或
  Isdbga51.dll(32Alpha) 或
  Isdbg51.dll(16)Windows系統文件夾中複製到另一個機器的相同文件夾中
  (4) 在另一個機器中,用DOS模式運行setup -d
  3. 啓動調試器
  一旦一個安裝程序被編譯通過後,就可以用下面兩種方式啓動調試器:
   (1) 選擇"Build"->"DebugSetup"菜單命令;
   (2) 單擊工具欄上的按鈕"DebugSetup"按鈕。

 5.5.2 使用調用器
  調試器啓動後,它還再次地編譯安裝程序,並將斷點停留在安裝腳本程序體中的第一條語句上,如圖5.7所示。
  1. 調試器的界面
  圖5.7InstallShield調試器的全部界面,它通常含有查看窗口、變量窗口、代碼窗口以及相關控制按鈕。
   1) 查看窗口
  查看窗口中往往顯示出一些變量及相應的值,當用戶在該窗口中選定某個變量,按下[Del]鍵將刪除該項,若用戶在變量窗口中選定一個變量後,單擊"查看"按鈕就會將該變量及其內容添加在查看窗口中。
   2) 變量窗口
  變量窗口有兩組控件,分別用於查看全局變量和局部變量的值。開始時,查看全局變量控件組被激活,用戶可以通過Variable組合框選定某個全局變量,控件組右邊的Value編輯框顯出相應的內容。
   3) 代碼窗口
  代碼窗口可兼作編輯器,用來編輯代碼、設置斷點位置以及顯示程序執行的流程等。
   4) 控制按鈕
  控制按鈕用來進行程序調試,各按鈕的功能如表5.4所示。

2. 設置並使用斷點
   調試器啓動後,單擊"Break"按鈕,將彈出如圖5.8所示的"SetBreakpoints"對話框,用來設置斷點。
  在對話框中,"Functions"組合框用來選定安裝腳本程序中可能出現的函數作爲斷點的位置,單擊[Set]按鈕將此函數所在的位置設置爲斷點。單擊[Clear]將在"CurrentBreakpoints"列表中選定的斷點刪除,單擊[ClearAll]按鈕將所有的斷點清除,單擊[Close]按鈕關閉"SetBreakpoints"對話框。
  實際上,斷點也可通過鼠標來設置,這種方法最爲直觀、隨意,其操作步驟如下:
   (1) 用鼠標將代碼窗口移動至用戶滿意的地方,並使代碼窗口變大些。
   (2) 此時的代碼窗口就像是一個編輯器窗口。通過代碼窗口的滾動條,用戶可瀏覽安裝腳本程序代碼,如圖5.9所示。
  (3) 在想要設置斷點的地方雙擊鼠標,此時斷點所在的行變成了紅色,參見圖5.9
  (4) 若想清除剛纔設置的斷點,只需將鼠標指針移至斷點所在行,然後雙擊鼠標即可。
  (5) 再將代碼窗口移走,使"控制按鈕"所在的窗口不被代碼窗口所遮擋。
  3. 查看變量
  查看安裝腳本程序中的全局和局部變量比較簡單容易,只需通過"變量窗口"的上下兩組控件進行相關操作即可。由於前面已論述過,這裏不再重複。但需要注意的是,內建函數的返回值是通過查看全局變量LAST_RESULT來得到的。

5.5.3 減少錯誤產生
  對於一般的代碼錯誤,用戶可以通過編譯器和調試器就可解決。但是,由於InstallShield集成開發環境在運行過程中還會產生一些代碼以外的錯誤,因此下面的一些建議值得參考。
  1. 使用系統的磁盤掃描程序
   Windows系統中的"磁盤掃描程序"不僅可以檢查硬盤的邏輯和物理錯誤,而且還可以修復已損壞的區域。爲了保證InstallShield運行穩定性,最後在安裝或啓動InstallShield前先進行磁盤掃描。在Windows98中,啓動"磁盤掃描程序"是通過選擇"開始"->"程序"->"附件"->"系統工具"->"磁盤掃描程序"菜單命令而進行的。
  2. 關閉反病毒程序
  一個反病毒程序往往對用戶的程序安裝有一定的影響,大多數反病毒程序通常會不讓程序向硬盤根扇區寫數據以及保護某些EXEDLLCOM文件不被修改。由於反病毒程序不能判斷哪些修改是有用的以及哪些修改是有害的,因而在安裝程序前最好關閉反病毒程序。
  關閉時,可採用下列步驟:
  (1) 若只有病毒監控程序,則只要關閉它即可進行程序安裝。但若反病毒程序是駐留在內存中,則需要按下面步驟進行。
  (2) 用文件編輯器打開根目錄下的"Autoexec.bat"
  (3) 將所有與反病毒程序相關的命令行前面加入"REM"
  (4) 保存Autoexec.bat
  (5) 重複前面的操作,將Config.sys文件作同樣修改。
  (6) 重新啓動計算機。
  3. 錯誤分析
  下面幾個措施可幫助用戶查找錯誤產生的根源:
  (1)InstallShield是否只安裝過一次?若是,再重新安裝一次,如果問題仍然存在,有條件的話,再在其他機器中進行多次安裝。若問題仍然存在,可排除是InstallShield開發環境產生錯誤的可能性。
  (2) 問題是否和發佈的媒介有關?若是,試試在3.5"軟盤和CD-ROM兩個媒介上同時發佈安裝程序,這樣可幫助用戶縮小錯誤產生的範圍。
  (3) 只要有可能就使用品牌好的媒介,這樣就可排除媒介本身產生錯誤的可能性。
  (4) 試試在多個操作系統環境中進行調試,若錯誤只發生在其中某個系統中,只有能找出該系統和其他系統的差別,就可找出錯誤產生的原因。
  (5) 儘可能將系統佔用的資源釋放出來,並保證內存足夠多,這樣可排除由於資源限制所產生的錯誤的可能性。InstallShield最小需要2MB的內存,若還要調用其他腳本程序以及大有位圖文件,則還需要更多的內存。
  事實上,安裝的深入話題還有很多,例如網絡安裝、其他應用程序的安裝等。需要說明的是,最新的InstallShield軟件包中還含有ExpressInstallFromTheWebPackageForTheWebJavaEditionApplicationRepackager等軟件,在附錄中將對這些內容作簡單介紹。

 

附錄A 使用InstallShield系統變量的缺省值
  InstallShield安裝在D盤,Windows系統安裝在C盤,測試的安裝項目是第一次創建的BlankSetup,則InstallShield系統變量的缺省值如下:
BATCH_INSTALL 0*
COMMONFILES C:\Program Files\Common Files\
ERRORFILENAME --
FOLDER_DESKTOP C:\WINDOWS\Desktop\
FOLDER_PROGRAMS C:\WINDOWS\Start Menu\Programs\
FOLDER_STARTMENU C:\WINDOWS\Start Menu\
FOLDER_STARTUP C:\WINDOWS\Start Menu\Programs\
啓動\
HINST_INSTALL 0*
INFOFILENAME --
ISRES C:\WINDOWS\TEMP\_ISTMP0.DIR\B3F9AB.DLL*
ISUSER C:\WINDOWS\TEMP\_ISTMP0.DIR\_ISUSER.DLL*
ISVERSION 5.50.136.0
LAST_RESULT 0*
LOGHANDLE 0*
MEDIA DATA
PROGRAMFILES C:\Program Files\
SELECTED_LANGUAGE 9
SRCDIR C:\My Installations\BlankSetup\Media\Default\DiskImages\disk1\
SRCDISK C:
SUPPORTDIR C:\WINDOWS\TEMP\_ISTMP0.DIR\*
TARGETDIR C:\WINDOWS\*
TARGETDISK C:*
UNINST C:\WINDOWS\ISUNINST.EXE
WINDIR C:\WINDOWS\
WINDISK C:
WINSYSDIR C:\WINDOWS\SYSTEM\
WINSYSDISK C:
  注:凡是後面有星號(*)的值在不同的安裝項目或安裝進程中可能會有不同。

 

附錄B 使用InstallShield for VisualC++6.0
InstallShield for Microsoft Visual C++6(簡稱InstallShieldVC)是隨MicrosoftVisual Studio 98一起發行的,雖是一個免費版本,但它的功能仍比其他安裝工具軟件略勝一籌,因爲它是InstallShield專業版5.1的子集,具備了InstallShield的基本功能及相同的集成開發環境;而且令人驚喜的是,InstallShield的腳本語言InstallScript使得用戶可以像其他高級語言那樣靈活地構造出自己的安裝腳本程序。但它與相應的InstallShield5.5專業版有許多不同,主要表現在以下幾個方面。
1.開發環境
  (1) VC版不能創建16Windows系統的安裝程序。
  (2) VC版不能創建自釋放的EXE安裝程序包。
  (3) VC版創建的組件不能超過5個,且不能創建子組件,而專業版不受限制。
  (4) VC版沒有組件嚮導。
  (5) VC版創建的文件組不能超過8個,而專業版不受限制。
  (6) VC版使用的文件壓縮方式同InstallShield3.0相同。
  (7) VC版不允許定義DLL函數。
  (8) VC版在創建的安裝程序中,啓動消息對話框不能更改也不能隱藏。
  (9) VC版不會報告媒介創建的結果。
  (10) VC版不能更改用戶對話框左側的位圖。
2InstallScrip功能
  nstallShieldVC版的腳本語言在函數功能上有一些限制,如表1所示。
3. 使用InstallShieldVC
   InstallShieldVC版的開發環境和InstallShield專業版5.1是一樣的,這裏不再重複。由於該InstallShield是專門爲MicrosoftVisual C++6定製的,所以從VisualC++6開發環境中直接運行InstallShield更快捷方便。
  當用戶用VisualC++6調試好應用程序後並編譯成Release版的EXE文件,就可選擇"Tools"菜單中"InstallShieldWizard"命令,執行下列過程(Visual C++單文檔應用程序MySDI爲例)
  (1) 首先,出現如圖1所示的"Welcome"對話框,要求用戶選定一個VisualC++6.0的應用程序項目(.dsw爲擴展名)。單擊[Browse...]按鈕可在磁盤中進行查找。
  (2) 單擊[下一步]按鈕,出現如圖2所示的"ApplicationInformation"對話框,要求用戶輸入應用程序名稱、公司名稱、應用程序的類型、版本號以及應用程序的可執行文件。單擊Browse按鈕("..."符號的按鈕)可將磁盤中已有的應用程序的可執行文件名調入。
 (3) 單擊[下一步]按鈕,出現如圖3所示的"Summary"對話框,顯示該安裝項目中的文件及文件組信息。
  (4) 單擊[完成]按鈕,安裝項目MySDI就創建好了,並自動啓動InstallShieldfor Microsoft Visual C++6
  (5) 利用InstallShield開發環境進行更深的安裝項目的操作。
  需要說明的是,由於InstallShieldfor Microsoft Visual C++6正確安裝後,會自動在Visual C++6.0(必須先安裝)"Tool"菜單下添加一個名爲"InstallShieldWizard"菜單命令,該命令是用來爲一個VisualC++6.0的應用程序創建安裝項目的。InstallShield安裝前沒有安裝VisualC++6.0,則當IntallShield安裝後,進行下列設置以便能在VisualC++6.0"Tool"菜單中使用"InstallShieldWizard"菜單命令:
  (1) VisualC++6.0正確安裝後,啓動VisualC++6.0,並選擇"Tools"菜單->"Customize"命令。
  (2) 在彈出的"Customize"對話框中,切換到"Tools"頁面,如圖4所示。
  (3) 將菜單列表項滾動到最後一個空行,並雙擊鼠標,鍵入"&InstallShieldWizard",並按Enter鍵。
  (4) 選定剛纔鍵入的菜單列表項,單擊Browse按鈕("..."符號的按鈕)InstallShield所在的Program文件夾下的IsVcWiz.exe調入。
  (5) "Initialdirectory"的路徑設爲IsVcWiz.exe所在的路徑。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章