黄色网页视频 I 影音先锋日日狠狠久久 I 秋霞午夜毛片 I 秋霞一二三区 I 国产成人片无码视频 I 国产 精品 自在自线 I av免费观看网站 I 日本精品久久久久中文字幕5 I 91看视频 I 看全色黄大色黄女片18 I 精品不卡一区 I 亚洲最新精品 I 欧美 激情 在线 I 人妻少妇精品久久 I 国产99视频精品免费专区 I 欧美影院 I 欧美精品在欧美一区二区少妇 I av大片网站 I 国产精品黄色片 I 888久久 I 狠狠干最新 I 看看黄色一级片 I 黄色精品久久 I 三级av在线 I 69色综合 I 国产日韩欧美91 I 亚洲精品偷拍 I 激情小说亚洲图片 I 久久国产视频精品 I 国产综合精品一区二区三区 I 色婷婷国产 I 最新成人av在线 I 国产私拍精品 I 日韩成人影音 I 日日夜夜天天综合

一個(gè)完整的Installshield安裝程序?qū)嵗瑵衫?/h1>
系統(tǒng) 2755 0

上一篇: 一個(gè)完整的安裝程序?qū)嵗瑵衫怪Q笈癯銎罚ㄈ?--高級(jí)設(shè)置一

4. 根據(jù)用戶選擇的組件,從外部文件夾拷貝相應(yīng)的文件到安裝目標(biāo)路徑的文件夾中

這個(gè)用途常見于配置文件和授權(quán)文件的應(yīng)用,同一程序,授權(quán)給不同的用戶,只需要不同的配置和授權(quán)文件。如果將配置和授權(quán)文件每次都打包在安裝程序里,那么變更一個(gè)用戶就需要重新打包一次,這是一個(gè)浪費(fèi)時(shí)間和精力的行為。如果將授權(quán)和配置文件(當(dāng)然內(nèi)容是加密過的)放在外部文件夾中,每次安裝的時(shí)候從這個(gè)文件夾中讀取拷貝,那么會(huì)是一個(gè)比較通用型的安裝程序。

另外,本程序的好幾個(gè)feature用到了相同的庫,如果直接在feature下加庫文件也可以,但是每一個(gè)feature都加一次這個(gè)庫文件夾,整個(gè)安裝程序就會(huì)變得很龐大,因此比較理想的情況是選到了這個(gè)feature的時(shí)候從外部拷貝這些庫文件。

這里我們先不包括文檔這個(gè)feature的說明,文檔feature另有詳細(xì)說明。

1. 這個(gè)功能需要在OnFirstUIAfter()函數(shù)體中實(shí)現(xiàn),選擇After Move Data | OnFirstUIAfter選項(xiàng),即在選擇了移動(dòng)哪些數(shù)據(jù)后這個(gè)操作生效。

clip_image002

2. 之前我們已經(jīng)接觸過了如何判斷是否選擇了某個(gè)Feature,這里也需要判斷是否選擇了某個(gè)Feature,并且根據(jù)這個(gè)Feature來拷貝對應(yīng)的外部文件

首先定義一些需要的變量并且進(jìn)行賦值,藍(lán)色字體即為所定義變量和賦值語句

function OnFirstUIAfter()

//feature name

STRING szFeatureName1;

STRING szFeatureName2;

STRING szFeatureName3;

STRING szFeatureName4;

STRING szFeatureName5;

STRING szSrcFile1;

STRING szSrcFile2;

STRING szTarFolder1;

STRING szTarFolder2;

NUMBER nResult;

STRING szTitle, szMsg1, szMsg2, szOption1, szOption2;

NUMBER bOpt1, bOpt2;

begin

//feature 定義

szFeatureName1 ="Server";

szFeatureName2 ="Client";

szFeatureName3 ="Watch_Portion";

szFeatureName4 ="Log_Portion";

szFeatureName5 ="Report_Portion";

//需要拷貝的源文件

szSrcFile1 = "Test\\lib\\*.*";

szSrcFile2 = "Test\\databaselib\\*.*";

//拷貝的目的地,目標(biāo)文件夾

szTarFolder1 = "lib\\*.*";

szTarFolder2 = "databaselib\\*.*";

3. 對每一個(gè)feature進(jìn)行判斷,進(jìn)行相應(yīng)的文件拷貝

在OnFirstUIAfter()的begin和end之間添加如下代碼:

//copy the lib to the target ,copy the necessary file to the target

if (FeatureIsItemSelected(MEDIA, szFeatureName1)=1) then

CopyFile(SRCDISK^szSrcFile1, TARGETDIR^szTarFolder1);

CopyFile(SRCDISK^"Test\\configure\\title.gif", TARGETDIR^"Server\\ title.gif");

CopyFile(SRCDISK^"Test\\configure\\background.gif", TARGETDIR^" Server \\ background.gif");

CopyFile(SRCDISK^"Test\\configure\\configure.dat", TARGETDIR^" Server \\configure.dat ");

endif;

if (FeatureIsItemSelected(MEDIA, szFeatureName2)=1) then

CopyFile(SRCDISK^szSrcFile1, TARGETDIR^szTarFolder1);

CopyFile(SRCDISK^"Test\\configure\\configure.dat", TARGETDIR^"Client\\configure.dat ");

CopyFile(SRCDISK^"Test\\configure\\license.dat", TARGETDIR^" Client \\license.dat");

endif;

if (FeatureIsItemSelected(MEDIA, szFeatureName3)=1) then

CopyFile(SRCDISK^szSrcFile1, TARGETDIR^szTarFolder1);

CopyFile(SRCDISK^"Test\\configure\\configure", TARGETDIR^" Watch Portion \\configure");

CopyFile(SRCDISK^"Test\\configure\\license.dat", TARGETDIR^" Watch Portion \\license.dat");

endif;

if (FeatureIsItemSelected(MEDIA, szFeatureName4)=1) then

CopyFile(SRCDISK^szSrcFile1, TARGETDIR^szTarFolder1);

endif;

if (FeatureIsItemSelected(MEDIA, szFeatureName5)=1) then

CopyFile(SRCDISK^szSrcFile1, TARGETDIR^szTarFolder1);

endif;

4. 代碼解釋

if (FeatureIsItemSelected(MEDIA, szFeatureName1)=1) then

CopyFile(SRCDISK^szSrcFile1, TARGETDIR^szTarFolder1);

CopyFile(SRCDISK^"Test\\configure\\title.gif", TARGETDIR^"Server\\ title.gif");

CopyFile(SRCDISK^"Test\\configure\\background.gif", TARGETDIR^" Server \\ background.gif");

CopyFile(SRCDISK^"Test\\configure\\configure.dat", TARGETDIR^" Server \\configure.dat ");

endif;

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

FeatureIsItemSelected(MEDIA, szFeatureName1) 這個(gè)函數(shù)用于判斷用戶是否選擇了某feature。Help里對這個(gè)函數(shù)是這樣描述的:FeatureIsItemSelected ( szFeatureSource, szFeature );

參數(shù)一:szFeatureSource,大意好像是feature的來源,具體不是很明白到底指什么,反正help自帶的例子里寫的MEDIA照抄沒有錯(cuò)。

參數(shù)二:szFeatureName1,就是 feature的名字了

如果返回值為1,則說明用戶選擇了這個(gè)feature

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

CopyFile(SRCDISK^szSrcFile1, TARGETDIR^szTarFolder1);

拷貝文件的函數(shù)。Help里是這樣描述的:CopyFile ( szSrcFile, szTargetFile );

參數(shù)一:szSrcFile,源文件,可帶路徑,要帶有擴(kuò)展名的文件名。當(dāng)這個(gè)文件帶路徑時(shí),則從這個(gè)指定路徑下拷貝指定的文件;如果是不帶路徑的,則直接從安裝文件所在盤的盤符下尋找指定的文件來進(jìn)行拷貝。如果要拷貝某個(gè)文件夾下的一系列文件,可以使用通配符。

參數(shù)二:目標(biāo)文件,可帶路徑,要帶有擴(kuò)展名的文件名。當(dāng)這個(gè)文件帶路徑時(shí),則將文件拷貝到這個(gè)指定路徑下;如果是不帶路徑的,則將文件拷貝到安裝路徑下。支持通配符。

小結(jié):上面這段代碼的意思是:如果用戶選擇了某個(gè)feature,則從安裝程序所在的盤下面的一些文件夾下拷貝文件到目標(biāo)路徑下的一些對應(yīng)文件夾下。這里記住拷貝文件一定要帶上文件的全名,包括擴(kuò)展名。

5. 如果用戶選擇了文檔feature ,則把文檔文件夾拷貝進(jìn)來,并且對該文件夾進(jìn)行遍歷,為每一個(gè)文檔創(chuàng)建一個(gè)在開始菜單下的快捷方式

1. 這個(gè)功能仍然在After Move Data | OnFirstUIAfter()的函數(shù)里實(shí)現(xiàn)

先定義一些變量并賦值,藍(lán)色字體

function OnFirstUIAfter()

//feature name

STRING szFeatureName6;//feature名

STRING szSrcFile3; //需要拷貝的源文件

STRING szTarFolder3; //拷貝的目的地,帶文件名

STRING szTarFolder4; //拷貝的目標(biāo)文件夾,后面有一個(gè)函數(shù)要用到不帶文件名的目標(biāo)路徑

STRING szDocFile, szDocFileName;// szDocFile,查找函數(shù)返回的查詢得到文件名;szDocFileName,要查找的文件名

NUMBER nResult; //數(shù)字型變量,存放函數(shù)的返回結(jié)果

begin

//feature 定義

szFeatureName6 ="Document";

//需要拷貝的源文件

szSrcFile3 = "Docs\\*.*";

//拷貝的目的地,目標(biāo)文件夾

szTarFolder3 = TARGETDIR^"Docs\\*.*";

szTarFolder4 = TARGETDIR^"Docs";//文檔的存放路徑,不帶文件名

2. 仍然在begin和end之間的函數(shù)體內(nèi)把下面的代碼拷貝進(jìn)去即可

if (FeatureIsItemSelected(MEDIA, szFeatureName6)=1) then //如果選擇了此feature

if(CopyFile(SRCDISK^szSrcFile3, szTarFolder3)=0) then //那么把要拷貝的文件拷貝過去

nResult = FindAllFiles(TARGETDIR^"Docs", "*.pdf", szDocFile, RESET); //對拷貝過去的文件進(jìn)行查找,該函數(shù)會(huì)在第一個(gè)符合條件//的文件處停止

while (nResult = 0)

LongPathToQuote(szDocFile, TRUE );

ParsePath (szDocFileName, szDocFile, FILENAME_ONLY);//對查找到的文件獲取文件名

AddFolderIcon(FOLDER_PROGRAMS^"Test\\Docs",szDocFileName, szDocFile, "", TARGETDIR^"Docs\\icons\\help.ico" , 0 ,"" , REPLACE ); //為該文件創(chuàng)建快捷方式,快捷方式的顯示名就是剛才獲取的文件名

nResult = FindAllFiles(TARGETDIR^"Docs", "*.pdf", szDocFile, CONTINUE);//從上一個(gè)查找的位置繼續(xù)向下查找,進(jìn)行循環(huán)

endwhile;

endif;

endif;

3. 代碼解釋

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

if (FeatureIsItemSelected(MEDIA, szFeatureName6)=1) then

endif;

如果用戶選擇了文檔feature,則進(jìn)行一些相應(yīng)操作

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

if(CopyFile(SRCDISK^szSrcFile3, szTarFolder3)=0) then

endif;

這里執(zhí)行了兩步操作:

第一步,從源盤的Docs文件夾下把所有文件都拷貝安裝路徑的Docs文件夾下,注意在定義變量的時(shí)候使用了通配符

第二步,如果拷貝成功,則返回值為0,那么進(jìn)行下一步相應(yīng)操作

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

nResult = FindAllFiles(TARGETDIR^"Docs", "*.pdf", szDocFile, RESET);

查找目標(biāo)文件夾下所有后綴名為pdf的文件,從文件夾的開始位置進(jìn)行查找,查找成功則返回0。

這個(gè)函數(shù)在這里有一個(gè)巧妙的應(yīng)用,因?yàn)檫@個(gè)函數(shù)會(huì)在查找到第一個(gè)符合條件的文件時(shí)就會(huì)停止繼續(xù)向下查找,因此利用靜態(tài)變量的傳值不同,來實(shí)現(xiàn)對文件夾的全部查找。

Help里的解釋如下:

FindAllFiles ( szDir, szFileName, svResult, nOp );

參數(shù)一:szDir,被查找的文件夾

參數(shù)二:szFileName,需要查找的文件的名字,支持通配符,例如*.*,*.pdf,*.doc

參數(shù)三:svResult,函數(shù)會(huì)在查找到第一個(gè)符合條件的文件時(shí)停止,返回這個(gè)符合條件的文件的文件名,帶全路徑和含擴(kuò)展名的文件名

參數(shù)四:nOp, 靜態(tài)變量。CONTINUE,從上一次查找的位置開始查找,這個(gè)特性我們呆會(huì)兒會(huì)用到;RESET,從文件夾的開始位置進(jìn)行查找;CANCEL,釋放被上一次的FindAllFiles查找的函數(shù)。在Windows NT系統(tǒng)下,需要在安裝過程中使用帶CANCEL的FindAllFiles來釋放之前的查找,確保安裝的正確性(因此我懷疑查找有bug,這個(gè)函數(shù)用來彌補(bǔ)這個(gè)bug…)。

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

LongPathToQuote(szDocFile, TRUE );

szDocFile為上一個(gè)函數(shù)查找到的第一個(gè)符合條件的文件名,帶完整路徑,這個(gè)LongPathToQuote函數(shù)加上這個(gè)文件名上的括號(hào);否則下面一個(gè)函數(shù)無法解析不帶括號(hào)的長文件名。

Help里的解釋如下:

LongPathToQuote ( svPath, nParameter );

參數(shù)一:svPath,長文件名

參數(shù)二:nParameter,靜態(tài)變量。 TRUE,為長文件名加上括號(hào);FALSE,為長文件名脫去括號(hào)。

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

ParsePath (szDocFileName, szDocFile, FILENAME_ONLY);

解析帶路徑的長文件名,返回文件本身的文件名

Help里的解釋如下:

ParsePath ( svReturnString, szPath, nOperation );。

參數(shù)一:svReturnString為返回的解析過的文件名,

參數(shù)二:szPath,即被解析的長文件名

參數(shù)三:nOperation,靜態(tài)變量,指定用何種方式來解析。這里使用FILENAME_ONLY,也就說返回值為不帶路徑、不包含擴(kuò)展名的文件名。這個(gè)文件名被下面一步用作顯示的快捷方式的名稱。

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

AddFolderIcon(FOLDER_PROGRAMS^"Test\\Docs",szDocFileName, szDocFile, "", TARGETDIR^"Docs\\icons\\help.ico" , 0 ,"" , REPLACE );

創(chuàng)建一個(gè)快捷方式,使用指定的圖標(biāo)。

Help里的解釋如下:

AddFolderIcon ( szProgramFolder, szItemName, szCommandLine, szWorkingDir, szIconPath, nIcon, szShortCutKey, nFlag );

參數(shù)一:szProgramFolder, 要?jiǎng)?chuàng)建的快捷方式所在的文件夾。這里FOLDER_PROGRAMS指開始 | 所有程序,因此我們的快捷方式將會(huì)出現(xiàn)在開始 | 所有程序 | Test的Docs下;如果要添加到桌面上,可以設(shè)置為FOLDER_DESKTOP;FOLDER_STARTUP 指添加為啟動(dòng)項(xiàng);FOLDER_STARTMENU添加到開始菜單下。

參數(shù)二:szItemName,help里解釋很晦澀,解釋為要添加到文件夾下的圖標(biāo)的名稱,即出現(xiàn)的圖標(biāo)旁邊的那個(gè)字符串。其實(shí)就是我們常說的快捷方式的名稱。這里填寫被解析出來的那個(gè)不帶路徑也不帶擴(kuò)展名的文件名。

參數(shù)三:szCommandLine,全限定路徑的文件名或文件夾名,可包含命令行參數(shù)。這里傳入剛才查找到的文件名,包含路徑、文件名和擴(kuò)展名。讀者可能注意到這個(gè)參數(shù)被做了一些預(yù)處理,這個(gè)處理也是折騰了幾次才搞出來的,不同的操作系統(tǒng)默認(rèn)路徑也是有是否帶引號(hào)的差別的,這里需要顯式地指定一下,以免在不同操作系統(tǒng)上運(yùn)行時(shí)引起不同的結(jié)果。

參數(shù)四:szWorkingDir,工作目錄。Help里的解釋如下:設(shè)置這個(gè)目錄為你的應(yīng)用程序文件所在的地方;要設(shè)置包含了應(yīng)用程序的目錄為工作目錄,則可傳一個(gè)空字符串給這個(gè)參數(shù)。這個(gè)參數(shù)一開始我并未理解其含義,不過傳空字符串也沒有出錯(cuò);在后來經(jīng)理提出新要求:允許用戶自行選擇是否在桌面上創(chuàng)建快捷方式時(shí)無意中明白這個(gè)參數(shù)的含義;請讀者隨便尋找一個(gè)自己計(jì)算機(jī)上的任意位置的快捷方式,右鍵點(diǎn)擊選擇“屬性”,這個(gè)szWorkingDir就是屬性面板上的“起始位置”,值為這個(gè)快捷方式所指的應(yīng)用程序所在的文件夾的路徑。至少在我試驗(yàn)的程序里,創(chuàng)建開始菜單的快捷方式和桌面快捷方式,這個(gè)參數(shù)要求的值還是略有不同的,開始菜單里創(chuàng)建,可以直接傳空字符串;而桌面快捷方式,傳控字符串總是會(huì)出錯(cuò),查看屬性面板里的“起始位置”值為空,因此手動(dòng)地傳了快捷方式所指向的應(yīng)用程序的所在文件夾的路徑,后面在“安裝結(jié)束時(shí)允許用戶選擇創(chuàng)建桌面快捷方式”話題里有詳細(xì)說明。

參數(shù)五:szIconPath,帶全限定名的圖標(biāo)的路徑,即包含路徑、文件名和擴(kuò)展名

參數(shù)六:nIcon。如果不是使用Windows圖標(biāo)的話,統(tǒng)統(tǒng)指定為0;Windows圖標(biāo)我沒有研究過,Help里說可以指定為0,1,2,3…n我猜測是不是圖標(biāo)文件本身包含了多個(gè)圖標(biāo),而我可以指定使用哪個(gè)圖標(biāo)?

參數(shù)七:szShortCutKey,熱鍵,一般用不到。如果有需要可以設(shè)置為比如"Ctrl + Alt + 1"這種形式。

參數(shù)八:nFlag,靜態(tài)變量,多個(gè)用途。這個(gè)程序里我們使用了REPLACE,即永遠(yuǎn)使用當(dāng)前這個(gè)快捷方式的屬性;RUN_MAXIMIZED ,當(dāng)從這個(gè)快捷方式登錄程序時(shí),程序界面最大化;RUN_MINIMIZED,當(dāng)從這個(gè)快捷方式登錄程序時(shí),程序界面最小化; NULL,無任何操作(不知道這個(gè)無任何操作適用于何種情況?)。

小結(jié):這段代碼的重點(diǎn)在于

1) 實(shí)現(xiàn)對文件夾下的文件的遍歷。因?yàn)橹肮P者的文檔都打包在程序里,苦于文檔的名稱和數(shù)量常常變更,每做一次都要耗費(fèi)人力物力,而且在光盤里仍然需要單獨(dú)放置一個(gè)文檔文件夾供用戶在沒有安裝程序前的隨時(shí)查看,重復(fù)打包安裝使得安裝內(nèi)容容量巨大,以至于從刻錄小光盤改成刻錄大光盤,從VCD盤改成DVD盤。這段代碼在用戶選擇了安裝文檔的條件下,對外部文件夾進(jìn)行了拷貝,并且讀取文件夾下所有的pdf文件(依次類推,只要設(shè)置了正確的過濾條件,可以讀取文件夾下想要的文件)。難點(diǎn)就在于將文件夾下的文件一個(gè)個(gè)讀取出來并且獲取該文件的信息。

2) 對讀取的文件創(chuàng)建快捷方式,這個(gè)難點(diǎn)在于8個(gè)參數(shù)的理解。我在互聯(lián)網(wǎng)上搜索了一陣子,并且啃了一陣子help,但是可能自己外語水平不是很過關(guān),以至于第四個(gè)參數(shù)沒有完全理解到底是什么意思,所見的例子也很單調(diào)并且偷懶,能賦””的地方都給賦了””,無語~~~~

整個(gè)安裝程序做下來這一段代碼是最難的,F(xiàn)indAllFiles在Help里解釋是當(dāng)碰到第一個(gè)符合條件的文件就會(huì)停下來,因此如何讀取全部文件,并且獲取文件信息,代碼的撰寫也是費(fèi)了很大的功夫,并且參考了別人的程序修改出來的。

6. 在安裝結(jié)束時(shí),顯示readme.txt 文件

這是個(gè)很有用的設(shè)置,但是在InstallScript工程里不是默認(rèn)自帶的,因此也需要腳本編程實(shí)現(xiàn)。

這段代碼的位置是在After Move Data | OnFirstUIAfter()的函數(shù)里實(shí)現(xiàn)的

1. 首先,在安裝的時(shí)候把readme.txt文件從源盤拷貝到安裝目錄下。把這段代碼拷貝到After Move Data | OnFirstUIAfter()的begin和end;之間即可。README.TXT文件放置在源盤的根目錄下,并且在安裝時(shí)拷貝到安裝目錄下。

CopyFile(SRCDISK^"README.TXT", TARGETDIR^"README.TXT");

這段代碼意味著當(dāng)安裝執(zhí)行的時(shí)候,這個(gè)文件總會(huì)被拷貝過去。

2. 創(chuàng)建一個(gè)Finish界面,并在界面上設(shè)置詢問是否顯示readme.txt文件的選項(xiàng)。

之前我們看到當(dāng)我們第一次選取了After Move Data | OnFirstUIAfter()選項(xiàng)時(shí),系統(tǒng)會(huì)為我們創(chuàng)建如下代碼(當(dāng)然不創(chuàng)建也不要緊,自己敲就是了)

這個(gè)就是結(jié)束界面。Installscript工程默認(rèn)安裝完畢后,界面直接消失,而不會(huì)出現(xiàn)一個(gè)帶有Finish按鈕的界面讓用戶點(diǎn)擊了以后才結(jié)束整個(gè)安裝過程。

這段代碼就是創(chuàng)建了一個(gè)Finish界面了,我們要對這段代碼進(jìn)行改造,使之出現(xiàn)一個(gè)是否顯示readme的選項(xiàng)。

clip_image004

把上圖中從Disable(STATUSEX);起到SdFinishEx這行的代碼,全部替換成如下代碼:

Disable(STATUSEX);

ShowObjWizardPages(NEXT);

bOpt1 = TRUE;

bOpt2 = TRUE;

szMsg1 = SdLoadString(IFX_SDFINISH_MSG1);

szTitle="";

szMsg1="";

szMsg2="";

szOption1="Show Readme";

szOption2="";

SdFinishEx(szTitle, szMsg1, szMsg2, szOption1, szOption2, bOpt1, bOpt2);

if (bOpt1=TRUE) then

if(FindFile(TARGETDIR, "README.TXT", szDocFile)=0) then

LaunchApp ( WINDIR^"Notepad.exe" , TARGETDIR^"README.TXT" );

endif;

endif;

3. 代碼解釋

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

Disable(STATUSEX);

使默認(rèn)的安裝設(shè)置對話框無效。

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

ShowObjWizardPages(NEXT);

順序執(zhí)行這個(gè)OnFirstUIAfter()的代碼,如果參數(shù)為BACK,則逆序執(zhí)行

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

SdLoadString(IFX_SDFINISH_MSG1);

返回參數(shù)所關(guān)聯(lián)的字符串值,這個(gè)參數(shù)應(yīng)當(dāng)是一個(gè)資源ID。

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

SdFinishEx(szTitle, szMsg1, szMsg2, szOption1, szOption2, bOpt1, bOpt2);

參數(shù)一:szTitle,即顯示在界面上的左上角的標(biāo)題,如果傳空值,則顯示默認(rèn)值

參數(shù)二:szMsg1,安裝結(jié)束的界面上允許最多有兩個(gè)可選項(xiàng),這個(gè)參數(shù)可以顯示第一個(gè)選項(xiàng)的一些相關(guān)說明,如果賦空則不顯示任何說明

參數(shù)三:szMsg2,解釋同上

參數(shù)四:szOption1,選項(xiàng)名。這個(gè)是一個(gè)Checkbox,如果設(shè)置為空則不顯示,如果賦值則顯示一個(gè)Checkbox并且在這個(gè)Checkbox旁邊顯示這個(gè)所賦的簡短值。

參數(shù)五:szOption2,解釋同上。

參數(shù)六:第一個(gè)選項(xiàng)的狀態(tài),如果設(shè)置為TRUE,則第一個(gè)選項(xiàng)Checkbox默認(rèn)為選中狀態(tài),F(xiàn)ALSE則為未選中狀態(tài)。

參數(shù)七:第二個(gè)選項(xiàng)的狀態(tài),解釋同上。

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

if (bOpt1=TRUE) then

判斷是否選擇了checkbox。如果用戶選擇了這個(gè)選項(xiàng),則進(jìn)行下一步操作

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

if(FindFile(TARGETDIR, "README.TXT", szDocFile)=0) then

為了保險(xiǎn)起見,需要進(jìn)一步判斷一下這個(gè)readme.txt是否被拷貝進(jìn)來了

Help里解釋如下:

FindFile ( szPath, szFileName, svResult );

參數(shù)一:szPath,文件所在的路徑,不包含文件名

參數(shù)二:szFileName,文件名,包含擴(kuò)展名

參數(shù)三:szDocFile,返回的文件名

如果查找成功,則返回值為1

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

LaunchApp ( WINDIR^"Notepad.exe" , TARGETDIR^"README.TXT" );

打開readme文件

Help里沒有對這個(gè)函數(shù)的專門的解釋,但是有個(gè)例子,以至于我看了好幾遍才看懂要表達(dá)的意思

參數(shù)一:應(yīng)用程序,也就是你用什么工具來打開第二個(gè)參數(shù)指定的文件。我們這里用記事本打開,因此要引用一下Windows下自帶的程序Notepad.exe,路徑為WINDIR^"Notepad.exe" 。如果是一些不是Windows自帶的程序,比如PDF,DOC,還需要從注冊表里得到所安裝的目標(biāo)位置,從這個(gè)目標(biāo)位置得到要用的工具。有興趣的朋友可以試驗(yàn)一下。

參數(shù)二:要打開的文件,帶路徑,包含擴(kuò)展名

小結(jié):這個(gè)界面我曾經(jīng)試圖寫在OnFirstUIBefore()里的結(jié)尾部分,用Dlg_SdFinish來實(shí)現(xiàn),但是總是發(fā)現(xiàn)雖然結(jié)束界面能出來,但是上一個(gè)界面不能消失掉的情況。因?yàn)檫@個(gè)資料也不好找,倉促之間試驗(yàn)出上述所說的辦法,估計(jì)是等安裝界面結(jié)束后補(bǔ)上一個(gè)界面來達(dá)到這個(gè)效果的;其實(shí)我本人是比較討厭結(jié)束的時(shí)候有這么一個(gè)要看readme的選項(xiàng)的,一般自己裝到這種軟件,都是去掉鉤選框,不看readme的;但是如果直接結(jié)束掉,不出這個(gè)結(jié)束界面又覺得提示不足,有時(shí)候不能確定安裝程序有沒有結(jié)束,所以私下里還是比較想去掉readme選項(xiàng),而直接顯示一個(gè)只有一個(gè)finish按鈕的界面的。

7. 在安裝結(jié)束時(shí),允許用戶選擇是否顯示桌面快捷方式

有時(shí)候我們會(huì)看到別的安裝程序在安裝過程中允許用戶選擇是否要在桌面上顯示快捷方式,一開始因?yàn)槲覀児镜姆植际较到y(tǒng)的組件太多了,不想顯示在桌面上,而且覺得和在開始菜單中顯示快捷方式的原理是一樣的,因此也就輕輕帶過;后來經(jīng)理抱怨說沒有桌面快捷方式,總是要去開始菜單找,覺得麻煩,而且客戶是使用專用計(jì)算機(jī)運(yùn)行我們的程序,也就是桌面上會(huì)很干凈,希望我能夠做這個(gè)功能出來。我試了一下,發(fā)現(xiàn)和在開始菜單中顯示快捷方式還是有一點(diǎn)不同的,也是值得寫出來的,至少可以讓讀者少走一些彎路。

1. 首先要顯示一個(gè)允許用戶選擇是否顯示桌面快捷方式的界面,這個(gè)界面上要有一個(gè)checkbox(鉤選框),當(dāng)鉤選了以后,安裝程序就要在安裝時(shí)為用戶顯示桌面快捷方式。

這段代碼的位置是在After Move Data | OnFirstUIAfter()的函數(shù)里實(shí)現(xiàn)的,也就是和“顯示readme文件”的功能放在一起。

把從Disable(STATUSEX);起到SdFinishEx這行的代碼,全部替換成如下代碼:

Disable(STATUSEX);

ShowObjWizardPages(NEXT);

bOpt1 = TRUE;

bOpt2 = TRUE;

szMsg1 = SdLoadString(IFX_SDFINISH_MSG1);

szTitle="";

szMsg1="";

szMsg2="";

szOption1="Show Readme";

szOption2="Create Shortcut on Desktop?";

SdFinishEx(szTitle, szMsg1, szMsg2, szOption1, szOption2, bOpt1, bOpt2);

2. 代碼解釋

與上面的“顯示readme文件”中的代碼相比,只動(dòng)了一個(gè)地方,即szOption2="Create Shortcut on Desktop?";

這個(gè)是一個(gè)Checkbox,如果值設(shè)置為空則不顯示,如果賦值則顯示一個(gè)Checkbox并且在這個(gè)Checkbox旁邊顯示這個(gè)所賦的簡短值。

這里我們需要它顯示出來,這樣在界面上用戶就會(huì)看到一個(gè)鉤選框詢問是否要顯示桌面快捷方式。

3. 接下來我們要對用戶所做的選擇做一些判斷,并且顯示桌面快捷方式,在這段代碼后面加上

if(bOpt2=TRUE) then

if (FeatureIsItemSelected(MEDIA, szFeatureName1)=1) then

szDocFile = TARGETDIR^"Server\\server.bat";

LongPathToQuote(szDocFile, TRUE );

AddFolderIcon(FOLDER_DESKTOP, "Server" , szDocFile, TARGETDIR^"Server" , TARGETDIR^"Server\\icons\\appClient.ico" , 0 ,"" , REPLACE );

endif;

4. 代碼解釋

因?yàn)樯厦鎸@些函數(shù)的每個(gè)參數(shù)都有詳細(xì)解釋了,所以這里就不做一一解釋了,只對要注意的地方做說明。

這里,一開始,筆者對第四個(gè)參數(shù)仍然傳的是空字符串,但是創(chuàng)建的快捷方式總是不能運(yùn)行,對比屬性面板才發(fā)現(xiàn),桌面快捷方式的“起始位置”的值居然是空的,看來Help解釋的“當(dāng)傳空值的時(shí)候,默認(rèn)為快捷方式所指的應(yīng)用程序所在的目錄”并未生效,只好老老實(shí)實(shí)地把運(yùn)行目錄的值手動(dòng)地傳進(jìn)去。

讀者可能注意到在AddFolderIcon函數(shù)里的第三個(gè)參數(shù)被做了一些預(yù)處理,這個(gè)處理也是折騰了幾次才搞出來的,不同的操作系統(tǒng)默認(rèn)路徑也是有是否帶引號(hào)的差別的,這里需要顯式地指定一下,以免在不同操作系統(tǒng)上運(yùn)行時(shí)引起不同的結(jié)果。

8. 在安裝結(jié)束后,啟動(dòng)指定的程序

在全部安裝完畢后,啟動(dòng)指定的程序,向Windows安裝一個(gè)服務(wù)。或者也可使用于安裝結(jié)束后的程序的自啟動(dòng)。

1. 這部分很明顯是要在安裝全部結(jié)束后進(jìn)行的,因此放在After Move Data | OnEnd里

clip_image006

2. 把OnEnd()的代碼替換如下

function OnEnd()

STRING szFeatureName;

STRING serviceTarget;

STRING szDocFile;

begin

/*

//這個(gè)服務(wù)所需的文件只有在鉤選了某feature時(shí)候才會(huì)被拷貝,并且也只有在用戶鉤選安裝了此feature時(shí)候才會(huì)在安裝結(jié)束時(shí)安裝此服務(wù),因此首要判斷是否選擇了此feature,然后尋找到該執(zhí)行文件,并且進(jìn)行安裝

*/

szFeatureName="Watch_Portion";

serviceTarget=TARGETDIR^"watch.exe";

if (FeatureIsItemSelected(MEDIA, szFeatureName)=1) then

if(FindFile(TARGETDIR, " watch.exe ", szDocFile)=0) then

if (LaunchApp (serviceTarget, "") < 0) then

MessageBox ("Unable to launch "+serviceTarget+".", SEVERE);

endif;

endif;

endif;

end;

3. 代碼解釋

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

if (FeatureIsItemSelected(MEDIA, szFeatureName)=1) then

endif;

首先判斷這個(gè)feature是否被用戶選擇安裝。因?yàn)樵谶@個(gè)應(yīng)用程序里這個(gè)服務(wù)只與此feature相關(guān),因此要做一下判斷,如果用戶沒有安裝這個(gè)feature,就不需要啟動(dòng)這個(gè)服務(wù)了。

當(dāng)用戶選擇了這個(gè)feature時(shí),返回值為0

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

if(FindFile(TARGETDIR, " watch.exe ", szDocFile)=0) then

endif;

這個(gè)是判斷一下文件是否被正確地拷貝過去了,這個(gè)文件應(yīng)該位于安裝目錄下,名為watch.exe。當(dāng)該文件存在時(shí),返回值為0

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

if (LaunchApp (serviceTarget, "") < 0) then

endif;

啟動(dòng)該服務(wù);如果啟動(dòng)失敗,則返回小于0的值。

這里L(fēng)aunchApp的用法和上面第6段的用法略有不同。這個(gè)函數(shù)的本意是啟動(dòng)第一個(gè)參數(shù)指定的運(yùn)行程序來打開第二個(gè)參數(shù)指定的文件。這里第二個(gè)參數(shù)指定為空,因?yàn)闆]有要打開的文件;第一個(gè)參數(shù)指向我們需要啟動(dòng)的可執(zhí)行程序即可。

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

MessageBox ("Unable to launch "+serviceTarget+".", SEVERE);

如果上一步中判斷到程序未能正確啟動(dòng),則彈出一個(gè)錯(cuò)誤提示框體現(xiàn)用戶。

小結(jié):這段代碼的用法非常簡單,但是如果用在適當(dāng)?shù)陌惭b程序里會(huì)非常重要;筆者的安裝程序,在一開始的時(shí)候需要用戶安裝完畢后手動(dòng)地去安裝目錄里找到這個(gè)服務(wù)并且啟動(dòng),使人感覺非常不友好;現(xiàn)在在安裝完畢后做到了靜默啟動(dòng),用戶無需做任何事情。而且這個(gè)服務(wù)需要JDK的支持,配合上述第2段中判斷是否安裝了JDK這個(gè)應(yīng)用,就不會(huì)出現(xiàn)安裝了此服務(wù)但是無法運(yùn)行的局面。

9. 安裝結(jié)束后,為JDK 設(shè)置一個(gè)環(huán)境變量

之前提到了,要在安裝本系統(tǒng)時(shí)判斷是否安裝了JDK,在最初筆者所做的安裝盤中,還要讓用戶手動(dòng)地去為JDK設(shè)置環(huán)境變量JAVA_HOME,設(shè)置環(huán)境變量對于外行來說簡直就是天方夜譚,在JAVA論壇新手區(qū)最常見就是求助設(shè)置環(huán)境變量的問題了,因此,這個(gè)功能最好還是由安裝程序代勞為妙。

1. 這段代碼在After Move Data | OnFirstUIAfter()里

//write the environment variable

szKey = "SOFTWARE\\JavaSoft\\Java Development Kit\\1.6.0_04";

RegDBSetDefaultRoot(HKEY_LOCAL_MACHINE);

if (RegDBKeyExist(szKey)=1) then//如果該注冊表值存在

if(RegDBGetKeyValueEx(szKey,"JavaHome",nvType,svValue,nvSize)=0) then//獲取注冊表值成功

szKey = "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment";

if(RegDBSetKeyValueEx(szKey, "JAVA_HOME", REGDB_STRING, svValue, -1)<0) then

MessageBox ("Javahome create failed, please set it manually!", SEVERE);

endif;

endif;

endif;

2. 代碼解釋

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

RegDBKeyExist(szKey)

判斷JDK1.6.0_04的注冊表值是否存在;要判斷JDK1.6.0_04是否被安裝,只有通過注冊表來判斷啦,同理可得,要是自己開發(fā)的一套系統(tǒng)中有多個(gè)安裝程序,而且相互關(guān)聯(lián),就得朝注冊表里寫入值了。

如果返回值為1,則說明存在該鍵值;

如果返回值小于0,則說明該鍵值不存在。

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

RegDBGetKeyValueEx(szKey,"JavaHome",nvType,svValue,nvSize)

因?yàn)樵O(shè)置JAVA_HOME環(huán)境變量需要JDK的安裝位置,所以要根據(jù)注冊表來尋找到這個(gè)安裝位置,而幸運(yùn)的是,該鍵值下的JavaHome鍵名所對應(yīng)的值就是JDK的安裝位置。

Help里對該函數(shù)的解釋如下:

RegDBGetKeyValueEx ( szKey, szName, nvType, svValue, nvSize );

參數(shù)一:szKey, 要查找的注冊表的鍵,這里我們查找SOFTWARE\\JavaSoft\\Java Development Kit\\1.6.0_04

參數(shù)二:szName,一些注冊表鍵下面會(huì)有一些鍵名,如果你去看一下我們查找的鍵,會(huì)發(fā)現(xiàn)該鍵下存在多個(gè)鍵名,這里我們只要查找JavaHome鍵名對應(yīng)的值,因此,指定szName為JavaHome

參數(shù)三:nvType,返回該鍵名對應(yīng)的值的類型,比如字符型,數(shù)字型;當(dāng)時(shí)筆者還犯了一個(gè)錯(cuò)誤,以為這個(gè)參數(shù)是需要筆者指定類型的,因此寫了一個(gè)REGDB_STRING,結(jié)果編譯出錯(cuò),搞了半天發(fā)現(xiàn)這個(gè)參數(shù)是個(gè)返回值,汗一個(gè)。

參數(shù)四:svValue,返回該鍵名對應(yīng)的值

參數(shù)五:nvSize,返回該鍵名對應(yīng)的值的字節(jié)數(shù)

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

szKey = "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment";

RegDBSetKeyValueEx(szKey, "JAVA_HOME", REGDB_STRING, svValue, -1)

如果搜索注冊表發(fā)現(xiàn)JDK已經(jīng)安裝了,就去讀一下注冊表的鍵值,并且設(shè)置我們所需要的環(huán)境變量,這兩句話就是用來設(shè)置環(huán)境變量的。

環(huán)境變量也是利用注冊表鍵值設(shè)置函數(shù)RegDBSetKeyValueEx來實(shí)現(xiàn)的,這個(gè)鍵是一個(gè)特殊的位置,一定是"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment",我們對該函數(shù)進(jìn)行進(jìn)行詳細(xì)說明。

RegDBSetKeyValueEx ( szKey, szName, nType, szValue, nSize );

函數(shù)作用:設(shè)置注冊表鍵值

參數(shù)一:szKey注冊表里的鍵,這里,我們需要設(shè)置環(huán)境變量的值,因此這里固定傳值為"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment"

參數(shù)二:szName,鍵名,這里我們需要設(shè)置的是名為JAVA_HOME的環(huán)境變量

參數(shù)三:nType,被設(shè)置的鍵的類型,這里是字符串型,并且不帶%PATH%之類的符號(hào),也不轉(zhuǎn)行

參數(shù)四:szValue,就是鍵值了,這里我們已經(jīng)從上面得到了JDK的安裝路徑,就把安裝路徑傳進(jìn)去

參數(shù)五:nSize,help里說明如果鍵類型為REGDB_STRING, REGDB_STRING_EXPAND, 或者 REGDB_NUMBER時(shí),都可以設(shè)置該值為-1,installshield會(huì)自動(dòng)為我們計(jì)算正確的長度,而當(dāng)鍵類型為REGDB_BINARY 和REGDB_STRING_MULTI時(shí),就必須傳該鍵值的實(shí)際大小進(jìn)去。

小結(jié):Installshield默認(rèn)鍵值位置是在HKEY_CLASSES_ROOT下的,因此在這里,我們需要在進(jìn)行搜索鍵值和設(shè)置鍵值的操作之前使用RegDBSetDefaultRoot(HKEY_LOCAL_MACHINE);這句話來設(shè)置一下默認(rèn)的根鍵值為HKEY_LOCAL_MACHINE;另,在網(wǎng)上看了一個(gè)帖子,當(dāng)時(shí)匆匆看了一下,說是設(shè)置的鍵值會(huì)在反安裝時(shí)候卸載掉,我倒是沒有在自己的安裝程序里發(fā)現(xiàn)這個(gè)問題,不過可以研究一下;作者說當(dāng)時(shí)為了解決這個(gè)問題,是在代碼頭加上DISABLE(LOGGING);代碼尾加上ENABLE(LOGGING)來實(shí)現(xiàn)的,雖然我沒有碰到這個(gè)問題,但是還是很感謝這位作者,因?yàn)楫?dāng)時(shí)他也說了,根本找不到資料,自己啃了天書般的HELP來解決,而自己一旦解決了問題,就分享出來,以便于大家少走彎路。

10. 完美卸載

在第一部分的第9點(diǎn)我們提到過InstallScript工程里自帶的Uninstall快捷方式的缺陷,這里我們將會(huì)創(chuàng)建一個(gè)可以實(shí)現(xiàn)全部卸載的卸載方式,這個(gè)卸載方式會(huì)以快捷方式出現(xiàn)在開始菜單下,利用安裝程序本身的反安裝功能來實(shí)現(xiàn)

3. 這段代碼在After Move Data | OnFirstUIAfter()里,和其他創(chuàng)建快捷方式的代碼放一起

function OnFirstUIAfter()

STRING szfilename,szFolder ,szmsg1,szmsg2;

NUMBER nresult;

begin

//創(chuàng)建刪除快捷方式

szfilename = UNINSTALL_STRING +" /UNINSTALL";

nresult = StrFind(szfilename,".exe");

if nresult >=0 then

StrSub(szmsg1,szfilename,0,nresult + 4);

StrSub(szmsg2,szfilename,nresult + 4,200);

LongPathToQuote(szmsg1, FALSE );

LongPathToQuote(szmsg2, FALSE );

szfilename = "\"" + szmsg1 + "\"" +szmsg2;

endif;

AddFolderIcon(FOLDER_PROGRAMS^"Test","Uninstall",szfilename,WINDIR,"",0,"",REPLACE);

End;

4. 代碼解釋

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

szfilename = UNINSTALL_STRING +" /UNINSTALL";

參數(shù)一:UNINSTALL_STRING這個(gè)靜態(tài)變量指向的就是我們的安裝程序,也就是setup.exe,不過指向的位置不是我們的源盤里的setup.exe,而是C:\Program Files\InstallShield Installation Information\{0D9DF66A-44E5-4754-A522-2AD6C9D5CDBE}\setup.exe;Installshield創(chuàng)建的安裝文件在安裝時(shí)總會(huì)在這個(gè)文件夾里創(chuàng)建對應(yīng)信息,一長串?dāng)?shù)字型序列碼就是安裝程序的Product ID。利用這個(gè)setup.exe就可以進(jìn)行反安裝

參數(shù)二:/UNINSTALL,告訴程序啟動(dòng)這個(gè)setup.exe時(shí)為非安裝狀態(tài),即修復(fù)、重新安裝和卸載狀態(tài)。

因此,這個(gè)字符串的值應(yīng)該是這種形式:

"C:\Program Files\InstallShield Installation Information\{0D9DF66A-44E5-4754-A522-2AD6C9D5CDBE}\setup.exe" -runfromtemp -l0x0409 /UNINSTALL

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

nresult = StrFind(szfilename,".exe");

尋找到“.exe”這個(gè)字符串在szfilename這個(gè)字符串中的位置。

Help里對這個(gè)函數(shù)的描述如下:

    StrFind (szString, szFindMe);
  

參數(shù)一:szString,被查找的源字符串

參數(shù)二:szFindMe,要查找的字符串

返回值為要查找的字符串在源字符串中的位置,如果返回值小于0,則說明源字符串中找不到要查找的字符串

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

StrSub(szmsg1,szfilename,0,nresult + 4);

StrSub(szmsg2,szfilename,nresult + 4,200);

如果要查找的字符串存在,那么源字符串就是正確的;這兩句語句就對源字符串進(jìn)行截?cái)啵玫较胍淖哟?

szmsg1應(yīng)該為C:\Program Files\InstallShield Installation Information\{0D9DF66A-44E5-4754-A522-2AD6C9D5CDBE}\setup.exe

而szmsg2應(yīng)該為 -runfromtemp -l0x0409 /UNINSTALL

    Helpl里的解釋如下:
  
    StrSub ( svSubStr, szString, nStart, nLength ); 
  
    參數(shù)一:svSubStr返回的結(jié)果字符串
  
    參數(shù)二:szfilename源字符串
  
    參數(shù)三:開始截?cái)嗟奈恢谩H绻付ǖ奈恢么笥谡麄€(gè)被解析的字符串長度,則返回一個(gè)空字串。
  
    參數(shù)四:結(jié)束截?cái)嗟奈恢谩H绻付ǖ奈恢么笥谡麄€(gè)被解析的字符串長度,則默認(rèn)為結(jié)束截?cái)嗟奈恢檬亲址慕Y(jié)尾處。
  

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

LongPathToQuote(szmsg1, FALSE );

LongPathToQuote(szmsg2, FALSE );

這兩句的作用是對上面解析出的兩個(gè)子串脫去括號(hào)。原本筆者參考的例子里沒有這兩句,在自己計(jì)算機(jī)上運(yùn)行正常,但是換了一臺(tái)計(jì)算機(jī)后,創(chuàng)建出的卸載快捷方式無效,查看快捷方式的指向發(fā)現(xiàn)和原來計(jì)算機(jī)的指向略有差別,查閱了一些資料得知Windows下的長文件名就有這個(gè)缺陷,每個(gè)操作系統(tǒng)解析出來的可能會(huì)有所不同,主要是引號(hào)的麻煩。在筆者自己的計(jì)算機(jī)上獲取的長文件名是不帶引號(hào)的,因此,解析正確;而測試的那臺(tái)計(jì)算機(jī)上獲取的文件名卻是帶引號(hào)的,這就造成了解析后拼湊的字符串的差別。這里就要顯式地為解析出來的子串脫一下引號(hào)。

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

szfilename = "\"" + szmsg1 + "\"" +szmsg2;

拼湊出正確的可執(zhí)行文件的長文件名,帶路徑,包含擴(kuò)展名

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

AddFolderIcon(FOLDER_PROGRAMS^"Test","Uninstall",szfilename,WINDIR,"",0,"",REPLACE);

添加一個(gè)快捷方式到開始 | 所有程序 | Test下;照抄即可。

小結(jié):可能讀者會(huì)比較奇怪這一段代碼的寫法,因?yàn)橹虚g那段if endif;代碼看上去簡直就是多此一舉。在Installshield7之前,一直是這樣寫的:

szfilename = UNINSTALL_STRING +" /UNINSTALL";

AddFolderIcon(FOLDER_PROGRAMS^"Test","Uninstall",szfilename,WINDIR,"",0,"",REPLACE);

從Installshield8開始,長文件名一直有引號(hào)封閉不正確的問題,因此if endif;代碼完全是為了解決這個(gè)問題而存在的,而上面提到的兩個(gè)脫去引號(hào)的語句,是筆者在前人基礎(chǔ)上修改加上的,因?yàn)榘l(fā)現(xiàn)解析出來的字串要是不脫一下括號(hào)還是有問題。

這個(gè)快捷方式運(yùn)行的時(shí)候,出現(xiàn)界面和在安裝完畢后再次運(yùn)行安裝程序出現(xiàn)的界面相同。選擇Remove即可進(jìn)行卸載。

clip_image008

這個(gè)卸載不會(huì)把程序運(yùn)行時(shí)產(chǎn)生的文件卸載掉,比如日志文件、配置信息文件等;會(huì)把安裝目錄中所有從安裝程序中安裝的文件都卸載掉,包括安裝時(shí)從外部拷貝的文件。利用Project Assistant創(chuàng)建的卸載快捷方式則無法卸載掉安裝時(shí)從外部拷貝的文件。

11. 完美卸載之卸載時(shí)觸發(fā)命令(卸載Windows服務(wù))

在做完這個(gè)安裝程序后,以為可以結(jié)束了,沒想到經(jīng)理又提出了一個(gè)新的要求,因?yàn)橹暗陌惭b里(參閱第二部分的第8小節(jié)),在安裝完畢后,啟動(dòng)了一個(gè)指定程序,這個(gè)指定程序干的事情就是向Windows寫了一個(gè)服務(wù)進(jìn)去(有興趣的同學(xué)可以去看看Java Service相關(guān)資料,是一個(gè)把Java程序注冊為Windows服務(wù)的一個(gè)工具或者說是組件更合適些);所以,這里希望能夠在卸載的時(shí)候能夠把這個(gè)服務(wù)給卸載掉。

首先我們介紹一下兩條Windows cmd命令:

1) SC stop XXX

這條命令用于停止某個(gè)名叫XXX的正在運(yùn)行的Windows服務(wù)

2) SC delete XXX

這條命令用于刪除某個(gè)名叫XXX的Windows服務(wù)

一開始我的思路是這樣的,獲取安裝程序的卸載狀態(tài),然后調(diào)用這兩條命令來刪除服務(wù);沒想到這個(gè)“獲取安裝程序的卸載狀態(tài)”讓我浪費(fèi)了整整一個(gè)下午的時(shí)間,只知道MAINTENANCE是程序的反安裝狀態(tài),而這個(gè)反安裝狀態(tài)是有可能包括“重裝”、“修復(fù)”和“卸載狀態(tài)”的,當(dāng)然我可以讓反安裝界面只能處于卸載狀態(tài),只要把前面創(chuàng)建卸載快捷方式中的szfilename = UNINSTALL_STRING +" /UNINSTALL"; 這句話改成szfilename = UNINSTALL_STRING +" /REMOVEONLY"; 就可以了;但是試驗(yàn)出來是不等我確認(rèn)刪除,這個(gè)服務(wù)就卸載掉了,原因是這個(gè)界面一出來就是MAINTENANCE狀態(tài),而程序捕獲了這個(gè)狀態(tài)后,是不管我是否按下了確認(rèn)按鈕就會(huì)去做這個(gè)操作了。

后來想在Onbegin里添加一個(gè)SdWelcomeMaint函數(shù)的判斷,結(jié)果是判斷倒是成功的,但是多了另一個(gè)重復(fù)界面。

看來這個(gè)思路可能是有問題的,然后滿地google之,還是吞硬幣的小豬的一篇文章給了啟發(fā),原文地址找不到了,只找到了這篇 http://school.ogdev.net/ArticleShow.asp?id=1699&categoryid=7 ,這里面其實(shí)是談反安裝時(shí)候不執(zhí)行OnMaintUIBefore函數(shù)的問題,我想既然這個(gè)函數(shù)是反安裝時(shí)候“應(yīng)該執(zhí)行的”,那么就看看這個(gè)函數(shù)吧。

于是 打開Before Move Data | OnMainUIBefore

clip_image010

打開一看,大喜過望,這個(gè)函數(shù)里明明白白地顯示了反安裝時(shí)候的所有界面。

于是順著向下看,找到Dlg_SdFeatureTree。

clip_image012

這里紅色圈出來的一行代碼明確地告訴我們:如果為反安裝狀態(tài),那么卸載所有組件!OK,代碼只要添在這里就可以了。

clip_image014

這里就運(yùn)用了一個(gè)函數(shù)LaunchAppAndWait來達(dá)到目的。其實(shí)一開始我還在想是不是要寫批處理文件來執(zhí)行呢,結(jié)果是不需要,直接寫在這個(gè)函數(shù)里就可以了。

LaunchAppAndWait ( szProgram, szCmdLine, nOptions );

參數(shù)一:szProgram,要運(yùn)行的程序。在Help里有這樣一句解釋:想在命令行里指定要運(yùn)行的程序,那么可以對這個(gè)參數(shù)傳空值

參數(shù)二: szCmdLine,命令行參數(shù);很奇妙的參數(shù),這里我們就可以寫入我們想要的批處理語句了。

參數(shù)三:靜態(tài)變量,操作類型,這里L(fēng)AAW_OPTION_HIDDEN可以使批處理窗口隱藏掉,如果使用了LAAW_OPTION_WAIT,就會(huì)看到一個(gè)命令行窗口一閃而過,讓人十分不爽。

于是,折騰了一下午的問題,就靠這短短的兩分鐘就解決了…

下一篇: 一個(gè)完整的安裝程序?qū)嵗瑵衫怪Q笈癯銎罚ㄎ澹?--補(bǔ)遺

一個(gè)完整的Installshield安裝程序?qū)嵗瑵衫怪Q笈癯銎罚ㄋ模?--高級(jí)設(shè)置二


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會(huì)非常 感謝您的哦!!!

發(fā)表我的評論
最新評論 總共0條評論