Is there any way to use language selector (Inno Setup) with VCL Styles? How?
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 'ShellExecuteW@shell32.dll 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. }
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;
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.
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;