1. 程式人生 > >一個完整的Installshield安裝程式例項

一個完整的Installshield安裝程式例項

前言

Installshield可以說是最好的做安裝程式的商業軟體之一,不過因為功能的太過於強大,以至於上手和精通都不是容易的事情,之前都是用Installshield的Project Assistant對付過去的,這次做這個安裝程式,為了實現一些功能,必須寫程式碼,國內外現成的資料很少,而且很多都語焉不詳,自己反覆啃了多次,對比Installshiel自帶的help,才明白資料所表達的意思。這個安裝程式雖然比較簡陋,在行家眼裡可能是小菜一碟,但是也花了筆者一個星期的時間,閱讀了很多資料,啃了好幾天英文help,集成了很多先驅者的經驗,也費了自己不少心血做成的,對每一段程式碼的用處、每一個用到的函式都進行了詳細的說明,因此轉載時請務必保留轉載出處和由艾澤拉斯之海洋女神出品的字樣;如需刊登,請與作者聯絡。

在此要感謝吞硬幣的小豬,天下曉明,餘滿青,海洋C++樂園(此海洋不是彼海洋)等大蝦在網際網路上的無私奉獻,他們的貼子和部落格給了我很大啟示。

因為本人是做java出身的,因此對這種類C++語言還是第一次接觸,有理解不當之處,請朋友們指正。歡迎Email至[email protected]

正文

需求:公司做了一個軟體產品,

1.         該軟體執行需要JDK環境(不是JRE,因為該軟體要向windows註冊一個服務,用到了JavaService,JDK才支援這個功能;不過這裡側重於判斷是否安裝了某軟體是否安裝,而不是糾纏於該裝JDK還是該裝JRE);

2.         由於是Server-Client形式的,需要允許使用者選擇安裝元件,比如A機只裝Server端,B機只裝Client端;

3.         文件不打包在安裝程式裡,直接存放在光碟資料夾下方便使用者檢視,同時允許使用者指定是否安裝文件到計算機上(為什麼這樣做,後面說明詳細原因);

4.         該軟體會以授權形式發放給使用者,不同的使用者,軟體本身可能相同,而不同的只是授權檔案和一些配置,因此希望授權檔案和配置檔案不打包在安裝程式裡,而直接存放在光盤裡,以減少可能的重複打包安裝程式的勞動;

5.         在安裝完畢後,希望能自啟動程式(因為該軟體需要在安裝完畢後啟動一個程式,該程式實現向Windows註冊服務的功能,該程式最好由安裝程式啟動,而不是由客戶手動啟動)。

6.         希望有反安裝程式

本文提到的“外部”指不打包在安裝程式裡的,與安裝程式一起存放在光盤裡的一些資料夾,這些資料夾包含了安裝中所需要的檔案,同時也可能有其他用途,因此不適合直接壓縮打包在安裝程式裡。

該例項實現瞭如下功能:

1.         顯示軟體許可協議

2.         判斷是否安裝了本軟體所需要的先決軟體JKD1.6.0_04,如無,則啟動外部安裝程式進行安裝(同樣原理可以用來判斷是否安裝了其他軟體,只要該軟體在登錄檔中有鍵值)

3.         安裝允許使用者選擇需要安裝的元件

4.         使用者的輸入資訊、所選安裝路徑、所選安裝元件將顯示在安裝介面上(Installshield雖然自帶了此介面,但是預設是顯示為空的,需要寫指令碼來顯示資訊)

5.         根據使用者選擇的元件,在開始選單顯示程式的快捷方式(同樣適用於桌面快捷方式,後面做詳細說明)

6.         根據使用者選擇的元件,從外部資料夾拷貝相應的檔案到安裝目標路徑的資料夾中

7.         根據從外部拷貝進來的檔案,建立快捷方式(這裡主要是拷貝文件,並在開始選單中建立快捷方式)

8.         在安裝結束時,顯示readme.txt檔案

9.         在安裝結束後,啟動指定的程式

10.     完美解除安裝

筆者所用的環境為Installshield 12 Premier Edition,Windows XP with SP2, 該環境下建立的工程可以直接使用在Installshield 2008 Premier Edition下,Installshield 2008在開啟Installshield 12所建的工程時會提示你是否需要進行Upgrade,確認即可,軟體會自動為你進行升級,很方便。

下面我們一步一步來建立一個基本的工程,並且使用指令碼來完善和豐富所需功能

1.開啟Installshield 12 Premier Edition,新建一個Installscript MSI Project,這種被稱之為半指令碼程式,因為兼具Basic Project基本型別和Installscript Project全指令碼型別兩者的優點,我比較喜歡用。像我這樣需求的,既要用到Wizard的便利,又想寫一點指令碼來實現一點自定義操作的,就比較適合用這種型別啦。

選擇型別為Windows Installer | InstallScript MSI Projcet,輸入工程名,指定工程所在的資料夾。

2.介面會切換到Project Assistant,我們先從這裡開始把工程的基本元件和基本檔案建立好。

3.Project Assistant介面的底部,會有一個引導動作條,在建立該工程的基本結構和檔案時,我們都將在此介面進行操作,下文都將以“引導條”來指代這個引導動作條。

 

4.點選引導條上的Application Information

這裡輸入:

公司名,公司名將會出現在Setup.exe的註解中

軟體名,將會出現在安裝過程的左上角標題欄上

版本號,沒看到在哪,不過自己比較方便地知道自己在編譯哪個版本的軟體

公司網址,沒看到在哪,而且如果該公司沒有網址呢?而且這裡有點bug,好像預設的值總是會報一個String_ID1為空的錯誤,自己輸入一個網址就不會報錯。

是否在你建立了更新時自動通知終端使用者,沒用過,我都選了No

選擇一個圖示,這個圖示會出現在“新增或刪除程式”裡,我一般用預設的,當然你可以替換成自己想要的圖示。

5.點選引導條上的Installation Requirement

這裡選擇對作業系統和一些軟體的需求。根據自己需要來選擇是否要求作業系統的版本,已經是否要求安裝了某些軟體。

6.點選引導條上的Installation Architecture

這是個十分有用的設定,對於本文所用的分散式軟體來說非常合適,分散式軟體的每個元件可以設定為一個Feature,使用者可以自由選擇安裝某些功能。

將選項Do you want to customize your Installation選擇為Yes

點選選中根節點Installation Architecture,點選New建立新的Feature,可以為每個Feature指定新名稱。

還可以在Feature下建立子Feature,比如如果文件Feature下包括軟體本身文件,和軟體所需的執行環境的文件,那麼可以建立兩個子Feature,分別包含兩種文件,使用者在安裝時就可以選擇安裝部分或者全部文件了。這裡我們沒有用到子Feature,用途和普通Feature一樣。

這裡,建立好所有Feature後,我們將切換到Installation Designer做一個設定

找到Installation Designer頁面上左邊導航樹Organization | Features分支,你會看到這裡Features都顯示為原始的名稱,而非我們改過的名字,因為FeatureNameDisplay Name兩種名稱,我們剛才改的不過是Display Name,為了便於檢視和使用,我們在這裡把Name也改一下

注意Name不可以有空格,可以使用下劃線

繼續切換回Project Assistant


我們將在這裡對安裝路徑進行微調,並且為每個Feature指定需要安裝的檔案

clip_image002

這裡我不想使用Program Files | Company Name | Product Name這個路徑,我想使用Program Files | Product Name,我直接點選選中My Product Name[INSTALLDIR]拖動到ProgramFileFolder下,還可以直接將My Product Name 改成自己想要的資料夾名字

clip_image004

接下來,為每個Feature指定要安裝的檔案。

clip_image006

開啟這個下拉列表,所有的Feature都在這裡,按順序來給每一個Feature建立資料夾,並且匯入所需的檔案。

選擇第一個Feature, 即Server,點選My Product Name[INSTALLDIR]節點,右鍵點選,在選單上選擇New Folder來建立一個資料夾。

clip_image008

建立一個Server資料夾,這個資料夾將用來存放該元件需要的一些檔案。

再在Server資料夾下建立一個icon資料夾,存放該元件所用的圖示。

clip_image010

然後為該Feature新增安裝時該Feature要安裝的檔案。

這裡我們建立的icon資料夾是用來存放這個feature在後面要建立快捷方式時使用的圖示的。為這個icon資料夾新增相應的圖示檔案,並且記住圖示檔案的來原始檔夾,後面設定快捷方式的時候要用。

clip_image012

點選選中要新增檔案的資料夾,然後點選右下角的Add Files,然後新增檔案

clip_image014

接下來我們為Feature新增資料夾,如果這個資料夾中的全部檔案都為這個Feature所需。新增資料夾的好處在於只要資料夾位置和名稱不變,那麼資料夾裡面的檔案都是動態載入的,有多少載入多少,不用考慮檔名的改動帶來的影響。

點選選中要新增資料夾的資料夾,然後點選右下角的Add Folders,然後新增資料夾。

clip_image016

選中資料夾,點選確定。

clip_image018

會詢問你是否要使用動態檔案連結,我都選擇確定,好處就在於我剛才上面所述。

clip_image020

顯示了原始檔夾,如果這個資料夾下有子資料夾,並且也需要一併新增進來的話,務必鉤選Include subfolders選項。

這裡還允許做一些簡單設定來包含或者排除一些特定檔案,支援萬用字元。

點選OK確定加入資料夾。

clip_image022

如法炮製為每個Feature建立資料夾,並且新增檔案,最後效果如圖所示

clip_image024

Document這個Feature,除了檔案所用的圖示外,什麼都不要新增,後面我們將用安裝時實時拷貝的方式來拷貝文件進來。

clip_image026

8. 接下來我們為可執行檔案建立快捷方式。

點選引導條上的Application Shortcuts

clip_image028

點選New新建一個快捷方式

clip_image030

選擇一個要建立快捷方式的Feature。

如果要建立快捷方式的程式為非.exe形式,請把Files Of選擇選為All Files(*.*)格式。

我們的程式安裝目標路徑設定在Program Files下,因此雙擊[ProgramFilesFolder]開啟,層層點選進入。

clip_image032

我們這裡要為client.bat建立一個快捷方式,因為這個是啟動用的批處理檔案。

Installshield可以自動監測到.exe檔案的存在,自動生成快捷方式,使用者只需要做一些適當修改即可。

clip_image034

新建的快捷方式將出現在這裡,名字不好聽,樣子也不好看,我們將為它改一個名字,並且換一個圖示。

選中快捷方式,點選Rename,並且為這個快捷方式改一個適當的名字。

clip_image036

注意右邊的幾個選項。

Create shortcut in Start Menu,將在開始選單裡建立一個快捷方式。

Create shortcut on Desktop,將在桌面上建立一個快捷方式。

Use alternate shortcut Icon,替換快捷方式的圖示

Associate a file extension with the shortcut’s target,沒用過,不知道什麼意思。

我們在這裡將只建立開始選單的快捷方式,因此鉤選第一項。

鉤選第三項,並且點選Browse來瀏覽圖示。

clip_image038

請回想剛才在為Feature新增檔案的時候,每個feature都添加了對應的icon。這裡,請把瀏覽的資料夾設定為剛才新增icons所用的資料夾,通俗的說,就是你剛才從哪兒新增一個圖示進feature的,現在還是從哪兒新增的這個圖示。

其實這一點我是一直很費解的,當初不知道要這麼選擇圖示,隨便從外面一個任意資料夾裡添加了一個圖示,以至於打包後死活找不到圖示,後來經過試驗才知道這個被選中的圖示檔案要拷貝進來,打包進安裝檔案才可以。這一點上不能不提一下visual studio,這個工具做安裝程式雖然功能一般,但是思想還是不錯的,當它的元件指定拷貝了圖示檔案後,在建立快捷方式時,快捷方式使用的圖示是指向虛擬的安裝目標路徑下的圖示檔案的,而不是指定到這個實實在在的原始檔夾。這一點差別就體現出了思想上的差異。

如法炮製為每個Feature指定快捷方式,Document除外,因為我們在這個feature裡除了圖示檔案外什麼都沒有新增。

clip_image040

至此我們為每個可執行程式添加了開始選單下的快捷方式。

我們再切換去Installation Designer,找到System Configuration | Shortcuts。

看到快捷方式在開始選單中是以 公司名 | 軟體名 | 快捷方式   這種形式存在的。事實上我是不喜歡這種形式了,想想點開一層還有一層,不如直接了當來得乾脆,因此做一些修改。

clip_image042

這裡我改成了如下設定

clip_image044

不要告訴我你不會改,直接拖動Test資料夾往Program Menu(即開始選單下的那個“所有程式”)下一塞即可,然後刪除掉多餘的Company Name資料夾。

9. 可能剛才在Project Assistant介面有人已經注意到了左邊欄上More Options下Create an uninstallation shortcut這個誘人的字樣了。

clip_image046

可是我要告訴你,如果你選擇了這種方式建立解除安裝快捷方式的話,你會很沮喪地發現:

a) 似乎只有在安裝某個feature的時候這個解除安裝快捷方式才會出現(當然,就是那個default feature,這種要命的feature形式決定了每個檔案或者快捷方式都必須明確地歸屬到某個feature下),因此,當你的客戶只選擇了其他feature安裝時,這個解除安裝方式不會出現,而他必須去“新增或解除安裝程式”裡面去解除安裝

b) 如果你寫指令碼使得安裝時會拷貝一些外部檔案進來,那麼這些檔案在這種解除安裝方式下是刪除不掉的。(如果你確實想儲存這些檔案,你可以在腳本里設定它們屬性為permanent,這個屬性可以保證什麼解除安裝方式都不能刪除你的這些檔案)。

所以這裡我們忽視這個解除安裝快捷方式的存在,而將在後面採用指令碼形式實現完美解除安裝。

10. 點選引導條上的Application Registry

向登錄檔寫鍵和鍵值,由於本工程不需要,忽略之。有需要的朋友可以查閱相關資料,不難。

11. 點選引導條上的Installation Location

這個是用來設定安裝包的語言的,選擇了多個語言後,使用者可以在安裝介面開始的時候選擇安裝時所用的語言;不過作為一個公司產品來說,這麼偷懶,客戶的印象是要打折扣的,所以還是選個單語言吧,該什麼語言的安裝包就什麼語言的安裝包,各歸各。

不過你又會沮喪地發現,如果要選擇一種其他語言作為Default Language,好像又報錯了。

這個問題當時折騰了我一個星期(當然那時候才接觸Installshield,還一竅不通),最後問了技術支援才得以解決。

切換去Installation Designer,找到Installation Information | General Information,看到String Tables下面是什麼?對,所有你選的語言都列出來了,選中你要的語言,右鍵,選擇Make Default,OK,再切換回Project Assistant去把所有不要的語言統統去掉鉤選即可。 

clip_image048

看到此處,已經變成了English為預設語言了。

clip_image050

12. 點選引導條上的Build Installation。

打包安裝盤的設定,本人從來不用這個選項,都用工具條上的Release Wizard。

至此,第一部分基本完成。如果是一些沒有特別要求的安裝包,這部分講解的內容足夠可以做一個基本的安裝包了 

在開始進行程式設計前,我們先明確一下我們要用程式設計來彌補前面設定的哪些功能的不足

1. 顯示軟體許可協議

2. 判斷是否安裝了本軟體所需要的先決軟體JKD1.6.0_04,如無,則啟動外部安裝程式進行安裝(同樣原理可以用來判斷是否安裝了其他軟體,只要該軟體在登錄檔中有鍵值)

3. 使用者的輸入資訊、所選安裝路徑、所選安裝元件將顯示在安裝介面上(Installshield雖然自帶了此介面,但是預設是顯示為空的,需要寫指令碼來顯示資訊)

4. 根據使用者選擇的元件,從外部資料夾拷貝相應的檔案到安裝目標路徑的資料夾中

5. 根據從外部拷貝進來的檔案,建立快捷方式(這裡主要是拷貝文件,並在開始選單中建立快捷方式)

6. 在安裝結束時,顯示readme.txt檔案

7. 在安裝結束後,啟動指定的程式

8. 完美解除安裝

指令碼程式設計這部分都將在Installer Designer這個介面進行。後面不再贅述。

Installshield大小寫敏感,因此請嚴格按照示例上所寫的大小寫規則來書寫。例:字串變數STRING和string都支援,但是String不支援。

1. 新增許可協議文字

在左邊導航樹上找到Behavior and Logic | Support Files/Billboards選項。這個選項允許使用者新增一些在安裝過程中需要用到的檔案。

clip_image002

中間的導航欄會顯示對應的選項

clip_image004

在Support Files分支下,會顯示一個Language Independent和所有你所選擇的語言型別。 Language Independent意為,如果你在這裡分支下做了設定,那麼無論選擇用何種語言安裝,這個設定都會生效;而各個語言型別意為,如果你在某語言下做了設定,那麼這個設定只有在選擇了用這種語言安裝的時候才會生效。

點選Language Independent,這次我們將在這個分支下進行試驗。

clip_image006

在右邊的Files欄中右鍵點選,在彈出選單上選擇Insert Files選項。

clip_image008

選擇事先撰寫好的許可協議的文字檔案,插入到Files欄中。

許可協議允許兩種文字格式:txt和rtf格式,此處我們採用 txt格式。

2. 然後切換到Behavior and Logic | InstallScript選項,

clip_image010

3. 中間的導航欄Files下有一個預設的Rul檔案Setup.Rul,我們這個工程的全部installscript程式碼都將寫在這個預設檔案裡

clip_image012

4. 點選選中Setup.Rul節點,右邊會顯示該檔案的可程式設計面板。

5. 許可協議應該在一開始執行安裝程式的時候就顯示,也就是在拷貝資料前。請在第一個下拉框中選擇Before Move Data選項,然後在第二個下拉框中選擇OnBegin選項(不要因為預設顯示的是這兩個選項,而不做這個開啟下拉列表進行選擇的動作,否則軟體檢測不到你選擇了選項,無法自動新增程式碼),則程式設計介面上會自動新增一些程式碼如下圖所示。當然,如果你手動敲程式碼上去也是可以的。

clip_image014

6. 我們將在function OnBegin()的函式體裡面寫程式碼來顯示剛才新增的許可協議文字的內容,直接把下面的程式碼拷貝到OnBegin()函式的begin和end;之間就可以了

Disable (BACKBUTTON);

if(!MAINTENANCE)then

SdLicense2 ("License ", "", "", SUPPORTDIR ^ "2.txt", FALSE);

endif;.

7. 程式碼解釋

************************************************************************

Disable (BACKBUTTON);

將“上一步”按鍵設定為不可用。安裝程式在一開始的時候會有一個預設的開始介面,第二步才顯示許可協議,一般來說沒必要回退回去看這個什麼都沒有的開始介面,因此將回退按鍵設定為不可用

************************************************************************

if(!MAINTENANCE)then

endif;

這一個條件用來判斷安裝程式處於何種狀態,安裝、修復、重新安裝或解除安裝狀態,後三者都屬於MAINTENANCE狀態,因此判斷只有在正常安裝的狀態才顯示許可協議

************************************************************************

SdLicense2 ("License ", "", "", SUPPORTDIR ^ "2.txt", FALSE);

這個函式用於在介面上顯示所用的許可協議。Help裡對該函式的建構函式如下

SdLicense2 ( szTitle, szOpt1, szOpt2, szLicenseFile, bLicenseAccepted );

引數一:szTitle,顯示在介面左上角的標題,如果填寫空字串””,則顯示為預設值”License Agreement”。

引數二:szOpt1,我們常見許可協議介面上會有兩個選項,一個是“同意”,一個是“不同意”,szOpt1和szOpt2就是這兩個選項,如果填寫空字串,則會顯示為預設值"I accept the terms of the license agreement"和"I do not accept the terms of the license agreement"。

引數三:szOpt2,見引數二的說明

引數四:szLicenseFile,指定需要顯示的文件,包含路徑和帶副檔名的文件名。我們剛才把許可協議文字放在supportfile選項下了,這個路徑在Installshield裡有專門的靜態變數來指明,即SUPPORTDIR,然後再新增上帶副檔名的文件名,這裡是2.txt。靜態變數路徑和引號引起來的路徑之間用^符號來連線。

引數四:bLicenseAccepted,布林型變數,TRUE狀態,則在許可協議介面上預設選中的是那個“同意”的選項;不過好像一般更常見的是預設選中為“不同意”的選項,因此這裡可以填入FALSE。

clip_image016

這是許可協議的介面。當用戶選擇了I accept the terms of the license agreement這個選項後,Next按鍵可用,安裝程式可以繼續。(請忽略這裡顯示的許可協議內容…網上有很多軟體許可協議的範本供下載...)

小結:至此,許可協議就新增完畢,在安裝執行的時候,使用者就可以看到許可協議顯示在介面上,並且只有選擇了“同意”選項後,安裝程式才會往下執行。

顯示許可協議的函式一共有三個SdLicense,SdLicenseRtf和SdLicense2,引數略有不同,顯示的介面也略有不同,使用者可以根據喜好來選擇。目前我常用的就是SdLicense2這個函式,顯示的介面符合大多數目前流行的安裝介面的習慣。

1. 程式碼還是在OnBegin()函式體內實現,直接把下面的程式碼拷貝到OnBegin()函式的begin和end;之間就可以了

RegDBSetDefaultRoot(HKEY_LOCAL_MACHINE);

if (RegDBKeyExist ("SOFTWARE\\JavaSoft\\Java Development Kit\\1.6.0_04") < 0) then

LaunchAppAndWait (SRCDISK^"jdk\\jdk-6u4-windows-i586-p.exe","", LAAW_OPTION_WAIT);

endif;

2. 程式碼解釋

************************************************************************

RegDBSetDefaultRoot(HKEY_LOCAL_MACHINE);

設定一下預設的登錄檔鍵值根節點為HKEY_LOCAL_MACHINE。

開啟登錄檔可以看到“我的電腦”下的根節點有HKEY_CLASSES_ROOT, HKEY_CURRENT_USER,HKEY_LOCAL_MACHINE等。我們這次要尋找的JDK軟體的登錄檔鍵值在HKEY_LOCAL_MACHINE下,因此要把根鍵設定為HKEY_LOCAL_MACHINE。

表告訴我你不知道怎麼看登錄檔,開始-〉執行-〉輸入命令regedit

***********************************************************************

RegDBKeyExist ("SOFTWARE\\JavaSoft\\Java Development Kit\\1.6.0_04") < 0)

判斷是否存在鍵值SOFTWARE\\JavaSoft\\Java Development Kit\\1.6.0_04,這個是JDK1.6.0_04安裝時向登錄檔寫入的值;

RegDBKeyExist( szSubKey );如果存在鍵值則返回1,否則返回小於0的隨機數字。

***********************************************************************

LaunchAppAndWait (SRCDISK^"jdk\\jdk-6u4-windows-i586-p.exe","", LAAW_OPTION_WAIT);

當上面判斷了沒有安裝JDK1.6.0_04這個軟體時,則啟動光盤裡jdk資料夾下的jdk-6u4-windows-i586-p.exe安裝程式來安裝。

這個函式在help裡是這樣敘述的:

LaunchAppAndWait ( szProgram, szCmdLine, nOptions );

引數一:szProgram,即要啟動的程式。這裡我們寫入的引數是SRCDISK^"jdk\\jdk-6u4-windows-i586-p.exe", SRCDISK指源盤,安裝程式所在的盤,光碟和硬碟都可以。"jdk\\jdk-6u4-windows-i586-p.exe"源盤下jdk資料夾下的jdk-6u4-windows-i586-p.exe安裝程式。

引數二:szCmdLine,如果要啟動的程式需要從命令列讀入引數來啟動,那麼在這裡寫入對應的引數值;我們這裡不需要,因此輸入空字串””。

引數三:nOptions,靜態變數,不同的靜態變數會得到不同的執行結果,比如無等待安裝,靜默安裝,滑鼠外形改變等等。詳情請參閱Installshield自帶的Help。這裡我們用LAAW_OPTION_WAIT,即當JDK安裝結束後(無論是正常安裝了,還是使用者點選取消了安裝),安裝程式才往下繼續。

clip_image018

這裡可以看到,當點選了同意許可協議的時候,安裝程式會自動檢測是否安裝了JDK,如果沒有安裝,則彈出安裝介面。

這裡在函式體裡面,沒有對找不到JDK安裝程式,以及安裝出錯等情況做判斷。如果使用者有需要,可以新增一個訊息框,提示在找不到安裝程式或者安裝出錯的情況下,使用者可以手動地安裝需要的軟體。程式碼可以改寫為

RegDBSetDefaultRoot(HKEY_LOCAL_MACHINE);

if (RegDBKeyExist ("SOFTWARE\\JavaSoft\\Java Development Kit\\1.6.0_04") < 0) th