作为和NSIS并立的、两个最流行的免费Windows应用程序安装包制作工具之一,Inno在学习难度上相对要低一些,非常适合对一些简单的桌面程序打包。但对于较复杂的安装过程,或者Web应用程序来说,我个人觉得不是Inno的强项。当然,既然Inno内嵌了Pascal语言用以扩展功能,理论上不是不可以应付复杂的安装过程,但实现起来要复杂一些。

比如对于在安装过程中连接数据库并执行SQL脚本这样的需求,使用InstallShield应该会简单地多,而Inno却不支持直接操作数据库,并且相关的资料说明少之又少,还不如NSIS丰富,以至于我踏破铁鞋无觅处,最终却在NSIS的资料中找到了思路。

主要的思路是,在安装过程中,调用数据库客户端连接数据库并执行SQL脚本,然后将执行结果或错误信息输出到文件中,最后通过分析这个文件来判断命令执行的结果。但是,既然是调用特定的客户端,那么对不同数据库的操作自然就有所区别,具体情况如下所述。

首先在打包脚本的[Files]段将必需的文件包含进来:

[Files]
Source: "D:/Development/MyDemoApp/code/*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs

;osql.exe在SQL Server2000安装目录中
Source: "D:/Development/MyDemoApp/osql.exe"; Flags: dontcopy
Source: "D:/Development/MyDemoApp/MySQL.exe"; Flags: dontcopy
Source: "D:/Development/MyDemoApp/script_mssql.sql"; Flags: dontcopy
Source: "D:/Development/MyDemoApp/script_mysql.sql"; Flags: dontcopy
Source: "D:/Development/MyDemoApp/script_oracle.sql"; Flags: dontcopy

在SQL Server中执行脚本的代码片断:

function ExecScriptInMSSQL(DBHost, DBLogin, DBPass, DBName: String): Boolean;
var
 ConnectExe: String;
 ConnectParam: String;
begin
 {解压临时文件}
 ExtractTemporaryFile('osql.exe');
 ExtractTemporaryFile('script_mssql.sql');
 {构造数据库连接字符串}
 ConnectExe := ExpandConstant('{tmp}') + '/osql.exe';
 ConnectParam := ' -S ' + DBHost
  + ' -U ' + DBLogin
  + ' -P ' + DBPass
  + ' -d ' + DBName
  + ' -i script_mssql.sql -o '
  + ExpandConstant('{tmp}') + '/dbstatus.txt';
 {建立数据库连接并执行脚本}
 if Exec(ConnectExe, ConnectParam, '', SW_HIDE, ewWaitUntilTerminated, ResultCode) then begin
  Result := ResultCode = 0;
  LoadStringFromFile(ExpandConstant('{tmp}') + '/dbstatus.txt', StatusString);
  if StatusString <> '' then begin
   MsgBox(StatusString, mbError, MB_OK);
   Result := False;
  end else begin
   Result := True;
  end;
 end else begin
  MsgBox('Database update failed:'#10#10 + SysErrorMessage(ResultCode), mbError, MB_OK);
  Result := False;
 end;
end;

在MySQL中执行脚本的代码片断:

function ExecScriptInMYSQL(DBHost, DBLogin, DBPass, DBName: String): Boolean;
var
 ConnectExe: String;
 ConnectParam: String;
begin
 {解压临时文件}
 ExtractTemporaryFile('mysql.exe');
 ExtractTemporaryFile('script_mysql.sql');
 {构造数据库连接字符串}
 ConnectExe := ExpandConstant('cmd');
 ConnectParam := ' /c "' + ExpandConstant('{tmp}') + '/mysql.exe'
  + ' -h' + DBHost
  + ' -u' + DBLogin
  + ' -p' + DBPass
  + ' -D' + DBName
  + ' -e "source ' + ExpandConstant('{tmp}') + '/script_mysql.sql""> ' + ExpandConstant('{tmp}') + '/dbstatus.txt 2>&1';
 {建立数据库连接并执行脚本}
 if Exec(ConnectExe, ConnectParam, '', SW_HIDE, ewWaitUntilTerminated, ResultCode) then begin
  Result := ResultCode = 0;
  LoadStringFromFile(ExpandConstant('{tmp}') + '/dbstatus.txt', StatusString);
  if StatusString <> '' then begin
   MsgBox(StatusString, mbError, MB_OK);
   Result := False;
  end else begin
   Result := True;
  end;
 end else begin
  MsgBox('Database update failed:'#10#10 + SysErrorMessage(ResultCode), mbError, MB_OK);
  Result := False;
 end;
end;

由于mysql.exe没有输出结果到文件的参数,故需要使用cmd.exe来运行mysql.exe以便将其输出重定向到文件dbstatus.txt中。此外,在命令的最后加上参数2>&1,将标准错误输出设备也重定向到文件上,否则命令执行的错误信息不会输出到文件中。

在Oracle中执行脚本的代码片断:
function ExecScriptInORACLE(ClientPath, DBInstance, DBLogin, DBPass: String): Boolean;
begin
 {解压临时文件}
 ExtractTemporaryFile('script_oracle.sql');
 {连接数据库并执行脚本}
 if Exec(ExpandConstant('cmd'), ' /c "' + ClientPath + ' -L -S ' + DBLogin
  + '/' + DBPass
  + '@' + DBInstance
  + ' @' + ExpandConstant('{tmp}') + '/script_oracle.sql> ' + ExpandConstant('{tmp}') + '/dbstatus.txt 2>&1',
  '',
  SW_HIDE, ewWaitUntilTerminated, ResultCode)
 then begin
  Result := ResultCode = 0;
  LoadStringFromFile(ExpandConstant('{tmp}') + '/dbstatus.txt', StatusString);
  if Pos('holytail', StatusString) <> 0 then begin
   {若输出信息中有“holytail”的子串,则表示脚本成功执行}
   {若执行有误,提示用户打开日志文件}
   if Pos('ORA-', StatusString) <> 0 then begin
    {提示用户脚本执行出错}
    if MsgBox('数据库更新出错,是否打开日志文件?', mbConfirmation, MB_YESNO) = IDYES then begin
     {打开日志}
     if not ShellExec('', ExpandConstant('{tmp}') + '/dbstatus.txt', '', '', SW_SHOW, ewNoWait, ErrorCode) then begin
      MsgBox('日志文件打开错误!', mbError, MB_OK);
     end;
    end;
    Result := False;
   {若执行无误,返回True}
   end else begin
    Result := True;
   end;
  end else if StatusString <> '' then begin
   MsgBox(StatusString, mbError, MB_OK);
   Result := False;
  end else begin
   Result := True;
  end;
 end else begin
  MsgBox('Database update failed:'#10#10 + SysErrorMessage(ResultCode), mbError, MB_OK);
  Result := False;
 end;
end;
Oracle的客户端太大,不能集成到安装包中,应使用一个TInputFileWizardPage由用户选择sqlplus.exe的安装位置。同时,由于sqlplus.exe也没有输出结果到文件的参数,也须使用cmd.exe来运行它并重定向输出到文件。此外,由于sqlplus.exe执行脚本时无论成功还是失败,都会输出信息,故无法像使用sqlcmd.exe和mysql.exe那样简单地判断脚本是否执行成功,需要在脚本的最后通过select语句输出一个特殊的字符串到文件中,然后通过判断dbstatus.txt中是否存在该字符串来判断脚本的执行情况;且由于sqlplus.exe执行完脚本后不会自动退出,还要在脚本最后加上exit语句;故script_oracle.sql的最后必须是如下内容:

SELECT 'holytail' FROM dual;
exit;

Inno Setup执行SQL脚本的方法的更多相关文章

  1. InstallShield在MySQL和Oracle中执行SQL脚本的方法InstallShield在MySQL和Oracle中执行SQL脚本的方法

    简述 InstallShield已经内建了对MySQL和Oracle的支持.但是这个功能是通过ODBC实现的,它对SQL脚本的格式要求非常严格,因此已经通过官方客户端测试的脚本在IS中执行时往往就会报 ...

  2. inno setup 执行SQL

    参考之:1.可将导入数据的功能写入一个小程序,再外部调用(楼上已经说了):2.可用程序代码:[Setup] AppName=科發醫院管理系統 AppVerName=科發醫院管理系統4.0 AppPub ...

  3. 使用命令执行 sql 脚本文件

    使用命令执行 sql 脚本文件 方法: 在 Windows 下使用 cmd 命令执行(或 Unix 或 Linux 控制台下)[Mysql的bin目录]\mysql –u用户名 –p密码 –D数据库名 ...

  4. Inno Setup的常用脚本

    Inno Setup的常用脚本 分类: VC++神奇理论 2012-12-06 10:07 3234人阅读 评论(2) 收藏 举报 安装不同的目录: [Files] Source: "我的程 ...

  5. inno setup教程解释脚本

    inno setup教程解释脚本 2007-04-08 21:31:36|  分类: 科技-> Inno Setu |  标签:inno   |举报 |字号 订阅     下载LOFTER客户端 ...

  6. mysql执行sql脚本

    最近用mysql执行sql脚本,遇到一些问题,顺便记录一下笔记. 首先,先开启mysql服务,创建一个空数据库(脚本里没有创建数据库) 执行脚本有两个方法 1.未连接数据库:在Windows下使用cm ...

  7. .net(C#)在Access数据库中执行sql脚本

    自己写的一个工具类,主要是业务场景的需要. 主要有两个功能: ①执行包含sql语句的字符串 ②执行包含sql语句的文件 调用方式 /// <summary> /// 执行sql语句 /// ...

  8. mysql执行sql脚本文件

    mysql执行sql脚本文件 方法一:使用cmd命令执行(windows下,unix或Linux在的其控制台下) [MySQL的bin目录]\mysql –u用户名 –p密码 –D数据库<[sq ...

  9. MySQL 执行SQL脚本 报ERROR 1231 (42000)的解决办法【转】

    今天在source mysqldump 备份文件时,发现导入的过程中报如下的错误: ERROR 1231 (42000): Variable 'time_zone' can't be set to t ...

随机推荐

  1. Linux 下 apache 日志分析与状态查看[转]

    假设apache日志格式为: 118.78.199.98 – - [09/Jan/2010:00:59:59 +0800] “GET /Public/Css/index.css HTTP/1.1″ 3 ...

  2. php 开启COM组件

    1.先到PHP.INI中打开COM选项,com.allow_dcom = true 2.我这里的环境是PHP5.4.7,PHP 5.4.5后,com/dotnet 模块已经成了单独的扩展,所以需要在P ...

  3. web前端-面试经验总结

    这几次面试主要是冲着百度去的 面试1的主要问题: 笔试: 1.解释css盒子模型 2.常用选择器,以及优先级 3.B如何继承A 4.写一个闭包实例,有什么优点缺点 5.html5的心特性有哪些 6. ...

  4. 006 [翻译] Haneke(一个Swfit iOS缓存类)

    Github项目地址:https://github.com/Haneke/HanekeSwift Haneke是一个用swift写成的轻量级iOS类,以简单好用著称(design-decisions- ...

  5. 【Android】【录音】Android录音--AudioRecord、MediaRecorder

    [Android][录音]Android录音--AudioRecord.MediaRecorder Android提供了两个API用于实现录音功能:android.media.AudioRecord. ...

  6. hadoop(二):hdfs HA原理及安装

    早期的hadoop版本,NN是HDFS集群的单点故障点,每一个集群只有一个NN,如果这个机器或进程不可用,整个集群就无法使用.为了解决这个问题,出现了一堆针对HDFS HA的解决方案(如:Linux ...

  7. .NET(C#)生成条形码

    using System; using System.Data; using System.Configuration; using System.Web; using System.Web.Secu ...

  8. UIPickerView自定义背景

    #import <UIKit/UIKit.h> @interface MyPicker : UIPickerView { } @end -------------------------- ...

  9. android学习笔记七——控件(DatePicker、TimePicker、ProgressBar)

    DatePicker.TimePicker ==> DatePicker,用于选择日期 TimePicker,用于选择时间 两者均派生与FrameLayout,两者在FrameLayout的基础 ...

  10. oracle学习笔记(四)oracle内存优化

    emca -config dbcontrol db -repos recreate 解决'oracle Environment variable ORACLE_SID not defined. Ple ...