一、引言
软件部署是一个获得应用程序并将它安装到另一台机器上的过程,一般通过安装程序完成。一个项目完成之后,在软件交互发布前,安装程序的制作是一个必要的过程。由于Microsoft SQL Server 2000的低成本、可管理性和易维护性,现在很多数据库应用程序都是基于SQL Server 2000数据库平台运行的,但对于最终用户而言,部署SQL Server 2000数据库环境却是一件很复杂的事情,因此设计人员在安装打包时,应充分考虑不同用户使用相同的SQL Server 2000安装配置等情况,减少软件发布成本,实现软件部署的“傻瓜化”。
二、设计
在默认情况下,当使用SQL Server 2000的安装程序屏幕进行值守安装时,所指定的所有选项都被记录进安装程序初始化文件(setup.iss)中,该文件存放在系统根目录(%windir%)下,用于为以后提供安装设置。而通过调用程序执行该文件,即可实现SQL Server 2000的无值守安装,整个安装过程基本不需要用户的干预,在 MicrosoftSQL Server™ 2000 安装光盘中提供了示例的批处理文件(如Sqlins.bat)和安装程序初始化文件(如Sqlins.iss)。在安装完毕数据库后,就可通过SQL Server的osql.exe 实用工具执行Transact-SQL 语句、系统存储过程和脚本文件实现数据库实例的初始化。以下是具体的实施步骤:
第1步:通过注册表判断SQL Server是否已经安装,如果已经安装,则启动服务器(第4步)。
第2步:通过调用批处理文件等方式执行SQL Server的安装命令:setupsql.exe -s -m -SMS -f1 " setup.iss"。
第3步:建一个循环,通过进程来判断安装程序的结束,一旦结束则启动服务器(第4步)。
第4步:通过进程或服务判断SQL Server服务是否已经启动,如果已经启动则配置数据库(第5步),否则调用sqlservr.exe程序或Windows服务启动服务。
第5步:通过批处理文件等方式调用SQL Server的osql.exe命令执行外部SQL脚本文件(该文件通过调用“CREATE DATABASE、CREATE DEVICE、RESTORE DATABASE”等Transact-SQL语句和“sp_dropdevice、sp_addumpdevice、sp_attach_db”等系统存储过程,实现数据库初始数据文件与日志文件的导入),将初始的数据库附加到服务器上。
第6步:建一个循环,通过进程来判断配置程序的结束,一旦结束则启动服务器,安装应用程序。
三、编码
设计人员需要把执行SQL Server安装命令的批处理文件setup.bat、经过值守安装后并记录在系统根目录(%windir%)的安装程序初始化文件setup.iss、数据库初始化文件MyInstance.mdf(假设数据库实例名为MyInstance)、应用系统安装文件Setup.exe一起发布至Microsoft SQL Server 2000安装根目录下。
其中setup.bat文件的内容为:
@echo on
start /wait x86\setup\setupsql.exe -s -m -SMS -f1 "setup.iss"
@echo off
1.功能实现的核心代码
//-从注册表中检索是否安装 Microsoft SQL Server 2000 数据库-///
procedure TForm1.FormCreate(Sender: TObject);
var
Registry: TRegistry;
begin
Registry := TRegistry.Create;
Registry.RootKey := HKEY_LOCAL_MACHINE;
if Registry.OpenKey('\SOFTWARE\Microsoft\MSSQLServer\Setup', False) then
//-从注册表中检索是否安装 MyInstance 数据库实例-
if not FileExists(Registry.ReadString('SQLDataRoot') + '\Data\MyInstance_Data.mdf') then
BitBtn2Click(nil);
else//没有安装 Microsoft SQL Server 2000 数据库
BitBtn1Click(nil);
Registry.Free;
end;
///-安装 SQL Server 2000数据库-///
procedure TForm1.BitBtn1Click(Sender: TObject);
var
sBatFileName: string;
iWinExecAndWait32: Integer;
begin
if Application.MessageBox('确认执行"安装 SQL Server 2000数据库"吗?', '提示', MB_YESNO + MB_ICONINFORMATION) = IDNO then
Exit;
//-生成调用osql.exe命令的批处理文件setup.bat'并等待其执行完毕-
sBatFileName := ExtractFileDir(ParamStr(0)) + '\setup.bat';
iWinExecAndWait32 := WinExecAndWait32(sBatFileName, SW_HIDE);
if iWinExecAndWait32 = 0 then
begin
//-启动MSSQLSERVER服务并等待-
RunService('MSSQLSERVER');
Sleep(6000);
Application.MessageBox('"安装 SQL Server 2000数据库"执行完毕!', '提示', MB_OK + MB_ICONINFORMATION);
///-配置 SQL Server 2000数据库-
BitBtn2Click(nil);
end;
end;
///-配置 SQL Server 2000数据库-///
procedure TForm1.BitBtn2Click(Sender: TObject);
var
Registry: TRegistry;
sCrlf, sMdfFileName, sSQL, sDataInstallLocation: string;
slSQL, slBAT: TStringList;
iWinExecAndWait32: Integer;
begin
if QueryServiceState('MSSQLSERVER') = 0 then
begin
if Application.MessageBox('确认执行"配置 SQL Server 2000数据库"吗?', '提示', MB_YESNO + MB_ICONINFORMATION) = IDNO then
Exit;
//-启动MSSQLSERVER服务并等待-
if QueryServiceState('MSSQLSERVER') = 0 then
begin
RunService('MSSQLSERVER');
Sleep(5000);
end;
///-配置MyInstance数据库-///
sCrlf := Chr(13) + Chr(10);
sMdfFileName := ExtractFilePath(ParamStr(0)) + '\MyInstance.mdf';
Registry := TRegistry.Create;
Registry.RootKey := HKEY_LOCAL_MACHINE;
Registry.OpenKey('\SOFTWARE\Microsoft\MSSQLServer\Setup', False);
sDataInstallLocation := Registry.ReadString('SQLDataRoot') + '\Data\MyInstance_Data.mdf';
Registry.Free;
//-生成SQL文件,并保存临时文件-
sSQL := 'USE master' + sCrlf +
'if not exists (select * from sysdatabases where name=''MyInstance'')' + sCrlf +
'CREATE DATABASE MyInstance' + sCrlf +
'if exists (select * from sysdevices where name=''MyInstance'')' + sCrlf +
'EXEC sp_dropdevice ''MyInstance''' + sCrlf +
'EXEC sp_addumpdevice ''disk'', ''MyInstance'', ''' + sDataInstallLocation + '''' + sCrlf +
'RESTORE DATABASE MyInstance' + sCrlf +
'FROM DISK=''' + sMdfFileName + '''' + sCrlf +
'WITH REPLACE';
slSQL := TStringList.Create;
slSQL.Add(sSQL);
slSQL.SaveToFile('c:\temp1.sql');
slSQL.Clear;
//-生成并执行调用osql.exe命令的批处理文件-
slBAT:= TStringList.Create;
slBAT.Add('@echo off');
slBAT.Add('osql.exe -E -S -i "c:\temp1.sql"' + ' -o "c:\result1.txt"');
slBAT.Add('exit');
slBAT.SaveToFile('c:\temp1.bat');
slBAT.Clear;
iWinExecAndWait32 := WinExecAndWait32('c:\temp1.bat', SW_HIDE);
if iWinExecAndWait32 = 0 then
begin
Application.MessageBox('"配置 SQL Server 2000数据库"执行完毕!', '提示', MB_OK + MB_ICONINFORMATION);
///-安装 xxxxxxxxxx信息管理系统-
BitBtn3Click(nil);
end;
end;
end;
///-安装 xxxxxxxxxx信息管理系统-///
procedure TForm1.BitBtn3Click(Sender: TObject);
var
sExeFileName: string;
iWinExecAndWait32: Integer;
begin
if Application.MessageBox('确认执行"安装 xxxxxxxxxx信息管理系统"吗?', '提示', MB_YESNO + MB_ICONINFORMATION) = IDNO then
Exit;
//-执行系统安装程序-
sExeFileName := ExtractFilePath(ParamStr(0)) + '\Setup.exe';
iWinExecAndWait32 := WinExecAndWait32(sExeFileName, SW_SHOW);
if iWinExecAndWait32 = 0 then
begin
Application.MessageBox('"安装 xxxxxxxxxx信息管理系统"执行完毕!', '提示', MB_OK + MB_ICONINFORMATION);
end;
end;
2.调用的几个关键函数和过程的核心代码
///-执行一个外部程序并等待其执行结束-///
function WinExecAndWait32(FileName: string; Visibility: Integer): Cardinal;
var
zAppName: array[0..512] of char;
zCurDir: array[0..255] of char;
WorkDir: string;
StartupInfo: TStartupInfo;
ProcessInfo: TProcessInformation;
Msg: TagMsg;
ExitCode: cardinal;
begin
StrPCopy(zAppName, FileName);
GetDir(0, WorkDir);
StrPCopy(zCurDir, WorkDir);
FillChar(StartupInfo, Sizeof(StartupInfo), #0);
StartupInfo.cb := Sizeof(StartupInfo);
StartupInfo.dwFlags := STARTF_USESHOWWINDOW;
StartupInfo.wShowWindow := Visibility;
if CreateProcess(nil, zAppName, nil, nil, false, NORMAL_PRIORITY_CLASS, nil, nil, StartupInfo, ProcessInfo) then
begin
repeat
while PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do
begin
if (Msg.message = WM_QUIT) then
Result := Msg.wParam;
TranslateMessage(msg);
Dispatchmessage(msg);
Sleep(100);
end;
GetExitCodeProcess(ProcessInfo.hProcess, ExitCode);
until (ExitCode <> STILL_ACTIVE);
CloseHandle(ProcessInfo.hThread);
CloseHandle(ProcessInfo.hProcess);
Result := 0;
end
else
Result := GetLastError;
end;
///-查询某一Windows服务-///
function QueryServiceState(ServiceName: String): Cardinal;
var
svcmgr, svc: Integer;
temp: _SERVICE_STATUS;
begin
svcmgr := OpenSCManager(nil, nil, SC_MANAGER_ALL_ACCESS);
if svcmgr = 0 then
begin
Result := 0;
Exit;
end;
svc := OpenService(svcmgr, PChar(ServiceName), SERVICE_ALL_ACCESS);
if svc = 0 then
begin
CloseServiceHandle(svcmgr);
Result := 0;
Exit;
end;
QueryServiceStatus(svc, temp);
if temp.dwCurrentState = SERVICE_STOPPED then
Result := 1
else
Result := 0;
CloseServiceHandle(svcmgr);
CloseServiceHandle(svc);
if (Result <> 1) and (Result <> 0) then
Result := 1;
end;
///-启动某一Windows服务-///
Procedure RunService(ServiceName: String);
var
svcmgr, svc: Integer;
temp: _SERVICE_STATUS;
s: Pchar;
begin
svcmgr := OpenSCManager(nil, nil, SC_MANAGER_ALL_ACCESS);
if svcmgr = 0 then
Exit;
svc := OpenService(svcmgr, PChar(ServiceName), SERVICE_ALL_ACCESS);
if svc = 0 then
begin
CloseServiceHandle(svcmgr);
Exit;
end;
QueryServiceStatus(svc, temp);
if temp.dwCurrentState = SERVICE_STOPPED then
StartService(svc, 0, s);
CloseServiceHandle(svcmgr);
CloseServiceHandle(svc);
end;
四、结语
本文介绍了使用Delphi实现一个完整的Microsoft SQL Server 2000数据库应用程序的部署过程,包括数据库全部自动安装运行,基本不需要用户的干预。以上所有程序在Delphi7.0、SQL Server 2000环境下调试通过。
|