Browse Source

Invoke the .NET installer better

Ask it not to restart the PC by itself, and handle the return code
properly
Antony Male 9 years ago
parent
commit
593f37be6f
1 changed files with 95 additions and 18 deletions
  1. 95 18
      installer/common.iss

+ 95 - 18
installer/common.iss

@@ -8,7 +8,8 @@
 #define AppPublisher "SyncTrayzor"
 #define AppURL "https://github.com/canton7/SyncTrayzor"
 #define AppDataFolder "SyncTrayzor"
-#define RunRegKey "Software\Microsoft\Windows\CurrentVersion\Run"
+#define RunRegKey "Software\Microsoft\Windows\CurrentVersion\Run"
+#define DotNetInstallerExe "dotNet451Setup.exe"
 
 
 [Setup]
@@ -43,7 +44,15 @@ ArchitecturesAllowed=x64
 #endif
 
 [Languages]
-Name: "english"; MessagesFile: "compiler:Default.isl"
+Name: "english"; MessagesFile: "compiler:Default.isl"
+
+[CustomMessages]
+InstallingDotNetFramework=Installing .NET Framework. This might take a few minutes...
+DotNetFrameworkFailedToLaunch=Failed to launch .NET Framework Installer with error "%1". Please fix the error then run this installer again.
+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.
+DotNetFrameworkFailed1603=A fatal error occurred while installing the .NET Framework. Please fix the error, then run the installer again.
+DotNetFrameworkFailed5100=Your computer does not meet the requirements of the .NET Framework. Please consult the documentation.
+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.
 
 [Tasks]
 Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
@@ -51,7 +60,10 @@ Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{
 [Dirs]
 Name: "{userappdata}\{#AppDataFolder}"
 
-[Files]
+[Files]
+; Near the beginning, as it's extracted first and this makes it cheaper
+Source: "..\{#DotNetInstallerExe}"; DestDir: {tmp}; Flags: dontcopy nocompression noencryption
+
 Source: "{#AppBin}\*"; DestDir: "{app}"; Excludes: "*.xml,*.vshost.*,*.config,*.log,FluentValidation.resources.dll,System.Windows.Interactivity.resources.dll,syncthing.exe,data,logs,ffmpegsumo.dll,d3dcompiler_43.dll,d3dcompiler_47.dll,libEGL.dll,libGLESv2.dll,pdf.dll"; Flags: ignoreversion recursesubdirs
 Source: "{#AppBin}\SyncTrayzor.exe.Installer.config"; DestDir: "{app}"; DestName: "SyncTrayzor.exe.config"; Flags: ignoreversion
 Source: "{#AppSrc}\Icons\default.ico"; DestDir: "{app}"; Flags: ignoreversion
@@ -60,43 +72,91 @@ Source: "{#AppRoot}\*.txt"; DestDir: "{app}"; Flags: ignoreversion
 Source: "*.dll"; DestDir: "{app}"; Flags: ignoreversion
 Source: "syncthing.exe"; DestDir: "{app}"; DestName: "syncthing.exe"; Flags: ignoreversion
 
-Source: "..\dotNet451Setup.exe"; DestDir: {tmp}; Flags: deleteafterinstall; Check: FrameworkIsNotInstalled
-
 [Icons]
 Name: "{group}\{#AppName}"; Filename: "{app}\{#AppExeName}"
 Name: "{group}\{cm:UninstallProgram,{#AppName}}"; Filename: "{uninstallexe}"
 Name: "{commondesktop}\{#AppName}"; Filename: "{app}\{#AppExeName}"; Tasks: desktopicon
 
 [Run]
-Filename: "{tmp}\dotNet451Setup.exe"; Parameters: "/passive /promptrestart"; Check: FrameworkIsNotInstalled; StatusMsg: "Microsoft .NET Framework 4.5.1 is being installed. Please wait..."
 Filename: "{app}\{#AppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(AppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
 
-[Code]
-function FrameworkIsNotInstalled: Boolean;
+[Code]
+var
+  GlobalRestartRequired: boolean;
+
+function DotNetIsMissing(): Boolean;
 var 
-  exists: boolean;
-  release: cardinal;
+  Exists: Boolean;
+  Release: Cardinal;
 begin
-  exists := RegQueryDWordValue(HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full', 'Release', release);
-  result := not exists or (release < 378758);
+  Exists := RegQueryDWordValue(HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full', 'Release', Release);
+  Result := not Exists or (Release < 378758);
+end;
+
+// Adapted from https://blogs.msdn.microsoft.com/davidrickard/2015/07/17/installing-net-framework-4-5-automatically-with-inno-setup/
+function InstallDotNet(): String;
+var
+  StatusText: string;
+  ResultCode: Integer;
+begin
+  StatusText := WizardForm.StatusLabel.Caption;
+  WizardForm.StatusLabel.Caption := CustomMessage('InstallingDotNetFramework');
+  WizardForm.ProgressGauge.Style := npbstMarquee;
+  try
+    ExtractTemporaryFile('{#DotNetInstallerExe}');
+    if not Exec(ExpandConstant('{tmp}\{#DotNetInstallerExe}'), '/passive /norestart /showrmui /showfinalerror', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then
+    begin
+      Result := FmtMessage(CustomMessage('DotNetFrameworkFailedToLaunch'), [SysErrorMessage(ResultCode)]);
+    end
+    else
+    begin
+      // See https://msdn.microsoft.com/en-us/library/ee942965(v=vs.110).aspx#return_codes
+      case resultCode of
+        0: begin
+          // Successful
+        end;
+        1602 : begin
+          MsgBox(CustomMessage('DotNetFrameworkFailed1602'), mbInformation, MB_OK);
+        end;
+        1603: begin
+          Result := CustomMessage('DotNetFrameworkFailed1603');
+        end;
+        1641: begin
+          GlobalRestartRequired := True;
+        end;
+        3010: begin
+          GlobalRestartRequired := True;
+        end;
+        5100: begin
+          Result := CustomMessage('DotNetFrameworkFailed5100');
+        end;
+        else begin
+          MsgBox(FmtMessage(CustomMessage('DotNetFrameworkFailedOther'), [IntToStr(ResultCode)]), mbError, MB_OK);
+        end;
+      end;
+    end;
+  finally
+    WizardForm.StatusLabel.Caption := StatusText;
+    WizardForm.ProgressGauge.Style := npbstNormal;
+  end;
 end;
 
 procedure BumpInstallCount;
 var
-  fileContents: AnsiString;
-  installCount: integer;
+  FileContents: AnsiString;
+  InstallCount: integer;
 begin
   { Increment the install count in InstallCount.txt if it exists, or create it with the contents '1' if it doesn't }
-  if LoadStringFromFile(ExpandConstant('{app}\InstallCount.txt'), fileContents) then
+  if LoadStringFromFile(ExpandConstant('{app}\InstallCount.txt'), FileContents) then
   begin
-    installCount := StrTointDef(Trim(string(fileContents)), 0) + 1;
+    InstallCount := StrTointDef(Trim(string(FileContents)), 0) + 1;
   end
   else
   begin
-    installCount := 1;
+    InstallCount := 1;
   end;
 
-  SaveStringToFile(ExpandConstant('{app}\InstallCount.txt'), IntToStr(installCount), False);
+  SaveStringToFile(ExpandConstant('{app}\InstallCount.txt'), IntToStr(InstallCount), False);
 end;
 
 procedure CurStepChanged(CurStep: TSetupStep);
@@ -136,6 +196,23 @@ begin
       end;
     end;
   end
+end;
+
+function PrepareToInstall(var NeedsRestart: Boolean): String;
+begin
+  // 'NeedsRestart' only has an effect if we return a non-empty string, thus aborting the installation.
+  // If the installers indicate that they want a restart, this should be done at the end of installation.
+  // Therefore we set the global 'restartRequired' if a restart is needed, and return this from NeedRestart()
+
+  if DotNetIsMissing() then
+  begin
+    Result := InstallDotNet();
+  end;
+end;
+
+function NeedRestart(): Boolean;
+begin
+  Result := GlobalRestartRequired;
 end;
 
 [UninstallDelete]