使用Innosetup对软件进行打包
2013-05-24 16:00
204 查看
一:背景
由于我们以SAAS服务方式,为客户分发软件包,每个软件包中都有客户自定义的一些特定信息,如:公司名称、LOGO、快捷方式名称、安装路径指定等,这些信息是在后台操作界面中指定,如下:
目前我们采用的是在客户下载客户端时,根据后台的设置,使用WINRAR生成一个自解压包,其中将客户定制的这些信息均会被自动打包到此自解压包中;在一般情况下,我们的这个方案还是可行的,并且也使用此方案运营了几年。
但我们逐步发现此方案有如下缺点:
1.
使用WINRAR自解压方式,没有请求管理员权限运行,导致在WIN7等系统上安装不成功的问题
2.
快捷方式创建只能在当前用户桌面
3.
由于使用的是自解压运行方式,并且没有添加数字签名【动态生成的,为了安全,没有在生成后再加签名,并且有些环境下也签不了名】,会引起如金山毒霸的报毒
4.
在WIN7上运行安装后,会提示此软件未正确安装的问题
以上问题,给客户的正常使用带来了非常多的困扰,特别是第3点,让客户误以为我们的安装包有病毒【我们为了客户安装的便利性,一次性做成了自解压运行行为】,在此背景下,我们进行了探索,使用了另外的方式来进行安装包的生成与安装。
二:方案与工具
1.
涉及工具:
Inno setup为打包工具
2.
方案
此方案采用InnoSetup来进行客户端文件的打包,完成后进行签名,放到服务器上后,即不再对此文件包进行任何变更,以免破坏其数字签名信息;
在SAAS服务器上针对不同客户进行信息定制时,以不同的文件名来反映出不同的客户信息。如:ClientSetup_100000001.exe
此文件名用于标识不同的客户,如:1000000为客户标识,后面的01作为对1000000的校验串,如果客户无意修改了文件名,则通过提示信息告知。
启动安装时,ClientSetup_100000001.exe先对文件名进行校验,如果正确,则通过此ID获取其定制的信息【连接的服务器地址已经包含在安装包中】,获取到安装地址、快捷方式名称、LOGO等信息后,控制后续的安装流程进行安装。
这里会涉及到安装过程中的网络访问问题,特别是代理的处理,所以需要处理好在安装时的网络访问,目前我们的处理是:
1)
如果直接能访问网络,下载信息,则下载,如果失败,则进入第2步
2)
如果检测到IE设置了代理,则通过此代理进行访问,如果需要授权则进入第3步
3)
则要设置代理信息等
见下图:
三:具体实现
由于我们对Pascal 脚本不熟悉,所以我更多的是使用C++写成DLL,通过在innosetup的相关事件及函数中调用的方式来实现。
1.
检测文件名是否有应该放在安装包刚启动时进行检测,在事件InitializeSetup中检测,如果文件名校验不正确,则直接退出安装:
2.
在文件检验检测通过,则开始复制文件,当复制了辅助安装的DLL后,即可在其AfterInstall中进行网络访问,获取服务器中定制的信息:
3.
对于DLL的引用,需要进行声明后方可调用:
4.
在安装的过程中,要对路径选择、快捷方式创建等窗口进行隐藏,如下:
5.
在获取到真正的安装路径后,需要将文件复制的路径定位到真正路径下[快捷方式的创建也是同样]:
这样才能保证安装的信息与服务器上配置的一致。
6.
对于调用的DLL中的函数,则具体执行文件名检测、服务器数据下载等事务,在此就不多述了
7.
打包的完整代码脚本如下【部分与公司业务相关代码未包含】
由于我们以SAAS服务方式,为客户分发软件包,每个软件包中都有客户自定义的一些特定信息,如:公司名称、LOGO、快捷方式名称、安装路径指定等,这些信息是在后台操作界面中指定,如下:
目前我们采用的是在客户下载客户端时,根据后台的设置,使用WINRAR生成一个自解压包,其中将客户定制的这些信息均会被自动打包到此自解压包中;在一般情况下,我们的这个方案还是可行的,并且也使用此方案运营了几年。
但我们逐步发现此方案有如下缺点:
1.
使用WINRAR自解压方式,没有请求管理员权限运行,导致在WIN7等系统上安装不成功的问题
2.
快捷方式创建只能在当前用户桌面
3.
由于使用的是自解压运行方式,并且没有添加数字签名【动态生成的,为了安全,没有在生成后再加签名,并且有些环境下也签不了名】,会引起如金山毒霸的报毒
4.
在WIN7上运行安装后,会提示此软件未正确安装的问题
以上问题,给客户的正常使用带来了非常多的困扰,特别是第3点,让客户误以为我们的安装包有病毒【我们为了客户安装的便利性,一次性做成了自解压运行行为】,在此背景下,我们进行了探索,使用了另外的方式来进行安装包的生成与安装。
二:方案与工具
1.
涉及工具:
Inno setup为打包工具
2.
方案
此方案采用InnoSetup来进行客户端文件的打包,完成后进行签名,放到服务器上后,即不再对此文件包进行任何变更,以免破坏其数字签名信息;
在SAAS服务器上针对不同客户进行信息定制时,以不同的文件名来反映出不同的客户信息。如:ClientSetup_100000001.exe
此文件名用于标识不同的客户,如:1000000为客户标识,后面的01作为对1000000的校验串,如果客户无意修改了文件名,则通过提示信息告知。
启动安装时,ClientSetup_100000001.exe先对文件名进行校验,如果正确,则通过此ID获取其定制的信息【连接的服务器地址已经包含在安装包中】,获取到安装地址、快捷方式名称、LOGO等信息后,控制后续的安装流程进行安装。
这里会涉及到安装过程中的网络访问问题,特别是代理的处理,所以需要处理好在安装时的网络访问,目前我们的处理是:
1)
如果直接能访问网络,下载信息,则下载,如果失败,则进入第2步
2)
如果检测到IE设置了代理,则通过此代理进行访问,如果需要授权则进入第3步
3)
则要设置代理信息等
见下图:
三:具体实现
由于我们对Pascal 脚本不熟悉,所以我更多的是使用C++写成DLL,通过在innosetup的相关事件及函数中调用的方式来实现。
1.
检测文件名是否有应该放在安装包刚启动时进行检测,在事件InitializeSetup中检测,如果文件名校验不正确,则直接退出安装:
function InitializeSetup(): Boolean; begin if StringCheck(ExpandConstant('{srcexe}')) = 0 then begin MsgBox('文件名无效,请重新下载安装包!', mbConfirmation, MB_OK); Result := False; end else begin Result := True; end MyProgChecked := False; ShotCutName := ''; end;
2.
在文件检验检测通过,则开始复制文件,当复制了辅助安装的DLL后,即可在其AfterInstall中进行网络访问,获取服务器中定制的信息:
Source: "..\dependency\RTSClient.ini"; DestDir: "{app}"; Flags: ignoreversion Source: "..\dependency\ClientLogo.ico"; DestDir: "{app}"; Flags: ignoreversion; Source: "..\ch\ProxyWrapper.dll"; DestDir: "{app}"; Flags: ignoreversion; Source: "..\ch\SetupWrapper.dll"; DestDir: "{app}"; Flags: ignoreversion; AfterInstall:DownLoadCustomizeData()
3.
对于DLL的引用,需要进行声明后方可调用:
function StringCheck(lpPath: AnsiString): Integer; external 'StringCheck@files:SetupWrapper.dll stdcall setuponly'; function DownCustomizeData(lpSetupPath:AnsiString): Integer; external 'DownCustomizeData@files:SetupWrapper.dll stdcall setuponly'; function GetSpecificPath(lpchar:PChar; nFlag:Integer): Integer; external 'GetSpecificPath@files:SetupWrapper.dll stdcall setuponly'; function CopyCustomizeData(lpPath: AnsiString): Integer; external 'CopyCustomizeData@files:SetupWrapper.dll stdcall setuponly';
4.
在安装的过程中,要对路径选择、快捷方式创建等窗口进行隐藏,如下:
function ShouldSkipPage(PageID: Integer): Boolean; begin Result := False; if PageID <> wpFinished then begin Result := True; end end; procedure CurPageChanged(CurPageID: Integer); begin if CurPageID <> wpFinished then begin PostMessage(WizardForm.NextButton.Handle,WM_LBUTTONDOWN,0,0); PostMessage(WizardForm.NextButton.Handle,WM_LBUTTONUP,0,0); end end;
5.
在获取到真正的安装路径后,需要将文件复制的路径定位到真正路径下[快捷方式的创建也是同样]:
Source: "..\ch\RTSKeyGenDll.Dll"; DestDir: "{code:GetRealSetupPath}"; Flags: ignoreversion Name: "{group}\{code:GetShotCutName}"; Filename: "{code:GetRealSetupPath}\{#MyAppExeName}"; WorkingDir:"{code:GetRealSetupPath}"; IconFilename: "{code:GetRealSetupPath}\ClientLogo.ico"
这样才能保证安装的信息与服务器上配置的一致。
6.
对于调用的DLL中的函数,则具体执行文件名检测、服务器数据下载等事务,在此就不多述了
7.
打包的完整代码脚本如下【部分与公司业务相关代码未包含】
; 脚本由 Inno Setup 脚本向导 生成! ; 有关创建 Inno Setup 脚本文件的详细资料请查阅帮助文档! #define MyAppName "远程在线系统客户端" #define MyAppVersion "3.1" #define MyAppPublisher "测试公司名称" #define MyAppURL "http://www.test.com/" #define MyAppExeName "Test.exe" [Setup] ; 注: AppId的值为单独标识该应用程序。 ; 不要为其他安装程序使用相同的AppId值。 ; (生成新的GUID,点击 工具|在IDE中生成GUID。) AppId={{2C5393DC-8D35-4D13-B7AB-B389AE5E4111} ;AppName={code:testtest|a} AppName={#MyAppName} AppVersion={#MyAppVersion} AppVerName={#MyAppName} {#MyAppVersion} AppPublisher={#MyAppPublisher} AppPublisherURL={#MyAppURL} AppSupportURL={#MyAppURL} AppUpdatesURL={#MyAppURL} VersionInfoDescription={#MyAppName} VersionInfoProductTextVersion={#MyAppVersion} VersionInfoVersion={#MyAppVersion} DefaultDirName={pf}\Test DefaultGroupName={#MyAppName} OutputDir=.\..\package OutputBaseFilename=Test Compression=lzma SolidCompression=yes [Languages] Name: "chinesesimp"; MessagesFile: "compiler:Default.isl" [Files] Source: "..\dependency\RTSClient.ini"; DestDir: "{app}"; Flags: ignoreversion Source: "..\dependency\ClientLogo.ico"; DestDir: "{app}"; Flags: ignoreversion; Source: "..\ch\ProxyWrapper.dll"; DestDir: "{app}"; Flags: ignoreversion; Source: "..\ch\SetupWrapper.dll"; DestDir: "{app}"; Flags: ignoreversion; AfterInstall:DownLoadCustomizeData() ;完成LOGO后,需要将Rtsclient.ini与logo复制过去 Source: "..\ch\AESEncryption.dll"; DestDir: "{code:GetRealSetupPath}"; Flags: ignoreversion; AfterInstall:CopyLogo() Source: "..\ch\ChatLib.dll"; DestDir: "{code:GetRealSetupPath}"; Flags: ignoreversion Source: "..\ch\FileTransferLib.dll"; DestDir: "{code:GetRealSetupPath}"; Flags: ignoreversion [Icons] Name: "{group}\{code:GetShotCutName}"; Filename: "{code:GetRealSetupPath}\{#MyAppExeName}"; WorkingDir:"{code:GetRealSetupPath}"; IconFilename: "{code:GetRealSetupPath}\ClientLogo.ico" Name: "{group}\{cm:UninstallProgram,{code:GetShotCutName}}"; Filename: "{uninstallexe}"; WorkingDir:"{code:GetRealSetupPath}"; IconFilename: "{code:GetRealSetupPath}\ClientLogo.ico" Name: "{commondesktop}\{code:GetShotCutName}"; Filename: "{code:GetRealSetupPath}\{#MyAppExeName}"; WorkingDir:"{code:GetRealSetupPath}"; IconFilename: "{code:GetRealSetupPath}\ClientLogo.ico" [Run] Filename: "{code:GetRealSetupPath}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{code:GetShotCutName}}"; Flags: nowait postinstall skipifsilent [Code] const WM_LBUTTONDOWN = 513; WM_LBUTTONUP = 514; var MyProgChecked: Boolean; RealSetupPath: String; ShotCutName : String; function StringCheck(lpPath: AnsiString): Integer; external 'StringCheck@files:SetupWrapper.dll stdcall setuponly'; function DownCustomizeData(lpSetupPath:AnsiString): Integer; external 'DownCustomizeData@files:SetupWrapper.dll stdcall setuponly'; function GetSpecificPath(lpchar:PChar; nFlag:Integer): Integer; external 'GetSpecificPath@files:SetupWrapper.dll stdcall setuponly'; function CopyCustomizeData(lpPath: AnsiString): Integer; external 'CopyCustomizeData@files:SetupWrapper.dll stdcall setuponly'; function InitializeSetup(): Boolean; begin if StringCheck(ExpandConstant('{srcexe}')) = 0 then begin MsgBox('文件名无效,请重新下载安装包!', mbConfirmation, MB_OK); Result := False; end else begin Result := True; end MyProgChecked := False; ShotCutName := ''; end; procedure CancelButtonClick ( CurPageID : Integer; var Cancel, Confirm: Boolean); begin Cancel := True; Confirm := False; end; function GetShotCutName(Param: String) : String; var ShotName: String; ReturnLength: Integer; begin if Length(ShotCutName) = 0 then begin SetLength(ShotName, 270); ReturnLength := GetSpecificPath(ShotName, 2); ShotCutName := Copy(ShotName, 0, ReturnLength); //MsgBox(ShotCutName, mbConfirmation, MB_OK); end Result := ShotCutName; end; procedure CopyLogo(); begin CopyCustomizeData(''); end; procedure DownLoadCustomizeData(); var RealPath: String; PathLength: Integer; ReturnValue: Integer; begin //在此处去获取服务器配置等信息 ReturnValue := DownCustomizeData(ExpandConstant('{app}')); if ReturnValue < 0 then begin MsgBox(Format('连接服务器失败[错误码:%d],将按默认设置进行安装!', [ReturnValue]), mbConfirmation, MB_OK); //PostMessage(WizardForm.CancelButton.Handle,WM_LBUTTONDOWN,0,0); //PostMessage(WizardForm.CancelButton.Handle,WM_LBUTTONUP,0,0); end //else //begin //设置好真正的安装目录 //设置好是否换了LOGO SetLength(RealPath, 270); PathLength := GetSpecificPath(RealPath, 1); RealSetupPath := Copy(RealPath, 0, PathLength); //MsgBox(RealSetupPath, mbConfirmation, MB_OK); //end end; function GetRealSetupPath(Param: String) : String; begin Result := RealSetupPath; end; function ShouldSkipPage(PageID: Integer): Boolean; begin Result := False; if PageID <> wpFinished then begin Result := True; end end; procedure CurPageChanged(CurPageID: Integer); begin if CurPageID <> wpFinished then begin PostMessage(WizardForm.NextButton.Handle,WM_LBUTTONDOWN,0,0); PostMessage(WizardForm.NextButton.Handle,WM_LBUTTONUP,0,0); end end;
相关文章推荐
- 使用Advanced Installer 13.7进行AutoCAD2012(2014)插件打包 二(打包软件使用)
- 使用Advanced Installer 13.7进行AutoCAD2010插件打包 三(打包软件使用)
- Qt程序发行Linux版,软件打包知识(patchelf 工具修改依赖库,确认 qmake -v 是自己使用的Qt版本,否则用export PATH进行修改)good
- 使用GooSeeker软件进行爬虫
- 用FileMenu Tools玩转系统右键菜单 软件还有其他的内置命令,这里由于篇幅原因不能全部介绍了,比如注册DLL、反注册命令,当注册表中的某个dll或ocx文件受损时,可以使用该命令进行恢
- 用InstallShield 进行 ASP 软件的打包和自动安装
- 软件测试之实验一——如何使用JUnit,Hamcrest和Eclemma进行简单的软件测试
- java程序从使用内嵌式数据库derby打包生成exe和exe软件安装包图文解说
- 学了Java就一定要使用Java进行软件开发吗?
- as通过gradle实现多渠道打包+使用配置文件进行版本管理+指定apk输出路径和apk文件名
- 使用意法的STM32CubeMX工具软件进行系统设计和检查(草稿)
- 使用Android studio编程进行签名打包时遇到的坑
- 如何使用itunes的Promo code进行软件购买?
- 使用maven-war-plugin 对Maven项目进行动态打包
- unity学习日记:使用AssetBunndle对场景进行打包及下载与加载
- 使用SignTool对软件安装包进行数字签名(二)--进行数字签名
- 使用ADS1.2进行嵌入式软件开发(上)
- Eclipse下使用Gradle进行Android批量打包,微信分享失败
- 搞定使用tar打包的应用软件
- 使用Sencha Architect进行打包,com.sencha.exceptions.exnotfound:failed to detect ruby