common.iss 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. #define AppExeName "SyncTrayzor.exe"
  2. #define AppRoot "..\.."
  3. #define AppSrc AppRoot + "\src\SyncTrayzor"
  4. #define AppBin AppRoot +"\bin\" + Arch + "\Release"
  5. #define AppExe AppBin + "\SyncTrayzor.exe"
  6. #define AppName GetStringFileInfo(AppExe, "ProductName")
  7. #define AppVersion GetVersionNumbersString(AppExe)
  8. #define AppPublisher "SyncTrayzor"
  9. #define AppURL "https://github.com/canton7/SyncTrayzor"
  10. #define AppDataFolder "SyncTrayzor"
  11. #define RunRegKey "Software\Microsoft\Windows\CurrentVersion\Run"
  12. #define DotNetInstallerExe "dotNet472Setup.exe"
  13. #define DonateUrl "https://synctrayzor.antonymale.co.uk/donate"
  14. [Setup]
  15. AppId={{#AppId}
  16. AppName={#AppName} ({#Arch})
  17. AppVersion={#AppVersion}
  18. VersionInfoVersion={#AppVersion}
  19. ;AppVerName={#AppName} {#AppVersion}
  20. AppPublisher={#AppPublisher}
  21. AppPublisherURL={#AppURL}
  22. AppSupportURL={#AppURL}
  23. AppUpdatesURL={#AppURL}
  24. DefaultDirName={commonpf}\{#AppName}
  25. DefaultGroupName={#AppName}
  26. AllowNoIcons=yes
  27. OutputDir="."
  28. OutputBaseFilename={#AppName}Setup-{#Arch}
  29. SetupIconFile={#AppSrc}\Icons\default.ico
  30. WizardSmallImageFile=..\icon.bmp
  31. Compression=lzma2/max
  32. ;Compression=None
  33. SolidCompression=yes
  34. PrivilegesRequired=admin
  35. CloseApplications=yes
  36. RestartApplications=no
  37. ; If we try and close CefSharp.BrowserSubprocess.exe we'll fail - it doesn't respond well
  38. ; However if we close *just* SyncTrayzor, that will take care of shutting down CefSharp and syncthing
  39. CloseApplicationsFilter=SyncTrayzor.exe
  40. TouchDate=current
  41. WizardStyle=modern
  42. ; We do access user areas, but only as a best-effort attempt to clean up after ourselves
  43. UsedUserAreasWarning=no
  44. #if "x64" == Arch
  45. ArchitecturesInstallIn64BitMode=x64
  46. ArchitecturesAllowed=x64
  47. #endif
  48. [Languages]
  49. Name: "english"; MessagesFile: "compiler:Default.isl"
  50. [CustomMessages]
  51. InstallingDotNetFramework=Installing .NET Framework. This might take a few minutes...
  52. DotNetFrameworkFailedToLaunch=Failed to launch .NET Framework Installer with error "%1". Please fix the error then run this installer again.
  53. DotNetFrameworkFailed1602=.NET Framework installation was cancelled. This installation can continue, but be aware that this application may not run unless the .NET Framework installation is completed successfully.
  54. DotNetFrameworkFailed1603=A fatal error occurred while installing the .NET Framework. Please fix the error, then run the installer again.
  55. DotNetFrameworkFailed5100=Your computer does not meet the requirements of the .NET Framework. Please consult the documentation.
  56. DotNetFrameworkFailedOther=The .NET Framework installer exited with an unexpected status code "%1". Please review any other messages shown by the installer to determine whether the installation completed successfully, and abort this installation and fix the problem if it did not.
  57. [Tasks]
  58. Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
  59. [Dirs]
  60. Name: "{userappdata}\{#AppDataFolder}"
  61. [Files]
  62. ; Near the beginning, as it's extracted first and this makes it cheaper
  63. Source: "..\{#DotNetInstallerExe}"; DestDir: {tmp}; Flags: dontcopy nocompression noencryption
  64. Source: "{#AppBin}\*"; DestDir: "{app}"; Excludes: "*.xml,*.vshost.*,*.config,*.log,*.pdb,FluentValidation.resources.dll,System.Windows.Interactivity.resources.dll,syncthing.exe,data,logs,cef_extensions.pak,d3dcompiler_47.dll,libEGL.dll,libGLESv2.dll,swiftshader/libEGL.dll,swiftshader/libGLESv2.dll"; Flags: ignoreversion recursesubdirs
  65. Source: "{#AppBin}\SyncTrayzor.exe.Installer.config"; DestDir: "{app}"; DestName: "SyncTrayzor.exe.config"; Flags: ignoreversion
  66. Source: "{#AppSrc}\Icons\default.ico"; DestDir: "{app}"; Flags: ignoreversion
  67. Source: "{#AppRoot}\*.md"; DestDir: "{app}"; Flags: ignoreversion
  68. Source: "{#AppRoot}\*.txt"; DestDir: "{app}"; Flags: ignoreversion
  69. Source: "vc++\*.dll"; DestDir: "{app}"; Flags: ignoreversion
  70. Source: "ucrt\*.dll"; DestDir: "{app}"; Flags: ignoreversion; OnlyBelowVersion: 10.0
  71. Source: "syncthing.exe"; DestDir: "{app}"; DestName: "syncthing.exe"; Flags: ignoreversion
  72. [InstallDelete]
  73. Type: files; Name: "{app}\msvcp120.dll"
  74. Type: files; Name: "{app}\msvcr120.dll"
  75. Type: files; Name: "{app}\vccorlib120.dll"
  76. Type: files; Name: "{app}\*.pdb"
  77. Type: files; Name: "{app}\System.Windows.Interactivity.dll"
  78. [Icons]
  79. Name: "{group}\{#AppName}"; Filename: "{app}\{#AppExeName}"
  80. Name: "{group}\{cm:UninstallProgram,{#AppName}}"; Filename: "{uninstallexe}"
  81. Name: "{commondesktop}\{#AppName}"; Filename: "{app}\{#AppExeName}"; Tasks: desktopicon
  82. [Run]
  83. Filename: "{app}\{#AppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(AppName, '&', '&&')}}"; Flags: nowait postinstall; Parameters: {code:SyncTrayzorStartFlags}; Check: ShouldStartSyncTrayzor
  84. [Code]
  85. var
  86. GlobalRestartRequired: boolean;
  87. function DotNetIsMissing(): Boolean;
  88. var
  89. Exists: Boolean;
  90. Release: Cardinal;
  91. begin
  92. // https://docs.microsoft.com/en-us/dotnet/framework/migration-guide/how-to-determine-which-versions-are-installed#minimum-version
  93. Exists := RegQueryDWordValue(HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full', 'Release', Release);
  94. Result := not Exists or (Release < 461808);
  95. end;
  96. // Adapted from https://blogs.msdn.microsoft.com/davidrickard/2015/07/17/installing-net-framework-4-5-automatically-with-inno-setup/
  97. function InstallDotNet(): String;
  98. var
  99. StatusText: string;
  100. ResultCode: Integer;
  101. begin
  102. StatusText := WizardForm.StatusLabel.Caption;
  103. WizardForm.StatusLabel.Caption := CustomMessage('InstallingDotNetFramework');
  104. WizardForm.ProgressGauge.Style := npbstMarquee;
  105. try
  106. ExtractTemporaryFile('{#DotNetInstallerExe}');
  107. if not Exec(ExpandConstant('{tmp}\{#DotNetInstallerExe}'), '/passive /norestart /showrmui /showfinalerror', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then
  108. begin
  109. Result := FmtMessage(CustomMessage('DotNetFrameworkFailedToLaunch'), [SysErrorMessage(ResultCode)]);
  110. end
  111. else
  112. begin
  113. // See https://msdn.microsoft.com/en-us/library/ee942965(v=vs.110).aspx#return_codes
  114. case resultCode of
  115. 0: begin
  116. // Successful
  117. end;
  118. 1602 : begin
  119. MsgBox(CustomMessage('DotNetFrameworkFailed1602'), mbInformation, MB_OK);
  120. end;
  121. 1603: begin
  122. Result := CustomMessage('DotNetFrameworkFailed1603');
  123. end;
  124. 1641: begin
  125. GlobalRestartRequired := True;
  126. end;
  127. 3010: begin
  128. GlobalRestartRequired := True;
  129. end;
  130. 5100: begin
  131. Result := CustomMessage('DotNetFrameworkFailed5100');
  132. end;
  133. else begin
  134. MsgBox(FmtMessage(CustomMessage('DotNetFrameworkFailedOther'), [IntToStr(ResultCode)]), mbError, MB_OK);
  135. end;
  136. end;
  137. end;
  138. finally
  139. WizardForm.StatusLabel.Caption := StatusText;
  140. WizardForm.ProgressGauge.Style := npbstNormal;
  141. end;
  142. end;
  143. procedure BumpInstallCount;
  144. var
  145. FileContents: AnsiString;
  146. InstallCount: integer;
  147. begin
  148. { Increment the install count in InstallCount.txt if it exists, or create it with the contents '1' if it doesn't }
  149. if LoadStringFromFile(ExpandConstant('{app}\InstallCount.txt'), FileContents) then
  150. begin
  151. InstallCount := StrTointDef(Trim(string(FileContents)), 0) + 1;
  152. end
  153. else
  154. begin
  155. InstallCount := 1;
  156. end;
  157. SaveStringToFile(ExpandConstant('{app}\InstallCount.txt'), IntToStr(InstallCount), False);
  158. end;
  159. procedure URLLabelOnClick(Sender: TObject);
  160. var
  161. ErrorCode: Integer;
  162. begin
  163. ShellExec('open', '{#DonateUrl}', '', '', SW_SHOWNORMAL, ewNoWait, ErrorCode);
  164. end;
  165. procedure InitializeWizard;
  166. var
  167. URLLabel: TNewStaticText;
  168. begin
  169. URLLabel := TNewStaticText.Create(WizardForm);
  170. URLLabel.Caption := 'Donate';
  171. URLLabel.Cursor := crHand;
  172. URLLabel.Parent := WizardForm;
  173. URLLabel.Font.Style := URLLabel.Font.Style + [fsUnderline];
  174. URLLabel.Font.Color := clBlue;
  175. URLLabel.Top := WizardForm.ClientHeight - URLLabel.Height - 30;
  176. URLLabel.Left := ScaleX(20)
  177. URLLabel.Anchors := [akLeft, akBottom]
  178. URLLabel.OnClick := @URLLabelOnClick;
  179. end;
  180. procedure CurStepChanged(CurStep: TSetupStep);
  181. var
  182. FindRec: TFindRec;
  183. FolderPath: String;
  184. FilePath: String;
  185. ExeConfig: String;
  186. begin
  187. if CurStep = ssInstall then
  188. begin
  189. BumpInstallCount();
  190. { We might be being run from ProcessRunner.exe, *and* we might be trying to update it. Funsies. Let's rename it (which Windows lets us do) }
  191. DeleteFile(ExpandConstant('{app}\ProcessRunner.exe.old'));
  192. RenameFile(ExpandConstant('{app}\ProcessRunner.exe'), ExpandConstant('{app}\ProcessRunner.exe.old'));
  193. Log(ExpandConstant('Looking for resource files in {app}\*'));
  194. { Remove resource files. This means that out-of-date languages will be removed, which (as a last-ditch resore) will alert maintainers that something's wrong }
  195. if FindFirst(ExpandConstant('{app}\*'), FindRec) then
  196. begin
  197. try
  198. repeat
  199. if (FindRec.Attributes and FILE_ATTRIBUTE_DIRECTORY <> 0) and (FindRec.Name <> '.') and (FindRec.Name <> '..') then
  200. begin
  201. FolderPath := ExpandConstant('{app}\') + FindRec.Name;
  202. FilePath := FolderPath + '\SyncTrayzor.resources.dll';
  203. if DeleteFile(FilePath) then
  204. begin
  205. Log('Deleted ' + FilePath);
  206. if DelTree(FolderPath, True, False, False) then
  207. Log('Deleted ' + FolderPath);
  208. end;
  209. end;
  210. until not FindNext(FindRec);
  211. finally
  212. FindClose(FindRec);
  213. end;
  214. end;
  215. end
  216. else if CurStep = ssPostInstall then
  217. begin
  218. ExeConfig := ExpandConstant('{param:SyncTrayzorExeConfig}');
  219. if ExeConfig <> '' then
  220. begin
  221. if FileExists(ExeConfig) then
  222. begin
  223. FileCopy(ExeConfig, ExpandConstant('{app}\SyncTrayzor.exe.config'), false);
  224. end
  225. else
  226. begin
  227. MsgBox('Could not find SyncTrayzorExeConfig file: ' + ExeConfig + '. Using default.', mbError, MB_OK);
  228. end
  229. end
  230. end
  231. end;
  232. function CmdLineParamGiven(const Value: String): Boolean;
  233. var
  234. I: Integer;
  235. begin
  236. // Can't use {param}, as it doesn't match flags with no value
  237. Result := False;
  238. for I := 1 to ParamCount do
  239. if CompareText(ParamStr(I), Value) = 0 then
  240. begin
  241. Result := True;
  242. Exit;
  243. end;
  244. end;
  245. function PrepareToInstall(var NeedsRestart: Boolean): String;
  246. begin
  247. // 'NeedsRestart' only has an effect if we return a non-empty string, thus aborting the installation.
  248. // If the installers indicate that they want a restart, this should be done at the end of installation.
  249. // Therefore we set the global 'restartRequired' if a restart is needed, and return this from NeedRestart()
  250. if not CmdLineParamGiven('/SkipDotNetInstall') and DotNetIsMissing() then
  251. begin
  252. Result := InstallDotNet();
  253. end;
  254. end;
  255. function NeedRestart(): Boolean;
  256. begin
  257. Result := GlobalRestartRequired;
  258. end;
  259. function ShouldStartSyncTrayzor(): Boolean;
  260. begin
  261. Result := (not WizardSilent()) or CmdLineParamGiven('/StartSyncTrayzor');
  262. end;
  263. function SyncTrayzorStartFlags(param: String): String;
  264. begin
  265. if WizardSilent() then begin
  266. Result := '-minimized'
  267. end else begin
  268. Result := ''
  269. end;
  270. end;
  271. // We won't be able to find keys for users other than the one running the installer, but try and do
  272. // a best-effort attempt to cleaning ourselves up.
  273. procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
  274. var
  275. keyValueNames: TArrayOfString;
  276. keyValue: String;
  277. i: Integer;
  278. begin
  279. if CurUninstallStep = usPostUninstall then
  280. begin
  281. if RegGetValueNames(HKEY_CURRENT_USER, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Run', keyValueNames) then
  282. begin
  283. for i := 0 to GetArrayLength(keyValueNames)-1 do
  284. begin
  285. if Pos('SyncTrayzor', keyValueNames[i]) = 1 then
  286. begin
  287. if RegQueryStringValue(HKEY_CURRENT_USER, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Run', keyValueNames[i], keyValue) then
  288. begin
  289. if Pos(ExpandConstant('"{app}\{#AppExeName}"'), keyValue) = 1 then
  290. begin
  291. RegDeleteValue(HKEY_CURRENT_USER, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Run', keyValueNames[i]);
  292. end;
  293. end;
  294. end
  295. end;
  296. end;
  297. end;
  298. end;
  299. [UninstallDelete]
  300. Type: files; Name: "{app}\ProcessRunner.exe.old"
  301. Type: files; Name: "{app}\InstallCount.txt"
  302. Type: filesandordirs; Name: "{userappdata}\{#AppDataFolder}"
  303. Type: filesandordirs; Name: "{localappdata}\{#AppDataFolder}"