Apologies if my code is terrible. I am running into this error message:
The installer currently starts as a non-admin user then kills and restarts itself as an admin user right before files start being copied. The error message only comes up when 'Register' install is selected.
This occurs if I enter any top-level drive in the install location. If I enter a folder on a network share, I'll get a different error (another privilege issue) and if I input a folder on the C:\ drive (as in C:\xyz) then it works fine.
#define MyAppName "O"
#define MyAppVersion "0.0"
[Setup]
; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={{9BB6F4FD-5530-4043-BD9E-A405BAEDDDFF}}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
DefaultDirName={sd}\{#MyAppName}
DisableDirPage=yes
AllowNoIcons=yes
OutputDir={desktop}
OutputBaseFilename=dummy
Compression=lzma
SolidCompression=yes
PrivilegesRequired=lowest
[Types]
Name: "server"; Description: "Odin Server Install"
Name: "client"; Description: "Odin Register/Workstation Install"
[Components]
Name: "data_shared_exe"; Description: "Core apps installed"; Types: server;
Name: "C_Odin"; Description: "Database stuff"; Types: server client;
Name: "shortcuts"; Description: "Shortcuts to Odin apps"; Types: server client;
[Code]
var
SelectOdinSharePage: TInputDirWizardPage;
SelectOdinInstallPage: TInputDirWizardPage;
Elevated: Boolean;
InstallType: Integer;
DirParam: string;
type
HINSTANCE = THandle;
// Privilege Escalation code
// Inspired by/Courtesy of http://stackoverflow.com/questions/21556853/make-inno-setup-installer-request-privileges-elevation-only-when-needed
procedure ExitProcess(uExitCode: UINT);
external 'ExitProcess@kernel32.dll stdcall';
function ShellExecute(hwnd: HWND; lpOperation: string; lpFile: string;
lpParameters: string; lpDirectory: string; nShowCmd: Integer): HINSTANCE;
external 'ShellExecuteW@shell32.dll stdcall';
function IsServerInstall(): Boolean;
begin
Result := (InstallType=0)
end;
function CmdLineParamExists(const Value: string): Boolean;
var
I: Integer;
begin
Result := False;
for I := 1 to ParamCount do
if CompareText(ParamStr(I), Value) = 0 then
begin
Result := True;
Exit;
end;
end;
function CmdLineParamReadInt(const Value: string): integer;
var
I: Integer;
begin
Result := -1;
for I := 1 to ParamCount do
if Pos(Value, ParamStr(I)) = 1 then
begin
Result := StrToInt(Copy(ParamStr(I), Length(Value)+2, 1));
//MsgBox(Format('ReadInt: %s', [Copy(ParamStr(I), Length(Value)+2, 1)]), mbInformation, MB_OK);
Exit;
end;
end;
function CmdLineParamReadStr(const Value: string): string;
var
I: Integer;
begin
Result := '';
for I := 1 to ParamCount do
if Pos(Value, ParamStr(I)) = 1 then
begin
Result := Copy(ParamStr(I), Length(Value)+2, Length(ParamStr(I))-Length(Value)-1);
//MsgBox(Format('ReadStr: %s',[Result]), mbInformation, MB_OK)
Exit;
end;
end;
procedure OnTypeChange(Sender: TObject);
begin
// set the item index in hidden TypesCombo
WizardForm.TypesCombo.ItemIndex := TNewRadioButton(Sender).Tag;
InstallType := TNewRadioButton(Sender).Tag;
// notify TypesCombo about the selection change
WizardForm.TypesCombo.OnChange(nil);
end;
procedure InitializeWizard();
var
I: Integer;
RadioButton: TNewRadioButton;
begin
// By default, InstallType is 1 ('Register')
InstallType := 1;
// Privilege escalation parameters
Elevated := CmdLineParamExists('/ELEVATE');
if Elevated then
begin
InstallType := CmdLineParamReadInt('/INSTALLTYPE');
DirParam := CmdLineParamReadStr('/DIR');
//MsgBox(Format('DirParam: %s', [DirParam]), mbInformation, MB_OK);
end;
for I := 0 to WizardForm.TypesCombo.Items.Count - 1 do
begin
// create radio button and set the basic properties
RadioButton := TNewRadioButton.Create(WizardForm);
RadioButton.Parent := WizardForm.SelectComponentsPage;
RadioButton.Left := WizardForm.TypesCombo.Left;
RadioButton.Top := WizardForm.TypesCombo.Top + I * RadioButton.Height;
RadioButton.Width := WizardForm.TypesCombo.Width;
// the Tag property substitutes the index property
RadioButton.Tag := I;
RadioButton.TabOrder := I;
RadioButton.OnClick := @OnTypeChange;
// check just the first item
RadioButton.Checked := I = InstallType;
RadioButton.Caption := WizardForm.TypesCombo.Items[I];
end;
// hide the TypesCombo combo box
WizardForm.TypesCombo.Visible := False;
SelectOdinSharePage := CreateInputDirPage(wpSelectComponents,
'Select Odin Exe''s directory', 'Where are the Odin exe''s located?',
'The Odin exe''s are usually located on the Odin Network Share under "exe" or "exes". ' +
'Once you have found this directory, click Next. If you would like to select a different folder, click Browse.',
False, 'New Folder');
SelectOdinSharePage.Add('');
SelectOdinSharePage.Values[0] := 'O:';
SelectOdinInstallPage := CreateInputDirPage(wpSelectComponents,
'Select Odin Install directory', 'Select where to install Odin core apps',
'To continue, click Next. If you would like to select a different folder, click Browse.',
False, 'New Folder');
SelectOdinInstallPage.Add('');
SelectOdinInstallPage.Values[0] := 'C:\Program Files (x86)\Odin';
if Elevated then
begin
if IsServerInstall() then SelectOdinInstallPage.Values[0] := DirParam;
if not IsServerInstall() then SelectOdinSharePage.Values[0] := DirParam;
end;
end;
function ShouldSkipPage(PageID: Integer): Boolean;
begin
Result := False;
if ((PageID=100) and IsServerInstall()) then Result := True;
if ((PageID=101) and not IsServerInstall()) then Result := True;
if Elevated then
begin
if IsServerInstall() then
begin
if ((PageID=7) or (PageID=101)) then Result := True;
end;
if InstallType=1 then
begin
if ((PageID=7) or (PageID=100)) then Result := True;
end;
end;
end;
function NextButtonClick(CurPageID: Integer): Boolean;
var
Params: string;
RetVal: HINSTANCE;
begin
Result := True;
//MsgBox(Format('Is Server Install: %d', [WizardForm.TypesCombo.ItemIndex]), mbInformation, MB_OK);
if (CurPageID=101) and IsServerInstall() then
begin
Params := ExpandConstant('/DIR="' + SelectOdinInstallPage.Values[0] + '" /ELEVATE /INSTALLTYPE=0');
RetVal := ShellExecute(WizardForm.Handle, 'runas',
ExpandConstant('{srcexe}'), Params, '', SW_SHOW);
if RetVal > 32 then
begin
ExitProcess(0);
end
else
MsgBox('Administrative privilege escalation failed. Install aborted. Contact Odin Support.', mbInformation, MB_OK);
ExitProcess(0);
end;
if (CurPageID=100) and (not IsServerInstall()) then
begin
Params := ExpandConstant('/DIR="' + SelectOdinSharePage.Values[0] + '" /ELEVATE /INSTALLTYPE=1');
RetVal := ShellExecute(WizardForm.Handle, 'runas',
ExpandConstant('{srcexe}'), Params, '', SW_SHOW);
if RetVal > 32 then
begin
ExitProcess(0);
end
else
MsgBox('Administrative privilege escalation failed. Install aborted. Contact Odin Support.', mbInformation, MB_OK);
ExitProcess(0);
end;
end;
As it turns out, the problem lies in the lines
In this section of code, the launcher is re-launched with administrative privileges. IN THE PARAMS SECTION, the '/DIR' option is an Inno Setup option, which overrides the default InstallDirectory. This needs to be set to something that is not a top-level drive (i.e., not 'C:\' or 'O:\', but 'C:\xyz').
As long as that is changed, the above program stub will work fine.