1.背景
用户一直使用某厂家开发的即时通讯软件,近期大量用户反馈,不能正常收取离线文件,严重影响了用户的日常工作。经询问厂家后,得知软件需升级至新版后可解决此问题。在安装新版客户端时,必须要把旧版本客户端文件全部卸载。但是使用软件的终端用户计算机水平层次不齐,尤其到乡、村级的用户计算机操作不熟练。用户总部希望新版本的安装包能够提供自动卸载旧版软件的功能,简化终端用户的操作步骤。
2.问题
原厂商开发的软件没有提供自动检测旧版本是否已安装的功能,和厂商寻求帮助后,厂商反馈:由于(各种…………)或者技术原因或者商务原因,无法提供此功能(如果大家在集成商单位工作,可能也已经遇到过这种情况)。到目前为止基本无解了,但是兄弟我偏愿意自己搞搞,厂家不给弄的东西,咱要给弄。客户是上帝嘛。况且我第一感觉这个应该不难,只是厂家可能由于某种原因不愿意给解决吧。
3.客户端安装软件解包
我想如果要增加这个检测旧版本功能应该需要先把安装包解压出来看看,然后再根据情况具体规划应对方案。于是百度一番得知,通用的安装包制作工具无非是Install Shield,AdvancedInstaller,Innosetup 等等,这些功能都还挺强大的。
解包工具好像Universal Extractor比较强大,于是下载下来一试果然威力不凡。用Universal Extractor解包出来的安装脚本部分内容有乱码。经Universal Extractor提示,客户端软件是用inno setup打包的,而inno setup官方提供了innoextractor解包工具,专门用来解开innosetup打包的文件。用innoextractor解出来的安装脚本install_script.iss文件内容也有部分汉字是乱码,这个文件和Universal Extractor解出来的安装脚本install_script.iss文件内容一做比对,就知道乱码文字是什么了。自己把这些乱码该成汉字就可以了。
对于检测旧版本的功能,只需要修改install_script.iss文件即可实现。在该文件中可以使用pascal代码来实现程序安装时或卸载时的一些自定义功能。
4.install_script.iss文件格式解析
本人对install_script.iss文件也不熟悉,下面的内容从网上复制来的,原文太长,删除了部分内容:
; 注: Setup定义了软件名称,版本,及编译时输出的文件名等基本信息。 [Setup] ; 注: AppId的值为单独标识该应用程序。 ; 不要为其他安装程序使用相同的AppId值。 ; (生成新的GUID,点击 工具|在IDE中生成GUID。) AppId={{A9861883-31C5-4324-BD9A-DC3271EEB675} ;程序名 AppName=ISsample ;版本号 AppVerName=ISsample 1.0.0.0 ;发布者名 ;默认安装目录 DefaultDirName={pf}\ISsample ;默认开始菜单名 DefaultGroupName=ISsample ;是否打开->可选安装开始菜单项 ;AllowNoIcons=yes ;输出文件夹 OutputDir=C:\Example\InnoSetup\out ;输出文件名 OutputBaseFilename=setup ;安装图标 SetupIconFile=C:\Example\原始文件\title.ico ;压缩相关 Compression=lzma SolidCompression=yes ;制作选择语言, [Languages] Name: "chs"; MessagesFile: "compiler:Default.isl" ;LicenSeFile :"C:\Example\原始文件\chs\agreement.txt" Name: "en"; MessagesFile: "compiler:Languages\English.isl";LicenSeFile :"C:\Example\原始文件\en\agreement.txt" ;文件安装 [Files] ;多语言安装环境设置 公共参数Languages 来设置 Source: "C:\Example\原始文件\enfile.txt"; DestDir: "{app}"; Languages: en ; Flags: ignoreversion Source: "C:\Example\原始文件\chsfile.txt"; DestDir: "{app}"; Languages: chs ; Flags: ignoreversion ;用户自定义任务 Tasks Source: "C:\Example\原始文件\Tasks\tasks_1.txt"; DestDir: "{app}\Tasks"; Flags: ignoreversion ;Tasks : Tasks_1 Source: "C:\Example\原始文件\Tasks\tasks_2.txt"; DestDir: "{app}\Tasks"; Flags: ignoreversion ;Tasks :Tasks_2 Source: "C:\Example\原始文件\Tasks\tasks_Components.txt"; DestDir: "{app}\Tasks"; Flags: ignoreversion ;Tasks :Tasks_2 ;开始菜单,桌面快捷方式 [Icons] Name: "{group}\ISsample"; Filename: "{app}\ISsample.exe" Name: "{group}\{cm:UninstallProgram,ISsample}"; Filename: "{uninstallexe}" Name: "{commondesktop}\ISsample"; Filename: "{app}\ISsample.exe"; Tasks: desktopicon Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\ISsample"; Filename: "{app}\ISsample.exe"; Tasks: quicklaunchicon ;更改显示在程序中显示的消息文本 [Messages] BeveledLabel=某某公司 ;卸载对话框说明 ConfirmUninstall=您真的想要从电脑中卸载ISsample吗?%n%n按 [是] 则完全删除 %1 以及它的所有组件;%n按 [否]则让软件继续留在您的电脑上. ;定义解压说明 ;StatusExtractFiles=解压并复制主程序文件及相关库文件... ;用于在用户系统中创建,修改或删除注册表健值 [Registry] Root: HKLM ;SubKey:"Software\ISsample";ValueType:dword;ValueName:config;ValueData:10 ;Flags:uninsdeletevalue ;在执行脚本 [code] ;在执行脚本,在这部分可以添加pascal代码
;我的检测旧版本,并提示卸载的代码就是在这里添加的
5.编写pascal代码
唉,哥们儿也不是搞开发的,研究pascal可真有点赶鸭子上架了,没办法。只好硬着头皮试了下。还好原来研究过一些小程序代码的经验,再简单的看些pascal教程,也能写出个七七八八来。代码写的比较糙,能用就行,大伙儿凑合看吧。
function InitializeSetup (): Boolean; var UnString: String; ResultCode: Integer; IfInstalled: Boolean; Names: TArrayOfString; I: Integer; S: String; SoftUnInstall: Boolean; IfUnstall: Boolean; begin if RegGetSubkeyNames(HKLM, 'Software\Microsoft\Windows\CurrentVersion\Uninstall', Names) then begin // 循环扫描注册表中的Uninstall键中的子键,那软件竟然没有自己的GUID,我晕,只能挨个找了 for I := 0 to GetArrayLength(Names)-1 do begin // 查看是否子键中有UninstallString项,如果没有久继续循环直到结束。如果有,就取UninstallString中的值准备做下一步判断 SoftUnInstall := RegQueryStringValue(HKLM , 'Software\Microsoft\Windows\CurrentVersion\Uninstall\' + Names[I],'UninstallString',UnString); if SoftUnInstall then begin // 一般UninstallString项的值类似这样:d:\Program Files\xxx\unins000.exe // 如果UninstallString中的值包含xxx这个关键字,就认为是曾经安装过旧版本 if pos('xxx',UnString) <> 0 then begin // #13#10#13#10表示换行,提示文字过多,不换行会挤在一起,视觉效果不好 IfUnstall := MsgBox('发现旧版程序,即将卸载!'+ #13#10#13#10 +'请备份您的用户名和密码,安装过程中将清空聊天记录和登录信息!'+ #13#10#13#10 +'是否继续?', mbConfirmation, MB_YESNO or MB_DEFBUTTON2) = IDYes; if IfUnstall then begin UnString := RemoveQuotes(UnString); // 执行卸载程序 Exec(UnString, '', '', SW_SHOW, ewWaitUntilTerminated, ResultCode); Result := true; break; end else begin MsgBox('程序将退出安装!', mbInformation, MB_OK); Result := False; break; end; end else Result := true; end else Result := true; end; end; end;
6.用Inno Setup打包测试
打包后,测试。经测试在win10 32bit,win7 32 bit,win7 64bit,winxp 32bit,win2008 64bit上测试无问题。
7.上传代码给厂商
还是要麻烦厂商啊,谁让咱用人家的产品了是不。如果人家恼了不管你,你就更没办法了。
把代码给厂商后,一再希望人家打包时考虑下这段代码。人家还真就给加上了,谢谢。
8.题外话
每次联系厂家,人家的服务态度还是挺好的。
9.后记
这个搜索注册表的方式,其实效率还是比较低的。但是由于旧版本程序安装好后,不知如何精确定位,也只好遍历了。如果大家有什么更好的方法,希望留言指正。
10.完结
嗯,到此为止应该没什么大问题了,等着向广大用户群体下发新的安装包吧。