当前位置:   article > 正文

使用Innosetup对软件进行打包_inno setup function

inno setup function

一:背景

由于我们以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中检测,如果文件名校验不正确,则直接退出安装:

 

  1. function InitializeSetup(): Boolean;
  2. begin
  3. if StringCheck(ExpandConstant('{srcexe}')) = 0 then
  4. begin
  5. MsgBox('文件名无效,请重新下载安装包!', mbConfirmation, MB_OK);
  6. Result := False;
  7. end
  8. else
  9. begin
  10. Result := True;
  11. end
  12. MyProgChecked := False;
  13. ShotCutName := '';
  14. end;


2.       在文件检验检测通过,则开始复制文件,当复制了辅助安装的DLL后,即可在其AfterInstall中进行网络访问,获取服务器中定制的信息:

 

  1. Source: "..\dependency\RTSClient.ini"; DestDir: "{app}"; Flags: ignoreversion
  2. Source: "..\dependency\ClientLogo.ico"; DestDir: "{app}"; Flags: ignoreversion;
  3. Source: "..\ch\ProxyWrapper.dll"; DestDir: "{app}"; Flags: ignoreversion;
  4. Source: "..\ch\SetupWrapper.dll"; DestDir: "{app}"; Flags: ignoreversion; AfterInstall:DownLoadCustomizeData()


3.       对于DLL的引用,需要进行声明后方可调用:

  1. function StringCheck(lpPath: AnsiString): Integer;
  2. external 'StringCheck@files:SetupWrapper.dll stdcall setuponly';
  3. function DownCustomizeData(lpSetupPath:AnsiString): Integer;
  4. external 'DownCustomizeData@files:SetupWrapper.dll stdcall setuponly';
  5. function GetSpecificPath(lpchar:PChar; nFlag:Integer): Integer;
  6. external 'GetSpecificPath@files:SetupWrapper.dll stdcall setuponly';
  7. function CopyCustomizeData(lpPath: AnsiString): Integer;
  8. external 'CopyCustomizeData@files:SetupWrapper.dll stdcall setuponly';


 

4.       在安装的过程中,要对路径选择、快捷方式创建等窗口进行隐藏,如下:

  1. function ShouldSkipPage(PageID: Integer): Boolean;
  2. begin
  3. Result := False;
  4. if PageID <> wpFinished then
  5. begin
  6. Result := True;
  7. end
  8. end;
  9. procedure CurPageChanged(CurPageID: Integer);
  10. begin
  11. if CurPageID <> wpFinished then
  12. begin
  13. PostMessage(WizardForm.NextButton.Handle,WM_LBUTTONDOWN,0,0);
  14. PostMessage(WizardForm.NextButton.Handle,WM_LBUTTONUP,0,0);
  15. end
  16. end;


 

5.       在获取到真正的安装路径后,需要将文件复制的路径定位到真正路径下[快捷方式的创建也是同样]:

  1. Source: "..\ch\RTSKeyGenDll.Dll"; DestDir: "{code:GetRealSetupPath}"; Flags: ignoreversion
  2. Name: "{group}\{code:GetShotCutName}"; Filename: "{code:GetRealSetupPath}\{#MyAppExeName}"; WorkingDir:"{code:GetRealSetupPath}"; IconFilename: "{code:GetRealSetupPath}\ClientLogo.ico"


 

这样才能保证安装的信息与服务器上配置的一致。

 

6.       对于调用的DLL中的函数,则具体执行文件名检测、服务器数据下载等事务,在此就不多述了

 

7.       打包的完整代码脚本如下【部分与公司业务相关代码未包含】

 

  1. ; 脚本由 Inno Setup 脚本向导 生成!
  2. ; 有关创建 Inno Setup 脚本文件的详细资料请查阅帮助文档!
  3. #define MyAppName "远程在线系统客户端"
  4. #define MyAppVersion "3.1"
  5. #define MyAppPublisher "测试公司名称"
  6. #define MyAppURL "http://www.test.com/"
  7. #define MyAppExeName "Test.exe"
  8. [Setup]
  9. ; 注: AppId的值为单独标识该应用程序。
  10. ; 不要为其他安装程序使用相同的AppId值。
  11. ; (生成新的GUID,点击 工具|在IDE中生成GUID。)
  12. AppId={{2C5393DC-8D35-4D13-B7AB-B389AE5E4111}
  13. ;AppName={code:testtest|a}
  14. AppName={#MyAppName}
  15. AppVersion={#MyAppVersion}
  16. AppVerName={#MyAppName} {#MyAppVersion}
  17. AppPublisher={#MyAppPublisher}
  18. AppPublisherURL={#MyAppURL}
  19. AppSupportURL={#MyAppURL}
  20. AppUpdatesURL={#MyAppURL}
  21. VersionInfoDescription={#MyAppName}
  22. VersionInfoProductTextVersion={#MyAppVersion}
  23. VersionInfoVersion={#MyAppVersion}
  24. DefaultDirName={pf}\Test
  25. DefaultGroupName={#MyAppName}
  26. OutputDir=.\..\package
  27. OutputBaseFilename=Test
  28. Compression=lzma
  29. SolidCompression=yes
  30. [Languages]
  31. Name: "chinesesimp"; MessagesFile: "compiler:Default.isl"
  32. [Files]
  33. Source: "..\dependency\RTSClient.ini"; DestDir: "{app}"; Flags: ignoreversion
  34. Source: "..\dependency\ClientLogo.ico"; DestDir: "{app}"; Flags: ignoreversion;
  35. Source: "..\ch\ProxyWrapper.dll"; DestDir: "{app}"; Flags: ignoreversion;
  36. Source: "..\ch\SetupWrapper.dll"; DestDir: "{app}"; Flags: ignoreversion; AfterInstall:DownLoadCustomizeData()
  37. ;完成LOGO后,需要将Rtsclient.ini与logo复制过去
  38. Source: "..\ch\AESEncryption.dll"; DestDir: "{code:GetRealSetupPath}"; Flags: ignoreversion; AfterInstall:CopyLogo()
  39. Source: "..\ch\ChatLib.dll"; DestDir: "{code:GetRealSetupPath}"; Flags: ignoreversion
  40. Source: "..\ch\FileTransferLib.dll"; DestDir: "{code:GetRealSetupPath}"; Flags: ignoreversion
  41. [Icons]
  42. Name: "{group}\{code:GetShotCutName}"; Filename: "{code:GetRealSetupPath}\{#MyAppExeName}"; WorkingDir:"{code:GetRealSetupPath}"; IconFilename: "{code:GetRealSetupPath}\ClientLogo.ico"
  43. Name: "{group}\{cm:UninstallProgram,{code:GetShotCutName}}"; Filename: "{uninstallexe}"; WorkingDir:"{code:GetRealSetupPath}"; IconFilename: "{code:GetRealSetupPath}\ClientLogo.ico"
  44. Name: "{commondesktop}\{code:GetShotCutName}"; Filename: "{code:GetRealSetupPath}\{#MyAppExeName}"; WorkingDir:"{code:GetRealSetupPath}"; IconFilename: "{code:GetRealSetupPath}\ClientLogo.ico"
  45. [Run]
  46. Filename: "{code:GetRealSetupPath}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{code:GetShotCutName}}"; Flags: nowait postinstall skipifsilent
  47. [Code]
  48. const
  49. WM_LBUTTONDOWN = 513;
  50. WM_LBUTTONUP = 514;
  51. var
  52. MyProgChecked: Boolean;
  53. RealSetupPath: String;
  54. ShotCutName : String;
  55. function StringCheck(lpPath: AnsiString): Integer;
  56. external 'StringCheck@files:SetupWrapper.dll stdcall setuponly';
  57. function DownCustomizeData(lpSetupPath:AnsiString): Integer;
  58. external 'DownCustomizeData@files:SetupWrapper.dll stdcall setuponly';
  59. function GetSpecificPath(lpchar:PChar; nFlag:Integer): Integer;
  60. external 'GetSpecificPath@files:SetupWrapper.dll stdcall setuponly';
  61. function CopyCustomizeData(lpPath: AnsiString): Integer;
  62. external 'CopyCustomizeData@files:SetupWrapper.dll stdcall setuponly';
  63. function InitializeSetup(): Boolean;
  64. begin
  65. if StringCheck(ExpandConstant('{srcexe}')) = 0 then
  66. begin
  67. MsgBox('文件名无效,请重新下载安装包!', mbConfirmation, MB_OK);
  68. Result := False;
  69. end
  70. else
  71. begin
  72. Result := True;
  73. end
  74. MyProgChecked := False;
  75. ShotCutName := '';
  76. end;
  77. procedure CancelButtonClick ( CurPageID : Integer; var Cancel, Confirm: Boolean);
  78. begin
  79. Cancel := True;
  80. Confirm := False;
  81. end;
  82. function GetShotCutName(Param: String) : String;
  83. var
  84. ShotName: String;
  85. ReturnLength: Integer;
  86. begin
  87. if Length(ShotCutName) = 0 then
  88. begin
  89. SetLength(ShotName, 270);
  90. ReturnLength := GetSpecificPath(ShotName, 2);
  91. ShotCutName := Copy(ShotName, 0, ReturnLength);
  92. //MsgBox(ShotCutName, mbConfirmation, MB_OK);
  93. end
  94. Result := ShotCutName;
  95. end;
  96. procedure CopyLogo();
  97. begin
  98. CopyCustomizeData('');
  99. end;
  100. procedure DownLoadCustomizeData();
  101. var
  102. RealPath: String;
  103. PathLength: Integer;
  104. ReturnValue: Integer;
  105. begin
  106. //在此处去获取服务器配置等信息
  107. ReturnValue := DownCustomizeData(ExpandConstant('{app}'));
  108. if ReturnValue < 0 then
  109. begin
  110. MsgBox(Format('连接服务器失败[错误码:%d],将按默认设置进行安装!', [ReturnValue]), mbConfirmation, MB_OK);
  111. //PostMessage(WizardForm.CancelButton.Handle,WM_LBUTTONDOWN,0,0);
  112. //PostMessage(WizardForm.CancelButton.Handle,WM_LBUTTONUP,0,0);
  113. end
  114. //else
  115. //begin
  116. //设置好真正的安装目录
  117. //设置好是否换了LOGO
  118. SetLength(RealPath, 270);
  119. PathLength := GetSpecificPath(RealPath, 1);
  120. RealSetupPath := Copy(RealPath, 0, PathLength);
  121. //MsgBox(RealSetupPath, mbConfirmation, MB_OK);
  122. //end
  123. end;
  124. function GetRealSetupPath(Param: String) : String;
  125. begin
  126. Result := RealSetupPath;
  127. end;
  128. function ShouldSkipPage(PageID: Integer): Boolean;
  129. begin
  130. Result := False;
  131. if PageID <> wpFinished then
  132. begin
  133. Result := True;
  134. end
  135. end;
  136. procedure CurPageChanged(CurPageID: Integer);
  137. begin
  138. if CurPageID <> wpFinished then
  139. begin
  140. PostMessage(WizardForm.NextButton.Handle,WM_LBUTTONDOWN,0,0);
  141. PostMessage(WizardForm.NextButton.Handle,WM_LBUTTONUP,0,0);
  142. end
  143. end;


声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/472288
推荐阅读
相关标签
  

闽ICP备14008679号