inno-setuppascalscriptinno-setup-v6

Display the info about wrong hash of downloaded file in a nice task dialog window in Inno Setup


I originally asked about this question on another platform (here).

In Inno Setup it has the following message definition:

ErrorFileHash2=Invalid file hash: expected %1, found %2

This message is displayed when the installer tries to download and run a file with the wrong hash value.

In my script I have:

function NextButtonClick(CurPageID: integer): boolean;
begin
    Result := True;
 
  if (CurPageID = wpSelectTasks) then
  begin
    DownloadPage.Clear;
    if (WizardIsTaskSelected('downloadhelp')) then
      AddFileForDownload('{#HelpDocSetupURL}', 'HelpDocSetup.exe', 
        '{#GetSHA256OfFile("..\HelpNDoc\CHM\Output\MSAHelpDocumentationSetup.exe")}');
  end
    else
  if (CurPageID = wpReady) then
  begin
    DownloadPage.Show;
    try
      try
        DownloadPage.Download;
        Result := True;
      except
        SuppressibleMsgBox(
          AddPeriod(GetExceptionMessage), mbCriticalError, MB_OK, IDOK);
        Result := False;
      end;
    finally
      DownloadPage.Hide;
    end;
  end;
end;

The error message that is displayed when there is an issue is rather ugly. The following was suggested to me:

It only shows a message box if you don't handle the exception. Use try/except and then you can do things like re-raising the exception with a filename added or using a task dialog.

I thought I would try the message box designer:

enter image description here

Which creates the following code:

// Display a message box
SuppressibleTaskDialogMsgBox(
  'Unable to download [file]', 'This is because the checksum value does not match',
  mbError, MB_OK, ['OK'], 0, IDOK);

But I don't know what I am doing here.


Solution

  • Just replace your current:

    SuppressibleMsgBox(
      AddPeriod(GetExceptionMessage), mbCriticalError, MB_OK, IDOK);
    

    With your new code:

    SuppressibleTaskDialogMsgBox(
      'Unable to download [file]', 'This is because the checksum value does not match',
      mbError, MB_OK, ['OK'], 0, IDOK);
    

    enter image description here


    If you want to identify the failed download, you can use the value of the DownloadPage.Msg2Label.Caption (you can see it if, you move the message box).

    If you need to include the hashes in your message, you would have to parse the data from the error message. That's bit fragile approach. But if you provide a fallback message, in case the parsing fails, it's doable.

    The following function tries to parse the data out of any standard Inno Setup string:

    function ParseDataFromSetupMessage(
      Msg: string; ID: TSetupMessageID; var Data: TArrayOfString): Boolean;
    var
      MsgOrig, Pattern, PatternOrig, S: string;
      I, P, P2: Integer;
    begin
      try
        MsgOrig := Msg;
        Pattern := SetupMessage(ID);
        PatternOrig := Pattern;
        while (Msg <> '') and (Pattern <> '') do
        begin
          P := Pos('%', Pattern);
          if (P = 0) or (P = Length(Pattern)) or (P > 1) then
          begin
            if (P = 0) or (P = Length(Pattern)) then P := Length(Pattern) + 1;
    
            if Copy(Msg, 1, P - 1) <> Copy(Pattern, 1, P - 1) then Abort;
    
            Delete(Msg, 1, P - 1);
            Delete(Pattern, 1, P - 1);
          end
            else
          if (Pattern[2] < '1') or (Pattern[2] > '9') then
          begin
            if Copy(Msg, 1, 1) <> '%' then Abort;
            Delete(Pattern, 1, 1);
            Delete(Msg, 1, 1);
          end
            else
          begin
            I := StrToInt(Pattern[2]);
            Delete(Pattern, 1, 2);
            if Length(Pattern) = 0 then
            begin
              S := Msg;
              SetLength(Msg, 0);
            end
              else
            begin
              P := Pos('%', Pattern);
              if P = 0 then P := Length(Pattern) + 1;
              P2 := Pos(Copy(Pattern, 1, P - 1), Msg);
              if P2 = 0 then Abort;
              S := Copy(Msg, 1, P2 - 1);
              Delete(Msg, 1, P2 - 1);
            end;
    
            if GetArrayLength(Data) < I then
              SetArrayLength(Data, I);
            Data[I - 1] := S;
          end;
        end;
    
        if Msg <> Pattern then Abort;
    
        Result := True;
      except
        Log(Format('"%s" does not seem to match format string "%s".', [
          MsgOrig, PatternOrig]));
        Result := False;
      end;
    end;
    

    You can use both in your except block like this:

    except
      Msg := GetExceptionMessage;
      if ParseDataFromSetupMessage(Msg, msgErrorFileHash2, Data) then
      begin
        Expected := Data[0];
        Hash := Data[1];
        Msg :=
          'This is because the checksum value does not match.' + #13+
          'Download: ' + DownloadPage.Msg2Label.Caption + #13 +
          'Expected: ' + Expected + #13 +
          'Got: ' + Hash;
      end
        else
      begin
        // Failed for other reasons?
        Msg :=
          'Download has failed.' + #13+
          'Download: ' + DownloadPage.Msg2Label.Caption + #13 +
          'Details: ' + Msg;
      end;
      SuppressibleTaskDialogMsgBox(
        'Unable to download', Msg, mbError, MB_OK, ['OK'], 0, IDOK);
      Result := False;
    end;