spiegel-keyman/developer/src/setup/bootstrapmain.pas
Marc Durdin 26cf278cc1 fix(developer): improve UI feedback on upgrade
Show a progress dialog when running the Keyman Developer upgrade
(`-au` parameter).

Fixes: #13958
Test-bot: skip
2026-03-04 06:27:36 +01:00

360 lines
9.3 KiB
ObjectPascal

(*
Name: bootstrapmain
Copyright: Copyright (C) SIL International.
Documentation:
Description:
Create Date: 4 Jun 2007
Modified Date: 12 Aug 2014
Authors: mcdurdin
Related Files:
Dependencies:
Bugs:
Todo:
Notes:
History: 04 Jun 2007 - mcdurdin - Initial version
05 Jun 2007 - mcdurdin - I817 - Fix -x option
19 Jun 2007 - mcdurdin - I817 - Translate to Unicode and remove Forms dependence
23 Aug 2007 - mcdurdin - Initial version from Desktop
17 Sep 2007 - mcdurdin - I1071 - Add -o option to work offline (no online update checks, no downloads of redists)
31 Mar 2011 - mcdurdin - I2849 - Developer online update check fails because it looks for a patch update
18 May 2012 - mcdurdin - I3306 - V9.0 - Remove TntControls + Win9x support
08 Jun 2012 - mcdurdin - I3310 - V9.0 - Unicode in Delphi fixes
08 Jun 2012 - mcdurdin - I3337 - V9.0 - Review of input/output for Unicode
08 Jun 2012 - mcdurdin - I3349 - V9.0 - Consolidate all process creation into TUtilExecute
15 Jun 2012 - mcdurdin - I3355 - Keyman Developer (and Desktop) sometimes reboot automatically with auto upgrade
03 Nov 2012 - mcdurdin - I3500 - V9.0 - Merge of I3355 - Keyman Developer (and Desktop) sometimes reboot automatically with auto upgrade
12 Aug 2014 - mcdurdin - I4366 - V9.0 - Installer still doesn't enforce Win7 or later
*)
unit bootstrapmain; // I3306
interface
uses
Classes,
SysUtils,
SetupStrings,
WideStrings;
type
EInstallInfo = class(Exception);
TInstallInfo = class
private
FEditionTitle: WideString;
FMSIFileName: WideString;
FVersion: WideString;
FStrings: TStrings;
public
constructor Create;
destructor Destroy; override;
procedure Load;
function Text(const Name: TInstallInfoText): WideString; overload;
function Text(const Name: TInstallInfoText; const Args: array of const): WideString; overload;
property Strings: TStrings read FStrings;
property EditionTitle: WideString read FEditionTitle;
property MSIFileName: WideString read FMSIFileName;
property Version: WideString read FVersion write FVersion;
end;
var
FInstallInfo: TInstallInfo = nil;
procedure Run;
var
ExtPath: string = '';
implementation
uses
TntDialogHelp,
TypInfo,
UfrmRun,
ActiveX,
GetOsVersion,
SetupForm,
CommonControls,
Unicode,
SFX,
//dialogs,
Windows;
const
ICC_PROGRESS_CLASS = $00000020; // progress
var
TempPath: string = '';
procedure DoExtractOnly(FSilent: Boolean; const FExtractOnly_Path: string); forward;
procedure CreateTempDir;
var
buf: array[0..260] of WideChar;
begin
GetTempPath(MAX_PATH-1, buf);
ExtPath := ExcludeTrailingPathDelimiter(buf); // I3310
GetTempFileName(PWideChar(ExtPath), 'kmt', 0, buf); // I3310
ExtPath := buf; // I3310
if FileExists(PWideChar(ExtPath)) then SysUtils.DeleteFile(ExtPath); // I331 // I33100
SysUtils.CreateDir(ExtPath);
TempPath := ExtPath;
end;
procedure DeletePath(const path: WideString);
var
fd: TWin32FindData;
n: DWord;
begin
n := FindFirstFile(PWideChar(path + '\*.*'), fd);
if n = INVALID_HANDLE_VALUE then Exit;
repeat
DeleteFile(PWideChar(path + '\' + fd.cFileName));
until not FindNextFile(n, fd);
FindClose(n);
RemoveDirectory(PWideChar(path));
end;
procedure RemoveTempDir;
begin
if TempPath <> '' then
DeletePath(TempPath); // I3310
end;
procedure ProcessCommandLine(var FUILevel: TInstallUILevel; var FPromptForReboot, FOffline, FExtractOnly, FContinueSetup: Boolean; var FExtractPath: WideString); // I3355 // I3500
var
i: Integer;
begin
FPromptForReboot := True; // I3355 // I3500
FUILevel := uiFull;
FOffline := False;
FExtractOnly := False;
FContinueSetup := False;
i := 1;
while i <= ParamCount do
begin
if WideSameText(ParamStr(i), '-c') then
FContinueSetup := True
else if WideSameText(ParamStr(i), '-au') then // I2849
begin
FUILevel := uiProgressOnly;
FOffline := True;
end
else if WideSameText(ParamStr(i), '-s') then
begin
FPromptForReboot := False; // I3355 // I3500
FUILevel := uiSilent;
end
else if WideSameText(ParamStr(i), '-o') then
FOffline := True
else if WideSameText(ParamStr(i), '-x') then
begin
Inc(i);
FExtractOnly := True;
FExtractPath := ParamStr(i);
end;
Inc(i);
end;
end;
procedure LogError(const s: WideString);
begin
ShowMessageW(s);
end;
procedure Run;
var
FExtractOnly: Boolean;
FContinueSetup: Boolean;
FPromptForReboot: Boolean; // I3355 // I3500
FUILevel: TInstallUILevel;
FOffline: Boolean;
FExtractOnly_Path: WideString;
BEGIN
CoInitializeEx(nil, COINIT_APARTMENTTHREADED);
try
try
{if GetOS in [osLegacy, osVista] then // I4366
begin
ShowMessageW(STR_E_OLDOSVERSION);
Exit;
end;}
FInstallInfo := TInstallInfo.Create;
try
CreateTempDir;
try
InitCommonControl(ICC_PROGRESS_CLASS);
{ Display the dialog }
//Cancel_From_InfoWin := False;
ProcessCommandLine(FUILevel, FPromptForReboot, FOffline, FExtractOnly, FContinueSetup, FExtractOnly_Path); // I3355 // I3500
if FExtractOnly then
begin
DoExtractOnly(FUILevel = uiSilent, FExtractOnly_Path);
Exit;
end;
if not ProcessArchive(ExtPath) then
begin
{ The files must be in the current directory. Use them instead }
ExtPath := ExtractFilePath(ParamStr(0)); // I3310
end;
ExtPath := IncludeTrailingPathDelimiter(ExtPath); // I3310
if ParamStr(1) <> '--test-dialog' then
begin
if not SysUtils.FileExists(ExtPath + 'setup.inf') then // I3310
begin
LogError('The file "setup.inf" is missing. Setup cannot continue.');
Exit;
end;
FInstallInfo.Load;
end;
with TfrmRun.Create(nil) do
try
ContinueSetup := FContinueSetup;
Offline := FOffline;
if FUILevel <> uiFull
then DoInstall(FUILevel, FPromptForReboot) // I3355 // I3500
else ShowModal;
finally
Free;
end;
finally
RemoveTempDir;
end;
finally
ExitCode := 1;
FInstallInfo.Free;
end;
except
on e:Exception do ShowMessageW(e.message);
end;
finally
CoUninitialize;
end;
end;
{ TInstallInfo }
constructor TInstallInfo.Create;
begin
inherited Create;
FStrings := TStringList.Create;
end;
destructor TInstallInfo.Destroy;
begin
FStrings.Free;
inherited Destroy;
end;
function TInstallInfo.Text(const Name: TInstallInfoText): WideString;
var
s: WideString;
begin
s := GetEnumName(TypeInfo(TInstallInfoText), Ord(Name));
Result := FStrings.Values[s];
if Result = '' then
Result := FDefaultStrings[Name];
//ssWelcome_Keyboards
end;
procedure TInstallInfo.Load;
var
i: Integer;
FInSetup: Boolean;
FInStrings: Boolean;
val, nm: WideString;
s: WideString;
begin
FInSetup := False;
FInStrings := False;
with TStringList.Create do
try
LoadFromFile(ExtPath + 'setup.inf'); // we'll use the preamble encoding // I3337
for i := 0 to Count - 1 do
begin
s := Trim(Strings[i]);
if s = '' then Continue;
if Copy(s, 1, 1) = '[' then
begin
FInSetup := WideSameText(s, '[Setup]');
FInStrings := WideSameText(s, '[Strings]');
end
else if FInSetup then
begin
nm := Trim(Names[i]); val := Trim(ValueFromIndex[i]);
if WideSameText(nm, 'Version') then FVersion := val
else if WideSameText(nm, 'Title') then FEditionTitle := val
else if WideSameText(nm, 'MSIFileName') then FMSIFileName := val;
end
else if FInStrings then
FStrings.Add(Strings[i]);
end;
//FGraphic := ReadString('Setup', 'Graphic', '');
if (FVersion = '') then
raise EInstallInfo.Create('setup.inf is corrupt (code 1). Setup cannot continue.');
if not SysUtils.FileExists(ExtPath + FMSIFileName) then // I3310
raise EInstallInfo.Create('Installer '+FMSIFileName+' does not exist (code 3). Setup cannot continue.');
finally
Free;
end;
end;
function TInstallInfo.Text(const Name: TInstallInfoText;
const Args: array of const): WideString;
begin
Result := WideFormat(Text(Name), Args);
end;
procedure DoExtractOnly(FSilent: Boolean; const FExtractOnly_Path: string);
var
path: string;
begin
path := FExtractOnly_Path; // I3476
if path = '' then
path := '.';
if (path <> '.') and (path <> '.\') and not DirectoryExists(path) then // I3081 // I3476
begin
if not CreateDir(path) then // I3476
begin
LogError('Setup could not create the target folder '+path); // I3476
ExitCode := Integer(GetLastError);
Exit;
end;
end;
if not ProcessArchive(path) then
begin
LogError('This file was not a valid self-extracting archive. The files should already be in the same folder as the archive.');
ExitCode := ERROR_BAD_FORMAT;
Exit;
end;
if not FSilent then
LogError('All files extracted from the archive to '+path+'\.'); // I3476
ExitCode := ERROR_SUCCESS;
end;
end.