Inno Setup language selector with VCL Styles
Asked Answered
B

2

4

Is there any way to use language selector (Inno Setup) with VCL Styles? How?

Breadthways answered 7/12, 2016 at 15:26 Comment(0)
P
6

The "Select Setup Language" dialog displays before the InitializeSetup event function is called. So you cannot load the skin for the dialog.


As a workaround, you can implement your own "language" dialog, and display that from the InitializeSetup. This way the custom dialog will be skinned. Once a user selects a language, you restart the installer with the /LANG switch to load the selected language.

Make sure you disable the standard language dialog by setting the ShowLanguageDialog to no.

[Setup]
ShowLanguageDialog=no

[Files]
Source: "skin.vsf"; Flags: dontcopy
Source: "VclStylesInno.dll"; Flags: dontcopy

[Languages]
Name: "en"; MessagesFile: "compiler:Default.isl"
Name: "cs"; MessagesFile: "compiler:Languages\Czech.isl"
[Code]

procedure LoadVCLStyle(VClStyleFile: String);
  external 'LoadVCLStyleW@files:VclStylesInno.dll stdcall setuponly';
procedure UnLoadVCLStyles;
  external 'UnLoadVCLStyles@files:VclStylesInno.dll stdcall setuponly';

function ShellExecute(hwnd: HWND; lpOperation: string; lpFile: string;
  lpParameters: string; lpDirectory: string; nShowCmd: Integer): THandle;
  external '[email protected] stdcall';
  
procedure SelectLanguage();
var
  LanguageForm: TSetupForm;
  CancelButton: TNewButton;
  OKButton: TNewButton;
  LangCombo: TNewComboBox;
  SelectLabel: TNewStaticText;
  Languages: TStrings;
  Params: string;
  Instance: THandle;
  P, I: Integer;
  S, L: string;
begin
  Languages := TStringList.Create();

  Languages.Add('en=English');
  Languages.Add('cs='+#$010C+'e'+#$0161+'tina');

  LanguageForm := CreateCustomForm;

  LanguageForm.Caption := SetupMessage(msgSelectLanguageTitle);
  LanguageForm.ClientWidth := ScaleX(297);
  LanguageForm.ClientHeight := ScaleY(125);
  LanguageForm.BorderStyle := bsDialog;
#if Ver < 0x06000000
  LanguageForm.Center;
#endif

  CancelButton := TNewButton.Create(LanguageForm);
  CancelButton.Parent := LanguageForm;
  CancelButton.Top := ScaleY(93);
  CancelButton.Width := ScaleY(75);
  CancelButton.Left := LanguageForm.ClientWidth - CancelButton.Width - ScaleX(16);
  CancelButton.Height := ScaleY(23);
  CancelButton.TabOrder := 3;
  CancelButton.ModalResult := mrCancel;
  CancelButton.Caption := SetupMessage(msgButtonCancel);

  OKButton := TNewButton.Create(LanguageForm);
  OKButton.Parent := LanguageForm;
  OKButton.Top := CancelButton.Top;
  OKButton.Width := CancelButton.Width;
  OKButton.Left := CancelButton.Left - OKButton.Width - ScaleX(8);
  OKButton.Height := CancelButton.Height;
  OKButton.Caption := SetupMessage(msgButtonOK);
  OKButton.Default := True
  OKButton.ModalResult := mrOK;
  OKButton.TabOrder := 2;

  LangCombo := TNewComboBox.Create(LanguageForm);
  LangCombo.Parent := LanguageForm;
  LangCombo.Left := ScaleX(16);
  LangCombo.Top := ScaleY(56);
  LangCombo.Width := LanguageForm.ClientWidth - ScaleX(16) * 2;
  LangCombo.Height := ScaleY(21);
  LangCombo.Style := csDropDownList;
  LangCombo.DropDownCount := 16;
  LangCombo.TabOrder := 1;

  SelectLabel := TNewStaticText.Create(LanguageForm);
  SelectLabel.Parent := LanguageForm;
  SelectLabel.Left := LangCombo.Left;
  SelectLabel.Top := ScaleY(8);
  SelectLabel.Width := LangCombo.Width;
  SelectLabel.Height := ScaleY(39);
  SelectLabel.AutoSize := False
  SelectLabel.Caption := SetupMessage(msgSelectLanguageLabel);
  SelectLabel.TabOrder := 0;
  SelectLabel.WordWrap := True;

  for I := 0 to Languages.Count - 1 do
  begin
    P := Pos('=', Languages.Strings[I]);
    L := Copy(Languages.Strings[I], 0, P - 1);
    S := Copy(Languages.Strings[I], P + 1, Length(Languages.Strings[I]) - P);
    LangCombo.Items.Add(S);
    if L = ActiveLanguage then
      LangCombo.ItemIndex := I;
  end;

  // Restart the installer with the selected language
  if LanguageForm.ShowModal = mrOK then
  begin
    // Collect current instance parameters
    for I := 1 to ParamCount do
    begin
      S := ParamStr(I);
      // Unique log file name for the elevated instance
      if CompareText(Copy(S, 1, 5), '/LOG=') = 0 then
      begin
        S := S + '-localized';
      end;
      // /SL5 switch is an internal switch used to pass data
      // from the master Inno Setup process to the child process.
      // As we are starting a new master process, we have to remove it.
      // This should not be needed since Inno Setup 6.2,
      // see https://groups.google.com/g/innosetup/c/pDSbgD8nbxI
      if CompareText(Copy(S, 1, 5), '/SL5=') <> 0 then
      begin
        Params := Params + AddQuotes(S) + ' ';
      end;
    end;

    L := Languages.Strings[LangCombo.ItemIndex];
    P := Pos('=', L);
    L := Copy(L, 0, P-1);

    // ... and add selected language
    Params := Params + '/LANG=' + L;

    Instance :=
      ShellExecute(0, '', ExpandConstant('{srcexe}'), Params, '', SW_SHOW);
    if Instance <= 32 then
    begin
      S := 'Running installer with the selected language failed. Code: %d';
      MsgBox(Format(S, [Instance]), mbError, MB_OK);
    end;
  end;
end;

function InitializeSetup(): Boolean;
var
  Language: string;
begin
  ExtractTemporaryFile('skin.vsf');
  LoadVCLStyle(ExpandConstant('{tmp}\skin.vsf'));

  Result := True;

  if WizardSilent then
  begin
    Log('Silent installation, keeping the default language');
  end
    else
  begin
    Language := ExpandConstant('{param:LANG}');
    if Language = '' then
    begin
      Log('No language specified, showing language dialog');
      SelectLanguage();
      Result := False;
      Exit;
    end
      else
    begin
      Log('Language specified, proceeding with installation');
    end;
  end;
end;

procedure DeinitializeSetup();
begin
  UnLoadVCLStyles;
end;

Select Setup Language dialog


Installer re-launch code is based on Make Inno Setup installer request privileges elevation only when needed.


Note that the code probably needs tweaking, if you want to allow parent process to wait for the installation to complete. Though in that case you are probably going to use the silent installation, what the code can handle correctly.


This hackish solution causes problems with a debugger: Debugging Inno Setup installer that respawns itself.

Primary answered 7/12, 2016 at 15:38 Comment(0)
F
0

You need to recompile Inno Setup from source.

Find this code in Main.pas file and move this section after CodeRunner and InitializeSetup.

{ Show "Select Language" dialog if necessary }
  if ShowLanguageDialog and (Entries[seLanguage].Count > 1) and
     not InitSilent and not InitVerySilent then begin
    if not AskForLanguage then
      Abort;
  end;

enter image description here

Filiform answered 26/6, 2017 at 21:10 Comment(1)
OK, but you should also mention side effects of this change. There's a reason why AskForLanguage is called so early. There's a lot of code with UI between AskForLanguage dialog and InitializeSetup. If you swap them all that code will use English.Primary

© 2022 - 2024 — McMap. All rights reserved.