فهرست منبع

Added new CreateDownloadPage support function, makes CodeDownloadFiles.iss a *lot* simpler.
Todo:
-Add messages.
-Document.
-Update ISPack.
-Update whatsnew screenshot.

Martijn Laan 5 سال پیش
والد
کامیت
82c1187cd9

+ 2 - 0
Examples/AllPagesExample.iss

@@ -85,6 +85,8 @@ begin
 
   OutputProgressWizardPage := CreateOutputProgressPage('CreateOutputProgressPage', 'ADescription');
   OutputProgressWizardPageAfterID := AfterID;
+
+  { See CodeDownloadFiles.iss for a CreateDownloadPage example }
 end;
 
 function NextButtonClick(CurPageID: Integer): Boolean;

+ 30 - 112
Examples/CodeDownloadFiles.iss

@@ -1,7 +1,7 @@
 ; -- CodeDownloadFiles.iss --
 ;
-; This script shows how the PrepareToInstall event function can be used to
-; download temporary files.
+; This script shows how the CreateDownloadPage support function can be used to
+; download temporary files while showing the download progress to the user.
 
 [Setup]
 AppName=My Program
@@ -26,120 +26,38 @@ Name: "{group}\My Program"; Filename: "{app}\MyProg.exe"
 
 [Code]
 var
-  DownloadStatusLabel, DownloadFilenameLabel: TNewStaticText;
-  DownloadProgressBar: TNewProgressBar;
-  DownloadAbortButton: TNewButton;
-  DownloadControls: array of TControl;
-  NeedToAbortDownload: Boolean;
+  DownloadPage: TDownloadWizardPage;
 
-procedure SetupDownloadControl(const Dest, Src: TControl; const Parent: TWinControl);
-var
-  N: Integer;
-begin
-  N := GetArrayLength(DownloadControls);
-  SetArrayLength(DownloadControls, N+1);
-  DownloadControls[N] := Dest;
-
-  if Src <> nil then begin
-    Dest.Left := Src.Left;
-    Dest.Top := Src.Top;
-    Dest.Width := Src.Width;
-    Dest.Height := Src.Height;
-    if Src is TNewStaticText then
-      TNewStaticText(Dest).Anchors := TNewStaticText(Src).Anchors
-    else if Src is TNewProgressBar then
-      TNewProgressBar(Dest).Anchors := TNewProgressBar(Src).Anchors;
-  end;
-  Dest.Visible := False;
-  Dest.Parent := Parent;
-end;
-
-procedure DownloadAbortButtonClick(Sender: TObject);
-begin
-  NeedToAbortDownload := MsgBox('Are you sure you want to stop the download?', mbConfirmation, MB_YESNO) = IDYES;
-end;
-
-procedure CreateDownloadControls;
-var
-  Page: TWizardPage;
+function OnDownloadProgress(const Url, FileName: String; const Progress, ProgressMax: Int64): Boolean;
 begin
-  Page := PageFromID(wpPreparing);
-
-  DownloadStatusLabel := TNewStaticText.Create(Page);
-  SetupDownloadControl(DownloadStatusLabel, WizardForm.StatusLabel, Page.Surface);
-  DownloadFilenameLabel := TNewStaticText.Create(Page);
-  SetupDownloadControl(DownloadFilenameLabel, WizardForm.FilenameLabel, Page.Surface);
-  DownloadProgressBar:= TNewProgressBar.Create(Page);
-  SetupDownloadControl(DownloadProgressBar, WizardForm.ProgressGauge, Page.Surface);
-  DownloadAbortButton := TNewButton.Create(Page);
-  SetupDownloadControl(DownloadAbortButton, nil, Page.Surface);
-
-  DownloadAbortButton.Caption := '&Stop download';
-  DownloadAbortButton.Top := DownloadProgressBar.Top + DownloadProgressBar.Height + ScaleY(8);
-  DownloadAbortButton.Height := WizardForm.CancelButton.Height;
-  DownloadAbortButton.Width := WizardForm.CalculateButtonWidth([DownloadAbortButton.Caption]);
-  DownloadAbortButton.Anchors := [akLeft, akTop];
-  DownloadAbortButton.OnClick := @DownloadAbortButtonClick;
+  if Progress = ProgressMax then
+    Log(Format('Successfully downloaded file to {tmp}: %s', [FileName]));
+  Result := True;
 end;
 
 procedure InitializeWizard;
 begin
-  CreateDownloadControls;
-end;
-
-function OnDownloadProgress(const Url, FileName: String; const Progress, ProgressMax: Int64): Boolean;
-begin
-  if NeedToAbortDownload then begin
-    Log('Need to abort download.');
-    Result := False;
-  end else begin
-    if ProgressMax <> 0 then
-      Log(Format('  %d of %d bytes done.', [Progress, ProgressMax]))
-    else
-      Log(Format('  %d bytes done.', [Progress]));
-    
-    DownloadFilenameLabel.Caption := Url;
-    DownloadFilenameLabel.Update;
-
-    if ProgressMax <> 0 then begin
-      DownloadProgressBar.Style := npbstNormal;
-      DownloadProgressBar.Max := ProgressMax;
-      DownloadProgressBar.Position := Progress;
-    end else
-      DownloadProgressBar.Style := npbstMarquee;
-    DownloadProgressBar.Update;
-
+  DownloadPage := CreateDownloadPage(SetupMessage(msgWizardPreparing), SetupMessage(msgPreparingDesc), @OnDownloadProgress);
+end;
+
+function NextButtonClick(CurPageID: Integer): Boolean;
+begin
+  if CurPageID = wpReady then begin
+    DownloadPage.Clear;
+    DownloadPage.Add('https://jrsoftware.org/download.php/is.exe', 'innosetup-latest.exe', '');
+    DownloadPage.Add('https://jrsoftware.org/download.php/iscrypt.dll', 'ISCrypt.dll', '2f6294f9aa09f59a574b5dcd33be54e16b39377984f3d5658cda44950fa0f8fc');
+    DownloadPage.Show;
+    try
+      try
+        DownloadPage.Download;
+        Result := True;
+      except
+        SuppressibleMsgBox(GetExceptionMessage, mbCriticalError, MB_OK, IDOK);
+        Result := False;
+      end;
+    finally
+      DownloadPage.Hide;
+    end;
+  end else
     Result := True;
-  end;
-end;
-
-procedure ShowDownloadControls(const AVisible: Boolean);
-var
-  I: Integer;
-begin
-  for I := 0 to GetArrayLength(DownloadControls)-1 do
-    DownloadControls[I].Visible := AVisible;
-end;
-
-procedure DownloadFiles;
-begin
-  try
-    DownloadStatusLabel.Caption := 'Downloading additional files...';
-    ShowDownloadControls(True);
-    NeedToAbortDownload := False;
-    DownloadTemporaryFile('https://jrsoftware.org/download.php/is.exe', 'innosetup-latest.exe', '', @OnDownloadProgress);
-    DownloadTemporaryFile('https://jrsoftware.org/download.php/iscrypt.dll', 'ISCrypt.dll', '2f6294f9aa09f59a574b5dcd33be54e16b39377984f3d5658cda44950fa0f8fc', @OnDownloadProgress);
-  finally
-    ShowDownloadControls(False);
-  end;
-end;
-
-function PrepareToInstall(var NeedsRestart: Boolean): String;
-begin
-  try
-    DownloadFiles;
-    Result := '';
-  except
-    Result := 'Failed to download files: ' + GetExceptionMessage;
-  end;
-end;
+end;

+ 16 - 0
Projects/ScriptClasses_C.pas

@@ -537,6 +537,19 @@ begin
   end;
 end;
 
+{$IFNDEF PS_NOINT64}
+procedure RegisterDownloadWizardPage_C(Cl: TPSPascalCompiler);
+begin
+  with CL.AddClassN(Cl.FindClass('TOutputProgressWizardPage'),'TDownloadWizardPage') do
+  begin
+    RegisterProperty('AbortButton', 'TNewButton', iptr);
+    RegisterMethod('procedure Add(const Url, BaseName, RequiredSHA256OfFile: String)');
+    RegisterMethod('procedure Clear');
+    RegisterMethod('function Download: Int64');
+  end;
+end;
+{$ENDIF}
+
 procedure RegisterHandCursor_C(Cl: TPSPascalCompiler);
 begin
   cl.AddConstantN('crHand', 'Integer').Value.ts32 := crHand;
@@ -650,6 +663,9 @@ begin
   RegisterOutputMsgWizardPage_C(Cl);
   RegisterOutputMsgMemoWizardPage_C(Cl);
   RegisterOutputProgressWizardPage_C(Cl);
+{$IFNDEF PS_NOINT64}
+  RegisterDownloadWizardPage_C(Cl);
+{$ENDIF}
 
   RegisterHandCursor_C(Cl);
   

+ 15 - 0
Projects/ScriptClasses_R.pas

@@ -314,6 +314,18 @@ begin
   end;
 end;
 
+{$IFNDEF PS_NOINT64}
+procedure RegisterDownloadWizardPage_R(CL: TPSRuntimeClassImporter);
+begin
+  with CL.Add(TDownloadWizardPage) do
+  begin
+    RegisterMethod(@TDownloadWizardPage.Add, 'Add');
+    RegisterMethod(@TDownloadWizardPage.Clear, 'Clear');
+    RegisterMethod(@TDownloadWizardPage.Download, 'Download');
+  end;
+end;
+{$ENDIF}
+
 procedure RegisterHandCursor_R(Cl: TPSRuntimeClassImporter);
 const
   IDC_HAND = MakeIntResource(32649);
@@ -416,6 +428,9 @@ begin
     RegisterOutputMsgWizardPage_R(Cl);
     RegisterOutputMsgMemoWizardPage_R(Cl);
     RegisterOutputProgressWizardPage_R(Cl);
+{$IFNDEF PS_NOINT64}
+    RegisterDownloadWizardPage_R(Cl);
+{$ENDIF}
 
     RegisterHandCursor_R(Cl);
 

+ 147 - 7
Projects/ScriptDlg.pas

@@ -14,8 +14,8 @@ interface
 {$I VERSION.INC}
 
 uses
-  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, StdCtrls,
-  Wizard,
+  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, StdCtrls, Contnrs,
+  Wizard, Install,
   NewCheckListBox, NewStaticText, NewProgressBar, PasswordEdit, RichEditViewer,
   BidiCtrls, TaskbarProgressFunc;
 
@@ -149,12 +149,13 @@ type
       FMsg1Label: TNewStaticText;
       FMsg2Label: TNewStaticText;
       FProgressBar: TNewProgressBar;
+      FUseMarqueeStyle: Boolean;
       FSavePageID: Integer;
       procedure ProcessMsgs;
     public
       constructor Create(AOwner: TComponent); override;
       procedure Hide;
-      procedure Initialize;
+      procedure Initialize; virtual;
       procedure SetProgress(const Position, Max: Longint);
       procedure SetText(const Msg1, Msg2: String);
       procedure Show;
@@ -164,11 +165,37 @@ type
       property ProgressBar: TNewProgressBar read FProgressBar;
   end;
 
+{$IFNDEF PS_NOINT64}
+  TDownloadFile = class
+    Url, BaseName, RequiredSHA256OfFile: String;
+  end;
+
+  TDownloadWizardPage = class(TOutputProgressWizardPage)
+    private
+      FFiles: TObjectList;
+      FOnDownloadProgress: TOnDownloadProgress;
+      FAbortButton: TNewButton;
+      FShowProgressControlsOnNextProgress, FNeedToAbortDownload: Boolean;
+      procedure AbortButtonClick(Sender: TObject);
+      function InternalOnDownloadProgress(const Url, BaseName: string; const Progress, ProgressMax: Int64): Boolean;
+    public
+      constructor Create(AOwner: TComponent); override;
+      destructor Destroy; override;
+      procedure Initialize; override;
+      procedure Add(const Url, BaseName, RequiredSHA256OfFile: String);
+      procedure Clear;
+      function Download: Int64;
+      property OnDownloadProgress: TOnDownloadProgress write FOnDownloadProgress;
+    published
+      property AbortButton: TNewButton read FAbortButton;
+  end;
+{$ENDIF}
+  
 implementation
 
 uses
   Struct, Main, SelFolderForm, Msgs, MsgIDs, PathFunc, CmnFunc, CmnFunc2,
-  BrowseFunc;
+  BrowseFunc, Logging;
 
 const
   DefaultLabelHeight = 14;
@@ -800,14 +827,17 @@ end;
 procedure TOutputProgressWizardPage.SetProgress(const Position, Max: Longint);
 begin
   if Max > 0 then begin
+    FProgressBar.Style := npbstNormal;
     FProgressBar.Max := Max;
     FProgressBar.Position := Position;
     FProgressBar.Visible := True;
     SetAppTaskbarProgressState(tpsNormal);
     SetAppTaskbarProgressValue(Position, Max);
-  end
-  else begin
-    FProgressBar.Visible := False;
+  end else begin
+    if FUseMarqueeStyle then
+      FProgressBar.Style := npbstMarquee
+    else
+      FProgressBar.Visible := False;
     SetAppTaskbarProgressState(tpsNoProgress);
   end;
   ProcessMsgs;
@@ -856,4 +886,114 @@ begin
   end;
 end;
 
+{$IFNDEF PS_NOINT64}
+
+{--- OutputDownload ---}
+
+procedure TDownloadWizardPage.AbortButtonClick(Sender: TObject);
+begin
+  FNeedToAbortDownload := LoggedMsgBox('Are you sure you want to stop the download?', '', mbConfirmation, MB_YESNO, True, ID_YES) = IDYES;
+end;
+
+function TDownloadWizardPage.InternalOnDownloadProgress(const Url, BaseName: string; const Progress, ProgressMax: Int64): Boolean;
+begin
+  if FNeedToAbortDownload then begin
+    Log('Need to abort download.');
+    Result := False;
+  end else begin
+    if ProgressMax <> 0 then
+      Log(Format('  %d of %d bytes done.', [Progress, ProgressMax]))
+    else
+      Log(Format('  %d bytes done.', [Progress]));
+
+    FMsg2Label.Caption := Url;
+    SetProgress(Progress, ProgressMax); { This will process messages which we need for the abort button to work }
+
+    if FShowProgressControlsOnNextProgress then begin
+      FMsg2Label.Visible := True;
+      FProgressBar.Visible := True;
+      FAbortButton.Visible := True;
+      FShowProgressControlsOnNextProgress := False;
+      ProcessMsgs;
+    end;
+
+    if Assigned(FOnDownloadProgress) then
+      Result := FOnDownloadProgress(Url, BaseName, Progress, ProgressMax)
+    else
+      Result := True;
+  end;
+end;
+
+constructor TDownloadWizardPage.Create(AOwner: TComponent);
+begin
+  inherited;
+  FFiles := TObjectList.Create;
+end;
+
+destructor TDownloadWizardPage.Destroy;
+begin
+  FFiles.Free;
+  inherited;
+end;
+
+procedure TDownloadWizardPage.Initialize;
+begin
+  inherited;
+
+  FMsg1Label.Caption := 'Downloading additional files...';
+
+  FUseMarqueeStyle := True;
+
+  FAbortButton := TNewButton.Create(Self);
+  with FAbortButton do begin
+    Caption := '&Stop download';
+    Top := FProgressBar.Top + FProgressBar.Height + WizardForm.ScalePixelsY(8);
+    Width := WizardForm.CalculateButtonWidth([Caption]);
+    Anchors := [akLeft, akTop];
+    Height := WizardForm.CancelButton.Height;
+    OnClick := AbortButtonClick;
+  end;
+  SetCtlParent(FAbortButton, Surface);
+end;
+
+procedure TDownloadWizardPage.Add(const Url, BaseName, RequiredSHA256OfFile: String);
+var
+  F: TDownloadFile;
+begin
+  F := TDownloadFile.Create;
+  F.Url := Url;
+  F.BaseName := BaseName;
+  F.RequiredSHA256OfFile := RequiredSHA256OfFile;
+  FFiles.Add(F);
+end;
+
+procedure TDownloadWizardPage.Clear;
+begin
+  FFiles.Clear;
+end;
+
+function TDownloadWizardPage.Download: Int64;
+var
+  F: TDownloadFile;
+  I: Integer;
+begin
+  FMsg2Label.Caption := '';
+  FMsg2Label.Visible := False;
+  FProgressBar.Position := 0;
+  FProgressBar.Visible := False;
+  FAbortButton.Visible := False;
+  FShowProgressControlsOnNextProgress := True;
+
+  FNeedToAbortDownload := False;
+  
+  Result := 0;
+  for I := 0 to FFiles.Count-1 do begin
+    F := TDownloadFile(FFiles[I]);
+    { Don't need to set DownloadTemporaryFileProcessMessages before downloading since we already process messages ourselves. }
+    Result := Result + DownloadTemporaryFile(F.Url, F.BaseName, F.RequiredSHA256OfFile, InternalOnDownloadProgress);
+  end;
+end;
+
+{$ENDIF}
+
 end.

+ 7 - 0
Projects/ScriptFunc.pas

@@ -14,7 +14,11 @@ interface
 const
 
   { ScriptDlg }
+{$IFNDEF PS_NOINT64}
+  ScriptDlgTable: array [0..13] of AnsiString =
+{$ELSE}
   ScriptDlgTable: array [0..12] of AnsiString =
+{$ENDIF}
   (
     'function PageFromID(const ID: Integer): TWizardPage;',
     'function PageIndexFromID(const ID: Integer): Integer;',
@@ -26,6 +30,9 @@ const
     'function CreateOutputMsgPage(const AfterID: Integer; const ACaption, ADescription, AMsg: String): TOutputMsgWizardPage;',
     'function CreateOutputMsgMemoPage(const AfterID: Integer; const ACaption, ADescription, ASubCaption: String; const AMsg: AnsiString): TOutputMsgMemoWizardPage;',
     'function CreateOutputProgressPage(const ACaption, ADescription: String): TOutputProgressWizardPage;',
+{$IFNDEF PS_NOINT64}
+    'function CreateDownloadPage(const ACaption, ADescription: String; const OnDownloadProgress: TOnDownloadProgress): TDownloadWizardPage;',
+{$ENDIF}
     'function ScaleX(X: Integer): Integer;',
     'function ScaleY(Y: Integer): Integer;',
     'function CreateCustomForm: TSetupForm;'

+ 28 - 0
Projects/ScriptFunc_R.pas

@@ -143,6 +143,11 @@ var
   NewOutputMsgPage: TOutputMsgWizardPage;
   NewOutputMsgMemoPage: TOutputMsgMemoWizardPage;
   NewOutputProgressPage: TOutputProgressWizardPage;
+{$IFNDEF PS_NOINT64}
+  NewDownloadPage: TDownloadWizardPage;
+  P: PPSVariantProcPtr;
+  OnDownloadProgress: TOnDownloadProgress;
+{$ENDIF}
   NewSetupForm: TSetupForm;
 begin
   PStart := Stack.Count-1;
@@ -270,6 +275,29 @@ begin
       raise;
     end;
     Stack.SetClass(PStart, NewOutputProgressPage);
+{$IFNDEF PS_NOINT64}
+  end else if Proc.Name = 'CREATEDOWNLOADPAGE' then begin
+    if IsUninstaller then
+      NoUninstallFuncError(Proc.Name);
+    P := Stack.Items[PStart-3];
+    { ProcNo 0 means nil was passed by the script }
+    if P.ProcNo <> 0 then
+      OnDownloadProgress := TOnDownloadProgress(Caller.GetProcAsMethod(P.ProcNo))
+    else
+      OnDownloadProgress := nil;
+    NewDownloadPage := TDownloadWizardPage.Create(GetWizardForm);
+    try
+      NewDownloadPage.Caption := Stack.GetString(PStart-1);
+      NewDownloadPage.Description := Stack.GetString(PStart-2);
+      GetWizardForm.AddPage(NewDownloadPage, -1);
+      NewDownloadPage.Initialize;
+      NewDownloadPage.OnDownloadProgress := OnDownloadProgress;
+    except
+      NewDownloadPage.Free;
+      raise;
+    end;
+    Stack.SetClass(PStart, NewDownloadPage);
+{$ENDIF}
   end else if Proc.Name = 'SCALEX' then begin
     InitializeScaleBaseUnits;
     Stack.SetInt(PStart, MulDiv(Stack.GetInt(PStart-1), ScaleBaseUnitX, OrigBaseUnitX));

+ 1 - 1
whatsnew.htm

@@ -52,7 +52,6 @@ For conditions of distribution and use, see <a href="https://jrsoftware.org/file
 <ul>
   <li>Added new <tt>DownloadTemporaryFile</tt> support function to download files without using a third-party tool:
   <ul>
-    <li>Allows you to <a href="https://i.imgur.com/ZLmiS3t.png">show the download progress</a> to the user. See the new <i>CodeDownloadFiles.iss</i> example script for an example.</li>
     <li>Supports HTTPS (but not expired or self-signed certificates) and HTTP.</li>
     <li>Redirects are automatically followed and proxy settings are automatically used.</li>
     <li>Safe to use from services unlike existing third-party tools.</li>
@@ -60,6 +59,7 @@ For conditions of distribution and use, see <a href="https://jrsoftware.org/file
     <li>Supports basic authentication.</li>
   </ul>
   </li>
+  <li>Added new <tt>CreateDownloadPage</tt> support function to easily <a href="https://i.imgur.com/ZLmiS3t.png">show the download progress</a> to the user. See the new <i>CodeDownloadFiles.iss</i> example script for an example.</li>
   <li>Added new <tt>DownloadTemporaryFileSize</tt> support function to get the size of a file without downloading it.</li>
   <li>Added new <tt>GetSHA256OfFile</tt>, <tt>GetSHA256OfString</tt>, and <tt>GetSHA256OfUnicodeString</tt> support functions to calculate SHA-256 hashes.</li>
   <li><b>Change in default behavior:</b> Setup no longer disables itself entirely while <tt>PrepareToInstall</tt> is running. Instead only the Cancel button is disabled.</li>