Inno Setup - 带有 VCL 样式的语言选择器
Inno Setup - Language selector with VCL Styles
有没有办法在 VCL 样式中使用语言选择器 (Inno Setup)?怎么样?
在调用 InitializeSetup
event function 之前显示“Select 安装语言”对话框。所以你不能加载对话框的皮肤。
作为解决方法,您可以实现自己的“语言”对话框,并从 InitializeSetup
中显示该对话框。这样自定义对话框将被换肤。用户选择语言后,您可以使用 /LANG
switch 重新启动安装程序以加载所选语言。
确保通过将 ShowLanguageDialog
设置为 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='+#0C+'e'+#61+'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;
安装程序重新启动代码基于 Make Inno Setup installer request privileges elevation only when needed。
请注意,如果您想让父进程等待安装完成,代码可能需要调整。虽然在那种情况下您可能会使用静默安装,但代码可以正确处理。
这种骇人听闻的解决方案会导致调试器出现问题:Debugging Inno Setup installer that respawns itself。
您需要从 source 重新编译 Inno Setup。
在 Main.pas
文件中找到此代码并将此部分移到 CodeRunner
和 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;
有没有办法在 VCL 样式中使用语言选择器 (Inno Setup)?怎么样?
在调用 InitializeSetup
event function 之前显示“Select 安装语言”对话框。所以你不能加载对话框的皮肤。
作为解决方法,您可以实现自己的“语言”对话框,并从 InitializeSetup
中显示该对话框。这样自定义对话框将被换肤。用户选择语言后,您可以使用 /LANG
switch 重新启动安装程序以加载所选语言。
确保通过将 ShowLanguageDialog
设置为 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='+#0C+'e'+#61+'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;
安装程序重新启动代码基于 Make Inno Setup installer request privileges elevation only when needed。
请注意,如果您想让父进程等待安装完成,代码可能需要调整。虽然在那种情况下您可能会使用静默安装,但代码可以正确处理。
这种骇人听闻的解决方案会导致调试器出现问题:Debugging Inno Setup installer that respawns itself。
您需要从 source 重新编译 Inno Setup。
在 Main.pas
文件中找到此代码并将此部分移到 CodeRunner
和 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;