InstallShield使用——几个技术问题介绍
在安装程序中设置数据库,有两种方法:一种采用命令行附加数据库文件,一种是通过SQL Script脚本创建数据库,对于编写SQL Script过于复杂,或创建数据库时间过长时建议采用第一种方法。
1.1.1 命令行附加数据库
1. 将数据库文件(a_data.mdf和a_log.ldf)插入Support Files中;
2. 定义变量:
STRING psvSQLsvr, psvSQLusr, psvSQLpwd;
BOOL bvWindowsLogin;
3. 定义CreateDataBase函数:
prototype CreateDataBase(STRING,STRING,STRING);
4. 编写CreateDataBase函数:
function CreateDataBase(svSQLsvr,svSQLusr,svSQLpwd)
STRING szWaitTxt,szdatabase,path,path3;
begin
path=TARGETDIR^"Data""a_data.MDF";
path3=TARGETDIR^"Data""a_log.LDF";
szWaitTxt=" 正在创建数据库....";
SdShowMsg (szWaitTxt, TRUE);
Delay(3);
CopyFile(SUPPORTDIR^"a_data.MDF",path);
CopyFile(SUPPORTDIR^"a_log.LDF",path3);
szdatabase = "/U "+svSQLusr+" /P "+svSQLpwd+" /S "+svSQLsvr+" /Q ""exec sp_attach_db N'a' , N'"+path+"',N'"+path3+"'""";
if (LaunchAppAndWait("osql.exe", szdatabase,WAIT)<0) then;
MessageBox ("数据库创建失败!请确您的系统中已安装 Microsoft SQL Server 2000."n如仍无法解决,请联系系统供应商!",SEVERE);
SdShowMsg (szWaitTxt, FALSE);
return -1;
endif;
end;
5. 获取数据库的登录信息,如果没有创建数据库的脚本,安装程序不会弹出SQL登录验证界面,需要编写代码:
Prototype number SQLLogin(); //定义登录函数
function SQLLogin ()
number nResult, nSize;
STRING sMessage, sTemp;
begin
Dlg_Sql:
SQLRTInitialize2 ();
nResult = SQLServerSelectLogin(psvSQLsvr, psvSQLusr, psvSQLpwd, bvWindowsLogin );
if (nResult = BACK) return BACK;
nSize=MAX_PATH;
MsiGetProperty( ISMSI_HANDLE, "IS_SQLSERVER_STATUS", sTemp, nSize );
if(sTemp!="0") then
nSize = _MAX_PATH;
MsiGetProperty( ISMSI_HANDLE, "IS_SQLSERVER_STATUS_ERROR", sMessage, nSize );
if( nSize = 0 ) then
sMessage = SdLoadString( IDS_IFX_SQL_ERROR_LOGIN_FAILED );
endif;
MessageBox( sMessage, MB_OK );
goto Dlg_Sql;
endif;
return 0;
end;
6. 在OnFirstUIBefore事件中调用SQLLogin函数:
Dlg_SdSQL:
nResult = SQLLogin();
if (nResult = BACK) goto Dlg_Custom;
7. 在OnEnd事件中调用CreateDataBase函数:
function OnEnd()
begin
if !MAINTENANCE then
CreateDataBase(psvSQLsvr,psvSQLusr,psvSQLpwd); // 创建和 优化数据库
endif;
end;
1.1.2 SQL Script
在Installation Designer中的SQL Scripts插入创建数据库的脚本即可,值得注意的是脚本中的文本替换,如果脚本中有安装前未知信息(在安装过程中输入的),如数据库的安装路径,使用软件的公司名称等等,需要使用文本替换,方法如下:
1. 创建数据库的脚本片段
CREATE DATABASE [test] ON (NAME = N'test_Data', FILENAME = N'%INSTALL_DIR%DATA"test_Data.MDF' , SIZE = 4, FILEGROWTH = 0) LOG ON (NAME = N'test_Log', FILENAME = N'%INSTALL_DIR%DATA"test_Log.LDF' , SIZE = 4, FILEGROWTH = 10%)
GO
use test
GO
INSERT INTO [dbo].[use_company] ( [company _name])
VALUES ('%MY_COMPANY_NAME%')
2. 在Installation Designer中的Property Manager添加一个MY_COMPANYNAME的属性,如下图所示
3. 在OnFirstUIBefore事件中添加代码:
Dlg_UserInfo:
szMsg = "请正确输入用户名称及公司名称,以便本系统显示的信息正确!";
nResult = SdRegisterUser(szTitle, szMsg, szUserInfo, szCompanyName);
MsiSetProperty(ISMSI_HANDLE,"MY_COMPANYNAME", szCompanyName);
if (nResult = BACK) goto Dlg_SdLicense;
4. 在Installation Designer中的SQL Scripts的Text Replacement页面添加两个替换参数,如下图所示:
安装过程中有些设置是InstallShield无法做到的,需要其他的编程软件实现,其他的编程软件可以编写成可执行的应用程序(EXE文件),通过LaunchApp和LaunchAppAndWait调用,也可以编写成动态运行库(DLL文件),InstallShield调用其中的函数,我使用的是Borland Delphi编程软件,我编写了一个将安装文件夹设置为FTP虚拟目录的动态运行库,在InstallShield中调用。
Delphi的源代码如下:
library FtpSetup;
uses
SysUtils, Classes, COMOBJ, Variants;
{$R *.res}
procedure FtpSet(siteName, appPath: PCHAR);stdcall;
var
FTPSite, FTPServer, FTPRoot, FTPVDir: Variant;
newSiteName, newAppPath:string;
begin
newSiteName:= siteName;
newAppPath:= appPath;
FTPSite := CreateOleObject('IISNamespace');
FTPSite := FTPSite.GetObject('IIsFtpService', 'localhost/MSFTPSVC');
FTPServer := FTPSite.GetObject('IIsFtpServer', '1');
FTPRoot := FTPServer.GetObject('IIsFtpVirtualDir', 'Root');
FTPVDir := FTPRoot.Create('IIsFtpVirtualDir', newSiteName);
FTPVDir.Path := newAppPath;
FTPVDir.AccessRead := true;
FTPVDir.AccessWrite := true;
FTPVDir.SetInfo;
FTPSite := Unassigned;
FTPServer := Unassigned;
FTPRoot := Unassigned;
FTPVDir := Unassigned;
end;
exports
FtpSet;
begin
end.
InstallScript脚本如下:
函数定义
prototype stdcall FtpSetup.FtpSet (POINTER,POINTER);
调用函数
DLL_FILE= SUPPORTDIR^"FtpSetup.dll";
nResult = UseDLL (DLL_FILE);
if (nResult = 0) then
svString1 = "drawingfile";
svString2 = TARGETDIR^"DrawingFilePath";
psvString1 = &svString1;
psvString2 = &svString2;
FtpSet(psvString1,psvString2);
UnUseDLL (DLL_FILE);
else
MessageBox("创建FTP服务失败,请手工设置。",SEVERE);
endif;
值得注意的是函数的编写及调用都采用stdcall方式。
1.3.1 Basic MSI Project工程项目
1. 创建自定义窗体,可参考标准窗体的各种设置,主要是窗体Behavior的设置,下面以一个例子说明创建过程;
2. 创建一个记录登录服务器的信息的自定义窗体,该窗体需要录入服务器的IP地址、用户名及口令,将录入信息记录到XML文件中;
3. 如下图所示,在User Interface的Dialogs的All Dialogs点击右键,选择New Dialgo菜单,进入自定义窗体向导;
4. 选择Blank Dialog创建一个空白窗体,重命名NewDialog1,如ServerLogin;
5. 如下图所示选择ServerLogin下的Chinse(Simplified)节,可设计此窗体;
6. 添加三个Text Area,分别将Text属性更改为服务器IP:、用户名:及口令:,添加三个Edit Field,分别录入Property Name(属性名称)为MY_SERVER_IP、MY_SERVER_USER及MY_SERVER_PWD, 将口令的录入框的Password属性的值更改为True,添加三个Push Button,分别是上一步、下一步及取消,如下图所示:
7. 如果想将此窗体插入CustomerInformation窗体与SetupType之间,可选择CustomerInformation的Behavior的Next按钮,如下图所示,将Event的NewDialog的Argument更改为ServerLogin窗体;
8. 选择ServerLogin的Behavior的Next按钮 ,新增一个NewDialog事件,将Argument更改为SetupType窗体,Condition更改为1,同理在上一步按钮中添加返回CustomerInformation窗体的事件,在取消按钮中添加Event为SpawinDialog,Argument为CancelSetup,Condiiton为1,如下图所示:
9. 可以在Behavior and Logic中的Custom Actions and Sequences中可以看出窗体的执行顺序,如下图所示:
10. 在XML File Changes节中添加记录信息,这里不再详述。
1.3.2 InstallScript MIS Project工程项目
1. 创建自定义窗体需要注意在InstallScript中如何调用自定义窗体,并将窗体的属性复制公共属性,下面以一个例子说明调用;
2. 创建一个记录登录服务器FTP的信息的自定义窗体,该窗体需要录入服务器的IP地址、FTP端口、FTP用户名及口令,将录入信息记录到XML文件中;
3. 在User Interface的Dialogs的All Dialogs点击右键,选择New Dialgo菜单,进入自定义窗体向导,选择Blank Dialog创建一个空白窗体,重命名NewDialog1,如SdComputerInfo,编辑窗体如下图所示:
4. 在Direct Editor的Dialog中查出该窗体的ID,我创建的这个自定义窗体的ID为12011,在Direct Editor的Control中查出各控件的ID;
5. 在Property Manager中新建四个属性LOCALHOST、LOGINUSER、LOGINPWD、FTPPORT;
6. 在InstallScript中新建一个名为computerinfo.rul的脚本文件,脚本如下:
prototype SdComputerInfoDialog(string);
#define RES_DIALOG_ID 12011 // ID of custom dialog
#define RES_PBUT_NEXT 1 // ID of Next button
#define RES_PBUT_CANCEL 1310 // ID of Cancel button
#define RES_PBUT_BACK 1308 // ID of Back button
#define RES_EDITIP 1302 // ID of edit box
#define RES_EDITUSER 1304
#define RES_EDITPWD 1306
#define RES_EDITPORT 1312
function SdComputerInfoDialog(szTitle)
STRING szDialogName, svName, svCompany;
NUMBER nResult, nCmdValue;
BOOL bDone;
HWND hwndDlg;
STRING szComputerIP, szFTPUser, szFTPPwd, szFTPPort;
number nSize;
begin
szDialogName = "SdComputerInfo";
nResult = EzDefineDialog (szDialogName, "", "", RES_DIALOG_ID);
if (nResult < 0) then
// Report an error; then terminate.
MessageBox ("Error in defining dialog", SEVERE);
abort;
endif;
bDone = FALSE;
// Loop until done.
repeat
// Display the dialog and return the next dialog event.
nCmdValue = WaitOnDialog (szDialogName);
// Respond to the event.
switch (nCmdValue)
case DLG_CLOSE:
// The user clicked the window's Close button.
Do (EXIT);
case DLG_ERR:
MessageBox ("Unable to display dialog. Setup canceled.", SEVERE);
abort;
case DLG_INIT:
hwndDlg = CmdGetHwndDlg( szDialogName );
SdGeneralInit( szDialogName, hwndDlg, 0, szSdProduct );
SdSetDlgTitle(szDialogName, hwndDlg, szTitle);
nSize=MAX_PATH;
MsiGetProperty( ISMSI_HANDLE, "LOCALHOST", szComputerIP, nSize );
nSize=MAX_PATH;
MsiGetProperty( ISMSI_HANDLE, "LOGINUSER", szFTPUser, nSize );
nSize=MAX_PATH;
MsiGetProperty( ISMSI_HANDLE, "LOGINPWD", szFTPPwd, nSize );
nSize=MAX_PATH;
MsiGetProperty( ISMSI_HANDLE, "FTPPORT", szFTPPort, nSize );
CtrlSetText(szDialogName, RES_EDITIP, szComputerIP);
CtrlSetText(szDialogName, RES_EDITUSER, szFTPUser);
CtrlSetText(szDialogName, RES_EDITPWD, szFTPPwd);
CtrlSetText(szDialogName, RES_EDITPORT, szFTPPort);
case RES_PBUT_CANCEL:
Do (EXIT);
case RES_PBUT_BACK:
bDone = TRUE;
case RES_PBUT_NEXT:
CtrlGetText (szDialogName, RES_EDITIP, szComputerIP);
CtrlGetText (szDialogName, RES_EDITUSER, szFTPUser);
CtrlGetText (szDialogName, RES_EDITPWD, szFTPPwd);
CtrlGetText (szDialogName, RES_EDITPORT, szFTPPort);
MsiSetProperty(ISMSI_HANDLE,"LOCALHOST", szComputerIP);
MsiSetProperty(ISMSI_HANDLE,"LOGINUSER", szFTPUser);
MsiSetProperty(ISMSI_HANDLE,"LOGINPWD", szFTPPwd);
MsiSetProperty(ISMSI_HANDLE,"FTPPORT", szFTPPort);
bDone = TRUE;
endswitch;
until bDone;
// Close the dialog.
EndDialog (szDialogName);
// Remove the dialog from memory.
ReleaseDialog (szDialogName);
// If dialog is closed with Next button, display name and company.
if nCmdValue = RES_PBUT_NEXT then
return NEXT;
else
return BACK;
endif;
end;
7. 在OnFirstUIBefore事件中添加
Dlg_ComputerInfo:
szTitle = "设置服务器信息";
nResult = SdComputerInfoDialog(szTitle);
if (nResult = BACK) goto Dlg_UserInfo;
8. 在XML File Changes节中添加记录信息,这里不再详述。