随着软件行业的蓬勃发展,各种ERP软件、财务软件、杀毒软件等商业软件如雨后春笋,在力求高质量高性能的软件产品背后,软件产品的安装也越来越受到重视。软件安装是软件产品的门面,也是给用户的第一印象。当使用安装工具制作出界面美观,安装快捷,配置简单的具有专业水准的软件安装后,一定会对产品的发布与推广起到事半功倍的效果。 InstallShield无疑是当前较流行的安装程序开发工具,在众多的安装制作软件中,功能最强大、最灵活。本文将介绍如何利用InstallShield和数据库DDL语言,快速简捷地制作带有数据库的应用软件产品安装包的另类方法和技巧。(以下将InstallShied简称为IS,运行调试环境为InstallShield12)
一、方法
有些ERP软件并不是在软件安装的时候就将用户的数据库服务器环境配置妥当,而是在安装结束后由实施人员或用户再进行环境的配置,那样势必会增加实施人员或用户的工作量并且给用户留下不易上手、使用麻烦等印象。还有些软件会连同ADO组件一同打包进安装程序,然后在脚本中编写程序来实现数据库的安装。虽然,这也是一种解决方案,但是这种方案还涉及到在IS的脚本中编写ADO程序的麻烦,而且还要在用户的机器上安装ADO环境,否则安装数据库无从谈起。本文介绍的制作方法将更为简单快捷,制作人员只需要简单了解数据库DDL脚本的编写,不需要懂得ADO编程技术,用户的服务器也无需安装ADO环境,就可以实现数据库应用程序的打包安装。
通常的实现方案,作法是将ADO组件放到IS中,并一同打包到用户的机器,同时在IS中编写复杂的ADO程序,用以执行备份、删除、安装数据库等操作。这种方式不仅对用户机器的环境有所要求,还要求制作人员必须懂得ADO编程,考虑复杂的用户环境,且安装效率不是很高。
本方案的设计思想充分利用IS的已有资源(窗体和函数),结合数据库DDL语言,对用户机器几乎没有额外的环境要求。主要工作集中在数据库DDL语言的编写上,这样只要对其稍有了解的人就可以制作数据库应用软件的安装包且提高了安装效率。
二、设计
实现思路是通过调用IS的窗体组件执行DDL语言,然后通过返回的状态来执行数据库的备份、删除、安装、升级等操作。在安装数据库时并不能简单的只执行安装操作,这样达不到实用效果。因为用户可能安装过较早期软件版本或存在同名数据库等现象,所以要在安装之前对用户的数据库服务器进行判断,比如是否存在同名数据库,对数据库进行全新覆盖安装还是升级安装,原数据库是否需要备份,是否正在使用等。对数据库部份的操作已经明确了,那么现在问题的焦点就落在了如何得到这一系列的判断结果呢?其实方法很多,但目的只有一个,即使IS能够在安装时作出正确的判断,然后执行安装。
这里给出两种建议:第一种将这一系列的判断结果写入注册表的某个临时路径下,待安装时由IS来判断注册表中的这一系列键值,然后作出正确的安装,在安装完成之后删除;第二种将判断结果写到临时目录中,待安装时由IS来判断临时目录中记录的信息来作出正确的安装。考虑到用户机器大多装有实时监控软件,特别是对注册表键值的修改有实时监控,为避免造成不必要的麻烦,建议采用后者。下面做一实例来实现一个SQL Server数据库的安装,在实现过程中还有一些技巧说明。
三、实现
1.新建工程
新建一个InstallShield工程。工程类型为InstallScriptProject,工程名为TestDBSetup,如图1所示:

图1新建工程
设定安装程序基本信息等,如图2所示。其中目标路径(TARGETDIR)的初始值可设置为d:\DBTestSetup,在安装时可以选择定制安装来修改此安装路径。

图2 设置基本信息
2.数据库DDL脚本编写
新建一个连接,如图3所示名为DBSetupTest,设定其Database Server为Microsoft SQL Server;版本要求为SQL Server2000+SP3及以上版本。

图3 建立数据库连接
在此连接下新建两个脚本,CheckDataBase.sql及InstallDataBase.sql。本文并非讲解数据库DDL语言,因此以下语句只做简单注释,具体用法可参考SQL Server联机帮助及相关文档。
编写第一个脚本CheckDataBase。该脚本检查用户SQL Server的版本及是否存在要安装的数据库及该数据库是否正在使用。如果存在数据库则在临时目录下创建一名为DBTestExists的文件夹,安装时检测不到此文件夹则表明不存在要安装数据库,直接安装数据库即可。若检查到此文件夹表明已存在要安装的数据库,同时弹出提示告知用户“数据库已存在,是否覆盖安装”的信息。如果数据库存在且用户正在使用中,则在临时目录下创建一名为DBTestUsed的文件夹,安装时检测到此文件夹则可提示用户“数据库正在使用,不能覆盖安装或升级安装”。若检测不到此文件夹表明未被使用,可以进行覆盖或升级安装。
检查数据库版本 判断用户机器的环境是SQLServer2000还是SQLServer2005。因为SQLServer2005的安全设置问题,系统存储过程需要被手动地设置为打开状态。SQLServer2000不存在这个问题。
use master --使用系统数据库
declare @databasename varchar(100) --定义数据库变量名
select @databasename = 'DBSetupTest' --赋值
if charindex('2005 - 9.00',@@version)> 0 --判断SQLServer版本
begin
--如果是SQLServer2005则设置xp_cmdshell存储过程为可用
EXEC sp_configure 'xp_cmdshell', 1
RECONFIGURE
end
--检查数据库是否存在,存在则在临时目录中建一文件夹,
--方便IS在安装时能做出正确的判断。
if exists(select name from sysdatabases where name=@databasename)
begin
--存在则在临时目录下建一个名为DBTestExists的文件夹
exec xp_cmdshell 'md SUPPORTDIR\DBTestExists\'
--检查数据库是否正在使用,在使用则在临时目录中建一文件夹;
If exists(select spid from master..sysprocesses where dbid=db_id(@databasename))
Begin
--在临时文件夹下建一个名为DBTestUsed的文件夹
exec xp_cmdshell 'md SUPPORTDIR\DBTestUsed\'
end
end
go
编写第二个脚本InstallDataBase。该脚本首先检查是否存在要安装的数据库,存在则按时间进行备份,然后删除旧数据库,安装新数据库;否则直接安装新库。安装数据库的方法本示例采用了直接还原备份好的数据库文件的做法,也可以采用其他方法,比如先执行创建数据库脚本,然后执行初始化脚本等方法。笔者认为使用前者方便快捷,且在不同数据库版本之间还原不会存在版本差异的问题,使用后者可能存在某些字段类型升级等因素而造成不必要的麻烦。
use master
declare @databasename varchar(100) --数据库变量名
declare @datapath varchar(4000) --数据库备份文件路径
declare @name varchar(8000) --数据库备份文件名
select @databasename = 'DBSetupTest' --待安装的数据库名赋值
--检查数据库是否存在
if exists(select * from sysdatabases where name = @databasename)
begin
select @name=(CONVERT(char(8),GETDATE(),112)+CONVERT(char(2), GETDATE(),108)+right(CONVERT(char(5),GETDATE(), 108),2)+right(CONVERT(char(8), GETDATE(), 108),2))
select @name='TARGETDIR\database\'+@databasename+@name+'.bak'
BACKUP DATABASE @databasename TO DISK = @name --按时间备份数据库
exec('drop database '+ @databasename) --删除旧数据库
end
select @datapath ='TARGETDIR\database\'+@databasename --数据库文件路径
--还原数据库
restore database @databasename from disk=@datapath with REPLACE,
move 'DBTest_data' to 'targetdir\database\DBTest_data.mdf',
move 'DBTest_log' to 'targetdir\database\DBTest_log.ldf'
此步骤非常关键,即将上两步中的用红色字体标识的变量用IS中的全局变量予以替换。否则安装程序不知道在哪个临时目录下创建文件夹,也不知道数据库应用程序会安装到硬盘上的哪个目录中。本示例并不将数据库安装到SQL Server默认路径下,而是将数据库安装到用户所选择的安装路径下的database文件夹下,以方便用户管理。SUPPORTDIR替换为<SUPPORTDIR>,TARGETDIR替换为〈TARGETDIR〉,如图4所示。SUPPORTDIR在IS中是安装程序的临时目录,TARGETDIR是IS中用户最终选择的安装路径。

图4 替换全局变量
然后,设置文件的组织形式,添加将要打包的组件。如将安装一个名为DBTestSetup的数据库,因此要将DBTestSetup数据库备份文件加入到DefaultComponent\DataBaseFile页下的DataBaseFile组件里。然后调整组件的级次位置。因为CheckDataBase.sql主要是检测脚本,要在安装程序开始时执行,而InstallDataBase.sql主要是安装脚本,要在数据库文件DBTestSetup被复制到目标机器后执行。且用户可能存在不覆盖数据库安装的情况,因此InstallDataBase.sql脚本还存在根本不运行的情况。因此设置级次关系如图5所示。同时设置DataBaseFile组件的安装路径为TRAGETDIR\DataBase,表示在目标路径下的DataBase文件夹下安装数据库文件。设置DataBase项的OnInstalling为Dbtest_installing,表示在安装此项之前将会调用Dbtest_installing函数,该函数将在下步说明。

图5 设置级次关系图
InstallShield脚本程序的编写。此处需要简单地加入三个新函数,修改原脚本函数OnSQLComponentInstalled和OnBegin。下面对图6所示的各函数具体功能。

图6 代码中的函数
DelDatadir函数:删除临时文件夹,包括通过CheckDataBase.sql脚本生成的因存在数据库而生成的文件夹DBTestExists和数据库正在使用而生成的文件夹DBTestUsed,在安装初始化时调用,保证IS判断的准确性。
CheckDatadir函数:检查文件夹是否存在,包括通过CheckDataBase.sql脚本生成的因存在数据库而生成的文件夹DBTestExists和数据库正在使用而生成的文件夹DBTestUsed,在安装初始化时调用。
DBTest_Installing函数:在安装DataBase组件之前调用,以提示用户如何操作。包括是否覆盖数据库等,返回用户的选择结果。
OnSQLComponentInstalled函数:要修改的函数。如果检查到用户机器没有安装或用户选择了覆盖数据库安装则执行安装数据库脚本(InstallDataBase),否则跳过此组件的安装与运行。
OnBegin函数:在安装之前调用删除临时文件夹函数。
具体脚本程序如下:(讲解IS语法并非本文讨论的范筹,因此以下只作简单注释,具体请参考IS联机帮助及相关书籍)。
#include "ifx.h"
//定义常量
#define C_DataExists "DBTestExists"
#define C_DataUsed "DBTestUsed"
#define C_DataInstallCript "InstallDataBase"
BOOL DataExists; //数据库是否存在
BOOL DataUsed; //是否存在使用
BOOL overflag; //是否覆盖
export prototype DelDatadir(STRING,STRING); //删除检查数据库用的文件夹
export prototype CheckDatadir(); //检查数据库目录是否存在
export prototype DBTest_Installing(); //在安装数据库之前执行
function OnBegin()
begin
//在安装程序初始化时调用
DelDatadir(SUPPORTDIR,C_DataExists);
DelDatadir(SUPPORTDIR,C_DataUsed);
end;
function DelDatadir(szPath,szDir) //删除检查数据库用的目录
STRING svResult;
begin
DeleteDir(szPath^szDir, ALLCONTENTS);
end;
function CheckDatadir() //检查数据库目录是否存在
STRING svResult;
begin
if (ExistsDir(SUPPORTDIR^ C_DataExists)= EXISTS) then
DataExists = TRUE;
if (ExistsDir(SUPPORTDIR^ C_DataUsed)= EXISTS) then
DataUsed = TRUE;
else
DataUsed = FALSE;
endif;
else
DataExists = FALSE;
endif;
end;
function DBTest_Installing()
begin
overflag =TRUE;
CheckDatadir();
if DataExists then
//如果存在
if (AskYesNo("您已经安装了DBTest知识库,是否覆盖?",YES) = NO) then
overflag=FALSE;
else
//检查数据库是否在使用
overflag=TRUE;
if DataUsed then
MessageBox("其它程序正在使用该数据库,请先停止后再继续本安装",WARNING);
abort;
endif;
endif ;
endif;
end;
function OnSQLComponentInstalled(szComponent)
begin
if( SQLRTGetBatchMode() ) then
// Queue up the component to run later
ISSQLServerAddToBatchInstall( szComponent );
else
// 加入此段代码〈
if (szComponent!= InstallDataBase) then
nResult = SQLRTComponentInstall( szComponent );
else
if overflag then //覆盖数据库
nResult = SQLRTComponentInstall( szComponent );
else
nResult = 0; //跳过不用执行。
endif;
endif;
×××× //星号为原系统产生代码
endif;
end;
编译脚本并打包。点击Media的Release选项,根据向导创建一个安装包,Build之后就完成了一个带数据库应用软件安装包的制作。
在一台装有SQL Server2000及以上版本的机器上运行安装包,即可完成安装。安装完成之后可以在SQL Server2000企业管理器中查看到已经装有一个名为DBSetupTest的数据库。如果没有修改默认安装路径,可以在D:\DBTestSetup目录下装有一个readme.txt文件和database文件夹,数据库文件就存放在此文件夹中。
四、结语
InstallShield脚本相当灵活,功能也很强大,可以编写更复杂的脚本程序。要为软件产品制作出精美的安装包,还需要用户深入钻研与实践。(原代码参考附件中的TestDBSetup.rar)
|