inno-setuppascalscript

Marquee style progress bar in Inno Setup for lengthy synchronous operation in C# DLL


I create setup for my program using Inno Setup. I have code C# and some wizard page runs it. I want to see "ProgressBar" (style Marquee) when my code C# works a long time. I want to understand if my code C# is working or hanging. How create a "ProgressBar" (style Marquee) in Inno Setup for my code C#. Thank you for any idea.

Example Progress par:

enter image description here

Some code:

[Files]
Source: "GetDataBases.dll"; Flags: dontcopy

[Code]

function ServerOfDataBases(
  scriptName, server, user, password,nameDB: string;
  out strout: WideString): Integer;
  external 'ServerOfDataBases@files:GetDataBases.dll stdcall';

var
  ServerDetailsPage: TInputQueryWizardPage;

function CallDB(scriptName, server, user, password, nameDB: string):string;
var
  retval: Integer;
  str: WideString;
begin  
  retval := ServerOfDataBases(scriptName, server, user, password, nameDB, str); 
  Result:= str; 
end;

procedure InitializeWizard;
var
 ...
begin
  ServerDetailsPage := CreateInputQueryPage(wpWelcome, '', '', '...');
  ServerDetailsPage.Add('Server...', False);
  ...
  ServerDetailsPage.Values[0] := '';
end;

function NextButtonClick(CurPageID: Integer): Boolean;
var
  DataDases: String;
...
begin
  ...  
  if CurPageID = ServerDetailsPage.ID then
  begin
    ...
    DataDases := '';
    scriptName := 'ListDB';
    DataDases := CallDB(
      scriptName, ServerDetailsPage.Values[0], ServerDetailsPage.Values[2],
      ServerDetailsPage.Values[3], '');
     ...
  end;
end;

    

Solution

  • This is not easy. Calling into synchronous function effectively blocks the GUI thread. So you cannot animate the progress bar.

    You have to run the lengthy task on a different thread. As it seems to be your DLL, you can modify it to offer an asynchronous API. Something like:

    private static Task _task = null;
    private static int _outcome;
    
    [DllExport(CallingConvention = CallingConvention.StdCall)]
    public static void StartSomething()
    {
        // Starts an operation on a different thread
        _task = new Task(() => { Something(); });
        _task.Start();
    }
    
    // The operation to run on a different thread
    private static void Something()
    {
        // The lengthy operation
        Thread.Sleep(10000);
        // Remember the results
        _outcome = 123;
    }
    
    [DllExport(CallingConvention = CallingConvention.StdCall)]
    public static bool HasSomethingCompleted(out int outcome)
    {
        // Check if the operation has completed
        bool result = _task.IsCompleted;
        // And collect its results
        outcome = _outcome;
        return result;
    }
    

    And then you can use this from Inno Setup like:

    procedure InitializeWizard();
    begin
      ServerDetailsPage := CreateInputQueryPage(wpWelcome, '', '', '...');
    end;
    
    procedure CallDll;
    var
      ProgressPage: TOutputMarqueeProgressWizardPage;
      Outcome: Integer;
    begin
      StartSomething;
    
      ProgressPage := CreateOutputMarqueeProgressPage('Calling DLL', '');
      ProgressPage.Show;
      try
        ProgressPage.ProgressBar.Style := npbstMarquee;
        // Wait for the Something to finish
        while not HasSomethingCompleted(Outcome) do
        begin
          ProgressPage.Animate;
          Sleep(50);
        end;
    
      finally
        ProgressPage.Hide;
        ProgressPage.Free;
      end;
    
      MsgBox(Format(
        'Something has finished and the outcome was %d', [Outcome]),
        mbInformation, MB_OK);
    end;
    
    function NextButtonClick(CurPageID: Integer): Boolean;
    begin
      if CurPageID = ServerDetailsPage.ID then
      begin
        CallDll;
      end;
      Result := True;
    end;
    

    enter image description here

    For a similar question see:
    How to delay without freezing in Inno Setup