InstallShield学习总结

来源:互联网 发布:英文演讲视频软件 编辑:程序博客网 时间:2024/06/05 10:35

http://chuliheng2002.blog.163.com/blog/static/86041989201152211953427/
http://blog.csdn.net/wanlixingzhe/article/details/7181307
http://wenku.baidu.com/view/ec7200df7f1922791688e875.html

http://www.doc88.com/p-030414869388.html

转载上述资料中提取的内容,同时进行汇总,加入自己实际运用后的补充总结。

安装需求功能点汇总:

1. 添加许可协议文本

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这个函数,显示的界面符合大多数目前流行的安装界面的习惯。

2.判断是否安装了本软件的先决软件或运行环境

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) then

if(LaunchAppAndWait (SRCDISK^"jdk\\jdk-6u4-windows-i586-p.exe","", LAAW_OPTION_WAIT)<0)then

MessageBox ("You haven't installed JDK 1.6.0_04 yet! ", INFORMATION);

endif;

endif;

小结:至此,判断运行所需软件的功能结束,用户可以自己试验一下判断多个软件。用法就是重复上述代码功能,仍在OnBegin()函数体内执行。 

3.注册一个WINDOWS系统服务

用过打包软件的朋友都知道我们在制作安装包的时候经常需要往windows系统里面注册一些服务,以方便管理。用Advanced Installer的话就太简单了,直接在向导里面填入服务名称和描述之类的就可以自动实现的,但是用installshield的朋友都知道installshield是没有这个向导,那么我们就只能通过代码来实现了。

一个windows服务的信息:

1.注册的服务名称:也就是我们在任务管理器里面看到的进程名称

2.服务的显示名称:就是我们在服务管理器上面看到

3.服务的描述信息:这个就不用说了(可有可无)

4.服务调用的程序路径:这个服务运行那个程序

5.服务器的启动类型:是否自动,还是手动,或者是禁用

有了上面这些信息我们就可以开始着手写代码了

function OnFirstUIAfter()
STRING szServiceName, szServiceDisplayName, szServiceDescription, szServicePathFile,  szStartServiceArgs;
NUMBER ,bStartService,
begin

ShowObjWizardPages(NEXT);
szServiceName=”hiadmin”;

//注册的服务名称
szServiceDisplayName=”阳光网志”;

//服务显示名称
szServiceDescription=”阳光网志,个人博客而已”;

//服务描述
szServicePathFile=TARGETDIR^”\\Server\\test.exe”;

//程序文件的路径,安装路径下的Server目录下的test.exe文件
bStartService=0;

//启动类型为自动
szStartServiceArgs=”";
ServiceAddService ( szServiceName, szServiceDisplayName, szServiceDescription, szServicePathFile, bStartService, szStartServiceArgs );

ServiceStartService(szServiceName,”");

//在最后加了一行代码,当程序安装完成后启动这个服务


4.DLL自动注册

由于开发中使用了dll控件,如果目标机器上没有对应的dll文件会造成应用程序无法正常运行,经过查找资料,发现InstallShield是自己带了自加载dll等自动注册控件的功能。流程如下:

第一步:Project Assistant —> Application Files —>Destination Computer
右键[Destination Computer]->Show PreDefined Folder->选中“System Folder”
/*注意,[SystemFolder]其实就对应目标机器的system32目录,具体可以查看帮助,或者在Installation Designer-Media-Path Variables里面查看其他变量定义
第二步:左边列表将显示[SystemFolder],将需要注册的*.ocx和*.dll文件拖到这个文件夹中,选中这些文件,右键属性,勾中"Self Register"

第三步:在Installation Designer —>Orgnization—>Setup Design—>Default Feature ,Default Feature目录下有你刚才设置自动注册的文件,选中该文件名,右边就有相关的属性,Shared 属性选为Yes,Permanent属性选为Yes(卸载时就会保留在系统中),这样你的注册控件就能顺利在目标机器上注册,能被其他程序锁使用,反安装的时候也不会被卸载掉。


4. 根据用户选择的组件,从外部文件夹拷贝相应的文件到安装目标路径的文件夹中

这个用途常见于配置文件和授权文件的应用,同一程序,授权给不同的用户,只需要不同的配置和授权文件。如果将配置和授权文件每次都打包在安装程序里,那么变更一个用户就需要重新打包一次,这是一个浪费时间和精力的行为。如果将授权和配置文件(当然内容是加密过的)放在外部文件夹中,每次安装的时候从这个文件夹中读取拷贝,那么会是一个比较通用型的安装程序。

另外,本程序的好几个feature用到了相同的库,如果直接在feature下加库文件也可以,但是每一个feature都加一次这个库文件夹,整个安装程序就会变得很庞大,因此比较理想的情况是选到了这个feature的时候从外部拷贝这些库文件。

这里我们先不包括文档这个feature的说明,文档feature另有详细说明。

1. 这个功能需要在OnFirstUIAfter()函数体中实现,选择After Move Data | OnFirstUIAfter选项,即在选择了移动哪些数据后这个操作生效。

clip_image002

2. 之前我们已经接触过了如何判断是否选择了某个Feature,这里也需要判断是否选择了某个Feature,并且根据这个Feature来拷贝对应的外部文件

首先定义一些需要的变量并且进行赋值,蓝色字体即为所定义变量和赋值语句

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\\*.*";

//拷贝的目的地,目标文件夹

szTarFolder1 = "lib\\*.*";

szTarFolder2 = "databaselib\\*.*";

3. 对每一个feature进行判断,进行相应的文件拷贝

在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) 这个函数用于判断用户是否选择了某feature。Help里对这个函数是这样描述的:FeatureIsItemSelected ( szFeatureSource, szFeature );

参数一:szFeatureSource,大意好像是feature的来源,具体不是很明白到底指什么,反正help自带的例子里写的MEDIA照抄没有错。

参数二:szFeatureName1,就是 feature的名字了

如果返回值为1,则说明用户选择了这个feature

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

CopyFile(SRCDISK^szSrcFile1, TARGETDIR^szTarFolder1);

拷贝文件的函数。Help里是这样描述的:CopyFile ( szSrcFile, szTargetFile );

参数一:szSrcFile,源文件,可带路径,要带有扩展名的文件名。当这个文件带路径时,则从这个指定路径下拷贝指定的文件;如果是不带路径的,则直接从安装文件所在盘的盘符下寻找指定的文件来进行拷贝。如果要拷贝某个文件夹下的一系列文件,可以使用通配符。

参数二:目标文件,可带路径,要带有扩展名的文件名。当这个文件带路径时,则将文件拷贝到这个指定路径下;如果是不带路径的,则将文件拷贝到安装路径下。支持通配符。

小结:上面这段代码的意思是:如果用户选择了某个feature,则从安装程序所在的盘下面的一些文件夹下拷贝文件到目标路径下的一些对应文件夹下。这里记住拷贝文件一定要带上文件的全名,包括扩展名。 

5.安装结束时,显示README.TXT文件内容

这是个很有用的设置,但是在InstallScript工程里不是默认自带的,因此也需要脚本编程实现。

这段代码的位置是在After Move Data | OnFirstUIAfter()的函数里实现的

1. 首先,在安装的时候把readme.txt文件从源盘拷贝到安装目录下。把这段代码拷贝到After Move Data | OnFirstUIAfter()的begin和end;之间即可。README.TXT文件放置在源盘的根目录下,并且在安装时拷贝到安装目录下。

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

这段代码意味着当安装执行的时候,这个文件总会被拷贝过去。

2. 创建一个Finish界面,并在界面上设置询问是否显示readme.txt文件的选项。

之前我们看到当我们第一次选取了After Move Data | OnFirstUIAfter()选项时,系统会为我们创建如下代码(当然不创建也不要紧,自己敲就是了)

这个就是结束界面。Installscript工程默认安装完毕后,界面直接消失,而不会出现一个带有Finish按钮的界面让用户点击了以后才结束整个安装过程。

这段代码就是创建了一个Finish界面了,我们要对这段代码进行改造,使之出现一个是否显示readme的选项。

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);

使默认的安装设置对话框无效。

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

ShowObjWizardPages(NEXT);

顺序执行这个OnFirstUIAfter()的代码,如果参数为BACK,则逆序执行

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

SdLoadString(IFX_SDFINISH_MSG1);

返回参数所关联的字符串值,这个参数应当是一个资源ID。

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

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

参数一:szTitle,即显示在界面上的左上角的标题,如果传空值,则显示默认值

参数二:szMsg1,安装结束的界面上允许最多有两个可选项,这个参数可以显示第一个选项的一些相关说明,如果赋空则不显示任何说明

参数三:szMsg2,解释同上

参数四:szOption1,选项名。这个是一个Checkbox,如果设置为空则不显示,如果赋值则显示一个Checkbox并且在这个Checkbox旁边显示这个所赋的简短值。

参数五:szOption2,解释同上。

参数六:第一个选项的状态,如果设置为TRUE,则第一个选项Checkbox默认为选中状态,FALSE则为未选中状态。

参数七:第二个选项的状态,解释同上。

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

if (bOpt1=TRUE) then

判断是否选择了checkbox。如果用户选择了这个选项,则进行下一步操作

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

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

为了保险起见,需要进一步判断一下这个readme.txt是否被拷贝进来了

Help里解释如下:

FindFile ( szPath, szFileName, svResult );

参数一:szPath,文件所在的路径,不包含文件名

参数二:szFileName,文件名,包含扩展名

参数三:szDocFile,返回的文件名

如果查找成功,则返回值为1

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

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

打开readme文件

Help里没有对这个函数的专门的解释,但是有个例子,以至于我看了好几遍才看懂要表达的意思

参数一:应用程序,也就是你用什么工具来打开第二个参数指定的文件。我们这里用记事本打开,因此要引用一下Windows下自带的程序Notepad.exe,路径为WINDIR^"Notepad.exe" 。如果是一些不是Windows自带的程序,比如PDF,DOC,还需要从注册表里得到所安装的目标位置,从这个目标位置得到要用的工具。有兴趣的朋友可以试验一下。

参数二:要打开的文件,带路径,包含扩展名

小结:这个界面我曾经试图写在OnFirstUIBefore()里的结尾部分,用Dlg_SdFinish来实现,但是总是发现虽然结束界面能出来,但是上一个界面不能消失掉的情况。因为这个资料也不好找,仓促之间试验出上述所说的办法,估计是等安装界面结束后补上一个界面来达到这个效果的;其实我本人是比较讨厌结束的时候有这么一个要看readme的选项的,一般自己装到这种软件,都是去掉钩选框,不看readme的;但是如果直接结束掉,不出这个结束界面又觉得提示不足,有时候不能确定安装程序有没有结束,所以私下里还是比较想去掉readme选项,而直接显示一个只有一个finish按钮的界面的。


6.读写注册表,设置环境变量

之前提到了,要在安装本系统时判断是否安装了JDK,在最初笔者所做的安装盘中,还要让用户手动地去为JDK设置环境变量JAVA_HOME,设置环境变量对于外行来说简直就是天方夜谭,在JAVA论坛新手区最常见就是求助设置环境变量的问题了,因此,这个功能最好还是由安装程序代劳为妙。

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是否被安装,只有通过注册表来判断啦,同理可得,要是自己开发的一套系统中有多个安装程序,而且相互关联,就得朝注册表里写入值了。

如果返回值为1,则说明存在该键值;

如果返回值小于0,则说明该键值不存在。

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

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

因为设置JAVA_HOME环境变量需要JDK的安装位置,所以要根据注册表来寻找到这个安装位置,而幸运的是,该键值下的JavaHome键名所对应的值就是JDK的安装位置。

Help里对该函数的解释如下:

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

参数一:szKey, 要查找的注册表的键,这里我们查找SOFTWARE\\JavaSoft\\Java Development Kit\\1.6.0_04

参数二:szName,一些注册表键下面会有一些键名,如果你去看一下我们查找的键,会发现该键下存在多个键名,这里我们只要查找JavaHome键名对应的值,因此,指定szName为JavaHome

参数三:nvType,返回该键名对应的值的类型,比如字符型,数字型;当时笔者还犯了一个错误,以为这个参数是需要笔者指定类型的,因此写了一个REGDB_STRING,结果编译出错,搞了半天发现这个参数是个返回值,汗一个。

参数四:svValue,返回该键名对应的值

参数五:nvSize,返回该键名对应的值的字节数

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

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

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

如果搜索注册表发现JDK已经安装了,就去读一下注册表的键值,并且设置我们所需要的环境变量,这两句话就是用来设置环境变量的。

环境变量也是利用注册表键值设置函数RegDBSetKeyValueEx来实现的,这个键是一个特殊的位置,一定是"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment",我们对该函数进行进行详细说明。

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

函数作用:设置注册表键值

参数一:szKey注册表里的键,这里,我们需要设置环境变量的值,因此这里固定传值为"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment"

参数二:szName,键名,这里我们需要设置的是名为JAVA_HOME的环境变量

参数三:nType,被设置的键的类型,这里是字符串型,并且不带%PATH%之类的符号,也不转行

参数四:szValue,就是键值了,这里我们已经从上面得到了JDK的安装路径,就把安装路径传进去

参数五:nSize,help里说明如果键类型为REGDB_STRING, REGDB_STRING_EXPAND, 或者 REGDB_NUMBER时,都可以设置该值为-1,installshield会自动为我们计算正确的长度,而当键类型为REGDB_BINARY 和REGDB_STRING_MULTI时,就必须传该键值的实际大小进去。

小结:Installshield默认键值位置是在HKEY_CLASSES_ROOT下的,因此在这里,我们需要在进行搜索键值和设置键值的操作之前使用RegDBSetDefaultRoot(HKEY_LOCAL_MACHINE);这句话来设置一下默认的根键值为HKEY_LOCAL_MACHINE;另,在网上看了一个帖子,当时匆匆看了一下,说是设置的键值会在反安装时候卸载掉,我倒是没有在自己的安装程序里发现这个问题,不过可以研究一下;作者说当时为了解决这个问题,是在代码头加上DISABLE(LOGGING);代码尾加上ENABLE(LOGGING)来实现的,虽然我没有碰到这个问题,但是还是很感谢这位作者,因为当时他也说了,根本找不到资料,自己啃了天书般的HELP来解决,而自己一旦解决了问题,就分享出来,以便于大家少走弯路。 

7.完美卸载

function OnFirstUIAfter()

STRING szfilename,szFolder ,szmsg1,szmsg2;

NUMBER nresult;

begin

//创建删除快捷方式

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";

参数一:UNINSTALL_STRING这个静态变量指向的就是我们的安装程序,也就是setup.exe,不过指向的位置不是我们的源盘里的setup.exe,而是C:\Program Files\InstallShield Installation Information\{0D9DF66A-44E5-4754-A522-2AD6C9D5CDBE}\setup.exe;Installshield创建的安装文件在安装时总会在这个文件夹里创建对应信息,一长串数字型序列码就是安装程序的Product ID。利用这个setup.exe就可以进行反安装

参数二:/UNINSTALL,告诉程序启动这个setup.exe时为非安装状态,即修复、重新安装和卸载状态。

因此,这个字符串的值应该是这种形式:

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

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

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

寻找到“.exe”这个字符串在szfilename这个字符串中的位置。

Help里对这个函数的描述如下:

StrFind (szString, szFindMe);

参数一:szString,被查找的源字符串

参数二:szFindMe,要查找的字符串

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

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

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

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

如果要查找的字符串存在,那么源字符串就是正确的;这两句语句就对源字符串进行截断,得到想要的子串。

szmsg1应该为C:\Program Files\InstallShield Installation Information\{0D9DF66A-44E5-4754-A522-2AD6C9D5CDBE}\setup.exe

而szmsg2应该为 -runfromtemp -l0x0409 /UNINSTALL

Helpl里的解释如下:
StrSub ( svSubStr, szString, nStart, nLength ); 
参数一:svSubStr返回的结果字符串
参数二:szfilename源字符串
参数三:开始截断的位置。如果指定的位置大于整个被解析的字符串长度,则返回一个空字串。
参数四:结束截断的位置。如果指定的位置大于整个被解析的字符串长度,则默认为结束截断的位置是字符串的结尾处。

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

LongPathToQuote(szmsg1, FALSE );

LongPathToQuote(szmsg2, FALSE );

这两句的作用是对上面解析出的两个子串脱去括号。原本笔者参考的例子里没有这两句,在自己计算机上运行正常,但是换了一台计算机后,创建出的卸载快捷方式无效,查看快捷方式的指向发现和原来计算机的指向略有差别,查阅了一些资料得知Windows下的长文件名就有这个缺陷,每个操作系统解析出来的可能会有所不同,主要是引号的麻烦。在笔者自己的计算机上获取的长文件名是不带引号的,因此,解析正确;而测试的那台计算机上获取的文件名却是带引号的,这就造成了解析后拼凑的字符串的差别。这里就要显式地为解析出来的子串脱一下引号。

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

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

拼凑出正确的可执行文件的长文件名,带路径,包含扩展名

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

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

添加一个快捷方式到开始 | 所有程序 | Test下;照抄即可。

小结:可能读者会比较奇怪这一段代码的写法,因为中间那段if endif;代码看上去简直就是多此一举。在Installshield7之前,一直是这样写的:

szfilename = UNINSTALL_STRING +" /UNINSTALL";

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

从Installshield8开始,长文件名一直有引号封闭不正确的问题,因此if endif;代码完全是为了解决这个问题而存在的,而上面提到的两个脱去引号的语句,是笔者在前人基础上修改加上的,因为发现解析出来的字串要是不脱一下括号还是有问题。

这个快捷方式运行的时候,出现界面和在安装完毕后再次运行安装程序出现的界面相同。选择Remove即可进行卸载。

clip_image008

这个卸载不会把程序运行时产生的文件卸载掉,比如日志文件、配置信息文件等;会把安装目录中所有从安装程序中安装的文件都卸载掉,包括安装时从外部拷贝的文件。利用Project Assistant创建的卸载快捷方式则无法卸载掉安装时从外部拷贝的文件。