大家一定有过这样的经历,每当我们殚精竭虑为用户开发完一个系统之后往往都是派一名技术员到用户那里进行应用软件的安装,数据库的配置,这些看似简单的配置对一般用户来说可不是件容易做的事。这不仅给那些喜欢力求完美的程序设计师带来一点遗憾外,而且也使我们设计出来的软件缺少一种安全可靠感。如果我们在交给用户软件的时候能给他们一个象一些专业软件的安装程序,这不仅给用户留下了一个良好的第一印象,同时也节省了开发维护成本。
要说起制作安装程序的软件有许多,像InstallShield,CreateInstall,Easy Install,EasySetup,SetupBuilder 等等,可以举出一大堆来。但其中最为著名的当属InstallShield。目前世界上绝大多数的商业软件的安装盘都是用她来完成,比如我们可能天天在使用的Norton AntiVirus 2000。
InstallShield之所以很流行也许是因为程序员可以在通过编写脚本语言方便的进行Windows应用程序的注册表修改、ODBC配置等复杂的操作,以及她本身提供了一些相当智能化的功能。下面,就本人在实际开发中的一些所得和大家一起探讨,文中如有任何不妥之处还请同行们给予指教。
在讲述到数据库配置的一系列问题是以目前最常用的ODBC接口为基础,文中用到的实例是在WIN98系统中,InstallShield用的是Professional 6.21下开发和调试的,其中的应用程序实例用的PowerBuilder 7.0下开发的演示程序。
第一步:快速体验
首先,我们可以来快速体验一下用InstallShield制作的安装程序是什么样的。
当然,我不可能详细到怎样建立一个项目之类的问题,有关这方面的内容还请看InstallShield的入门教材。
第二步:;InstallShield初探
首先,用Project Wizard建立一个新的项目,取名Demo,同时在对话框中选择所有的选项,并且在语言选择中选择Chinese(PRC)和English,即简体中文和英文支持。当然你也可以选择其它语言,为了便于讨论我只选择了Chinese(PRC)和English。
运行一下试一试,一切都正常,只是一个空壳罢了,没有任何东西。
好了,战斗的号角已经吹响了,让我们集中精力个个歼灭。
一、语言选择
由于我们在生成项目的同时在语言选择中选择了Chinese(PRC)和English,因此,在图01中的下拉列表框中会自动出现中文,英文的选项。这一步由模板自动完成,我们不要做任何事情。
二、设置启动画面
在我们生成项目的同时,InstallShield会为我们缺省的放上一张启动画面。见图16。
从该图片上我们可以看出,一般情况下是放置一张能很好的代表你公司的图片,同时上面很清楚地标明你的应用程序的名称,版本,公司名称,版权归属等基本信息。毫无疑问,我们必须用我们自己的图片来替换它。替换的方法是,在Setup File面板中,打开Splast Screen,找到Language Independent,这时我们会发现在它右边的列表中有一个文件Setup.bmp,它就是图18的那一张,我们必须首先把它删除,然后再插入我们想要的那一张,在列表中任何一个位置点击鼠标右键,选择Insert Files,用一个BMP文件替换它就行了。注意,我们替换的图片的文件名必须是Setup.bmp。
另外,顺便提一下,在Splash Screen下除了Language Independent分支外,还有Chinese(PRC)和English 分支,如果在它们中放置不同的Setup.bmp文件,那么系统会根据用户在安装前选择了不同的语言而启动不同的背景,这样可以有效地避免多国语言的烦恼。同样,在设置法律声明(许可协议)和自述文件的时候也是一样的。
三、开始安装
这一步由系统自动给出,我们不要做任何事情。但是我们可以在这里进行软件安装前的准备工作。比如,操作系统判断,机器可用内存容量的检测等,这些往往被忽视。
当我们用项目向导生成的新项目时,InstallShield只为我们生成两个事件,分别是OnFirstUIBefore和OnMoving,它们的意义是,
OnFirstUIBefore:在应用程序第一次安装时为了搜集用户信息而弹出的对话框之前,应用程序所响应的事件。
OnMoving:该事件在当所选择的安装组件正在安装或反安装时响应。
显然,准备工作可以在事件OnFirstUIBefore中完成,但InstallShield同时还为我们提供了事件OnBegin,该事件在是在Setup脚本中第一个被触发。还记得图片02,上面有个进度条,显示了“正准备InstallShield(R)向导...”,当该进度条结束后就首先调用OnBegin,然后才是OnFirstUIBefore。因此,我们可以将一些前期的准备工作放到这里来完成。下面是如何具体实现。
1、首先,如何添加OnBegin事件?
InstallShield将事件分成三类:全局事件,组件事件和杂项事件。其中,全局事件的添加最为方便,只要将当前行光标移到Serup脚本的最后,然后写上
function OnBegin()
begin
... // 函数体
end;
就行了。
2、实现操作系统的识别
像其它编程工具一样,InstallShield也为我们提供了一些系统变量,利用这些变量我们可以轻而易举的得到一些像系统目录,语言代码,CUP类型等实用数据。
具体实现代码如下:
/*****************程序代码*******************/
Function OnBegin()
Begin
if ( !SYSINFO.bIntel) then
szMsg = "提示:该软件只能运行在Intel系列的处理器上!\n\n安装程序将终止";
MessageBox(szMsg, SEVERE);
abort;
endif;
if (SYSINFO.WIN9X.bWinMe ||
SYSINFO.WINNT.bWinNT ||
SYSINFO.WINNT.bWinNT4 ||
SYSINFO.WINNT.bWin2000) then
szMsg = "提示:该软件只能运行在WIN9X系统上才能确保程序的正常工作!\n\n是否继续安装?"; if ( !AskYesNo (szMsg, NO)) then
abort;
endif;
endif;
end
/******************结束*********************/
相关变量:SYSINFO
存放本机的一些系统变量,如操作系统,CUP类型等。有关系统变量的具体使用请参阅编程手册。
相关函数:AskYesNo
该函数弹出一消息窗口,用户通过按是或非来回答该窗口显示的问题。
3、实现内存容量的检测
在查阅了InstallShield系统变量后并没有找到有关内存容量方面的变量,因此只有通过调用函数来实现。
相关函数:GetSystemInfo()
该函数返回有关目标系统的诸如CUP类型,磁盘容量,当前日期,当前操作系统,内存容量等信息,返回的内存容量是以千字节为单位。有关函数的具体使用请参阅函数手册。
具体实现代码如下:
/*****************程序代码*******************/
if (GetSystemInfo (EXTENDEDMEMORY, nvFreeMem, svResult) < 0) then
MessageBox ("内存检测失败,安装程序将终止!.", SEVERE);
abort;
endif;
if ( nvFreeMem < 16384 ) then
szMsg = "该软件只能安装在16M以上内存的机器中。\n很遗憾,本机器可用内存不足16M!\n\n安装程序将终止!";
MessageBox (szMsg, SEVERE);
abort;
endif;
// end.
/******************结束*********************/
四、许可协议
选择Setup File面板,打开Language Independent,选择Operating System Independent ,在右边列表中找到License.txt文件,双击它输入自己的许可协议就行了。
五、Readme文件
同上,找到Infolist.txt文件,双击它输入需要说明的自述文件。
六、客户信息
看一看图片,不难发现当安装程序运行这一步时上面已经填上了缺省的用户名和客户名称,甚至连序列号都有。这是怎么回事呢?原来在前面我们介绍的事件OnFirstUIBefore的开始有一段代码,如下:
nSetupType = TYPICAL;
TARGETDIR = PROGRAMFILES ^@COMPANY_NAME ^@PRODUCT_NAME;
szDir = TARGETDIR;
SHELL_OBJECT_FOLDER = @FOLDER_NAME;
svName = "";
svCompany = "";
svSerial = "";
如果我们没有给svName和svCompany赋新值的话缺省的用户名和客户名称就是我们在安装Windows 时注册的用户和单位。当然,如果没有给svSerial赋值的话,图片上的序列号就为空。
相关系统变量:
PROGRAMFILES:存放Windows程序文件夹(Program files)的完整路径。不能更改。
七、序列号判断
用InstallShield安装模板制作出的安装程序在运行过程如果没有序列号是无法再继续下去的。但如果我们在序列号上不加任何检验的话,InstallShield会默认为任何字符都是有效的而不管它是什么,此时序列号也就毫无意义,除非你想让你的应用程序无限制的发布。因此,还是建议你在安装过程中对序列号进行一下有效性检验。方法很简单,在事件OnFirstUIBefore中找到Dlg_SdRegisterUserEx分支,见下列代码:
Dlg_SdRegisterUserEx::
szMsg = "";
szTitle = "";
nResult = SdRegisterUserEx( szTitle, szMsg, svName, svCompany, svSerial );
if (nResult = BACK) goto Dlg_SdShowInfoList;
在语句
if (nResult = BACK) goto Dlg_SdShowInfoList;
前添上相应的序列号校验程序
//*******增加序列号的检验
if ( svSerial != '199721') then
MessageBox("警告:输入序列号错误,请确认后重输!",SEVERE);
goto Dlg_SdRegisterUserEx;
endif;
//end.
就这么简单。声明一下,这里我只是给出的最简单的判别模式,较为安全的模式是在判别时对序列号进行必要的加解密转换,有兴趣的朋友可以自己去实现。
八、安装文件夹
在一般情况下,InstallShield默认的安装文件夹是存放Windows程序文件夹(Program files)的完整路
径,即C:\ Program files(如果Windows安装在C盘的话)。但是,在绝大多数情况下我们都希望客户将应用程序安装在单独的数据盘上,即不与操作系统安装在同一个硬盘分区里。一方面便于管理,另一方面也为了防止我们的程序因为系统盘格式化而受到连累。因此,我们常常会建议用户在安装应用程序的时候选择另一个硬盘,如D盘。
另外,我们有时希望我们所开发出来的应用程序安装在一个带有自己公司名称和应用程序名称的目录下。InstallShield默认的目录就做的非常好,她会建立一个诸如“C:\Program Files\ABCD软件工作室\Demo”的目录(在这里我虚构了一个公司“ABCD软件工作室”)。但,问题是,一旦让用户选择了其它的路径,或者硬盘,他们几乎都不会在目录上加上开发商和应用程序目录,“\ABCD软件工作室\Demo”。如果用户仅仅选择了磁盘而忘了选择任何目录的话,InstallShield就会将所有的程序和数据文件复制到磁盘根目录下,用户和我们当然都不希望这样。因此,为了避免这一切的发生我们只能寄希望于我们的安装程序能够有足够的智能来应付这一切,这样就不管用户是否选择了目录。实现方法如下。
在事件OnFirstUIBefore中找到Dlg_SdAskDestPath分支,如下
Dlg_SdAskDestPath:
szTitle = "";
szMsg = "";
nResult = SdAskDestPath( szTitle, szMsg, szDir, 0 );
TARGETDIR = szDir;
if (nResult = BACK) goto Dlg_SdRegisterUserEx;
我们首先需要定义一个临时字符串变量svTemp
STRING svTemp;
然后,将语句
nResult = SdAskDestPath( szTitle, szMsg, szDir, 0 );
TARGETDIR = szDir;
修改成如下:
svTemp = szDir;
nResult = SdAskDestPath( szTitle, szMsg, szDir, 0 );
if ( svTemp != szDir) then
szDir = szDir^@COMPANY_NAME ^@PRODUCT_NAME;
endif;
TARGETDIR = szDir;
该语句的功能是在用户选择的路径后加上单位和应用程序名称作为安装的目录(注:这里我并没有考虑到用户在选择了默认外的路径后加上单位和应用程序名称作为安装目录的情况)。其中,变量COMPANY_NAME 和PRODUCT_NAME中存放的是单位名和产品名,我们可以随时在资源面板中修改它的值。
变量TARGETDIR是指向在硬盘上目的文件夹的完整路径。当调用函数SdAskDestPath时,该函数会创建一个对话框来让用户选择应用程序要存放的目的路径。
不要以为加上了单位和程序的目录就万事大吉了,可别忘了既然让用户来选择安装的目的盘就很有可能出现磁盘空间不够大,从而导致程序的安装失败或安装后的剩余空间不够程序将来数据的存放等等。因此,必须在用户选择之后,程序进入下一步之前对目的盘的可用空间进行判断。在这里我假设我们的程序需要10兆的空间来存放目前的文件和未来几年可能的数据。
为了达到上述目的就必须知道:
1、用户最终选择的是哪个硬盘
2、该硬盘当前的剩余空间是多少
那么,如何来实现呢?幸好InstallShield为我们提供了足够多的函数来应付这一切。
函数一:GetDisk
该函数从指定的路径或带有路径的文件名当中提取该路径或文件所在的磁盘驱动器号。调用成功返回0,否则返回小于0的任何数。
函数二:GetDiskSpace
该函数返回指定磁盘或指定路径所在磁盘的剩余空间,用字节表示。
具体实现代码如下:
//*******加入磁盘剩余空间判断,目前判断剩余空间是否少于10M
//******* 10M * 1024千字节* 1024字节= 10485760 字节
GetDisk (szDir, svDrive);
lFreeSpace = GetDiskSpace (svDrive);
if (lFreeSpace < 10485760) then
MessageBox("警告:安装路径所在的磁盘空间不足10M!请重新选择!",SEVERE);
goto Dlg_SdAskDestPath;
endif;
// end.
我们只要将这段代码放入语句
if (nResult = BACK) goto Dlg_SdRegisterUserEx;
之前就行了。
九、安装类型
该步骤是由InstallShield自动给出的,一般情况下我们不必做任何事。三个安装选项:典型安装、压缩安装和自定义安装是根据我们在安装类型(Setup Type)的面板中预先设置好的值来进行的。如果用户选择了自定义安装,系统就会弹出如下界面,见图。
这是由系统自动给出的,我们用不着写一行代码,这是不是很棒?
不知道你有没有注意到窗口的左面有一个说明框,它是用来提示用户目前所选择的组件是干什么的。在缺省的状态下它是空的,需要我们来添加。添加的方法是在组件(Components)的面板中,每当我们点中一个组件在左边的列表中就会有一个描述栏(Description),它的内容就是上面提到的帮助信息,你只要将相对用户说的话写上去就行了。
十、程序文件夹
这一步由InstallShield自动给出,一般不要做任何修改。
十一、最后确认
我们一定还记得每当安装一个像Office等专业化软件时,在软件正式安装前系统都会给出一个列表框,其中列出了你所做的一切选择,包括软件的安装路径,需要安装的组件等,同时还包括了注册的用户名和单位等信息。在我们用InstallShield制作安装盘的时候也会有这一个列表框,但遗憾的是在缺省状况下InstallShield提供的这个列表框是空的,什么内容也没有,这需要我们给它加入。好在有函数ListAddString来帮我们的忙。
相关函数:ListAddString
该函数在字符串列表框的当前对象前后新增加一个字符串。调用成功返回0,否则返回小于0的任何数。
在脚本Setup.rul的事件OnFirstUIBefore中找到Dlg_SdStartCopy分支,如下:
Dlg_SdStartCopy:
szTitle = "";
szMsg = "";
listStartCopy = ListCreate( STRINGLIST );
//The following is an example of how to add a string(svName) to a list(listStartCopy).
//eg. ListAddString(listStartCopy,svName,AFTER);
nResult = SdStartCopy( szTitle, szMsg, listStartCopy );
ListDestroy(listStartCopy);
if (nResult = BACK) goto Dlg_SdSelectFolder;
// setup default status
SetStatusWindow(0, "");
Enable(STATUSEX);
StatusUpdate(ON, 100);
return 0;
在语句
nResult = SdStartCopy( szTitle, szMsg, listStartCopy );
前加上如下代码
//*******加入安装过程中用户所选择的主要步骤
// 如果不手动加入的话将会什么也不列出
ListAddString(listStartCopy,"客户信息:",AFTER);
ListAddString(listStartCopy,"用户名:" + svName,AFTER);
ListAddString(listStartCopy,"用户单位:" + svCompany,AFTER);
ListAddString(listStartCopy,"",AFTER);
ListAddString(listStartCopy,"程序安装路径:" + szDir,AFTER);
ListAddString(listStartCopy,"",AFTER);
ListAddString(listStartCopy,"程序文件夹:" + szfolder,AFTER);
ListAddString(listStartCopy,"",AFTER);
switch (nSetupType)
case TYPICAL : ListAddString(listStartCopy,"安装类型:典型安装",AFTER);
case COMPACT: ListAddString(listStartCopy,"安装类型:压缩安装",AFTER);
case CUSTOM: ListAddString(listStartCopy,"安装类型:自定义安装",AFTER);
endswitch;
//end.
其中svName、svCompany、szDir等变量我们可以在事件OnFirstUIBefore开始的变量定义中找到。从变量的名称我们很清楚的知道该变量存放的是用户名,单位,安装目录等。
十二、第一幅背景,第二幅背景——图片12,图片13
细心的朋友一定会注意到软件在安装过程中会出现两副不同的背景图片,图12和图13,这就是InstallShield 的显示区界面,我们可以称它为布告板。这看上去是不是很像在播放幻灯片?呵呵,我们可以和微软的比一比了(说句笑话)。言归正传,还是来说说是如何实现的吧。
布告板,它只有在文件被传输时才被激活。也就是说,当你调用ComponentTransferData函数来解压并拷贝时布告板才被显示,该函数是有系统自动调用,用不着我们来操心。
但是,你不能为一个布告板指定显示时间,InstallShield会根据整个程序的安装时间(指文件拷贝时间)自动的为每一个文件平均分配,但至少是2秒。如果你的安装程序仅持续20秒,而你却放置了25副图片,很显然系统只会显示前10副图片。
当你需要显示布告板之前还必须确保以下两个条件:
1、在你的脚本里,首先要确保在文件被传输前调用Enable(BACKGROUND) 和Enable(FULLWINDOWMODE),我们可以加在事件OnMoving中。形式如下:
function OnMoving()
STRING szAppPath;
begin
Enable(BACKGROUND);
Enable(FULLWINDOWMODE);
Enable(STATUSDLG);
PlaceWindow(STATUSDLG, 400, 10, LOWER_LEFT);
szAppPath = TARGETDIR;
RegDBSetItem(REGDB_APPPATH, szAppPath);
RegDBSetItem(REGDB_APPPATH_DEFAULT, szAppPath ^ @PRODUCT_KEY);
end;
相关函数:
Enable(BACKGROUND) :显示安装主背景窗口
Enable(FULLWINDOWMODE):设置主背景窗口为最大化。
2、在设置文件(Setup Files)面板中,在合适的目标语言和平台下放置具有特殊后缀名的布告板文件。默认的命名是:“Bbrd”,然后再跟一个数字,最后再加上后缀“.bmp”或“.wmf”。例如,我们现在要加三副BMP图片,则这三副图片的名称分别应该是Bbrd1.bmp、Bbrd2.bmp、Bbrd3.bmp。
好了,运行一下,很不错。我们可以利用这项技术在安装过程中播放类似幻灯片效果,就像安装Windows 一样。
十三、复制完成
这一步由InstallShield自动给出。
十四、重新启动