Inno Setup desktop shortcut (link) which has "Run as administrator" advanced property set - inno-setup

I am struggling to get Inno setup (5.5.9u) to created a desktop shortcut that has an icon and has the advanced property of "Run as administrator" set.
Issue
This question, is a little different than: How to set 'Run as administrator' on a file using Inno Setup
Since what I am trying to do is not run a program at setup time with admin rights, (setup is already running at Admin), but rather leave a link on the desktop that has has the advanced property of "Run as Administrator".
Code Sample
[Icons]
Name: "{group}\EGPL Watson Uninstall"; Filename: "{uninstallexe}"; \
WorkingDir: "{app}"
Name: "{commondesktop}\DashBoard"; \
Filename: "{app}\dashboard\node_modules\electron\dist\electron.exe main.js"; \
WorkingDir: "{app}\dashboard"; \
IconFilename: "{src}\dashboard\build\configure.ico"

First, make sure you have a very good reason to run your application with Administrator privileges. User applications should not need Administrator privileges. If they need it, it's usually a sign of a bad design. One common (bad) reason to want an application to run with Administrator privileges, is that the application needs to write to its installation folder.
See Application does not work when installed with Inno Setup
Inno Setup does not natively support creating a shortcut with "Run as Administrator" flag set.
The "Run as Administrator" flag is a bit the .lnk file. See:
LinkFlags in [MS-SHLLINK]: Shell Link (.LNK) Binary File Format;
How to create a Run As Administrator shortcut using Powershell
How can I use JScript to create a shortcut that uses "Run as Administrator"
You can set the bit using the following code:
[Icons]
Name: "{userdesktop}\My Program"; Filename: "{app}\MyProg.exe"; \
AfterInstall: SetElevationBit('{userdesktop}\My Program.lnk')
[Code]
procedure SetElevationBit(Filename: string);
var
Buffer: string;
Stream: TStream;
begin
Filename := ExpandConstant(Filename);
Log('Setting elevation bit for ' + Filename);
Stream := TFileStream.Create(FileName, fmOpenReadWrite);
try
Stream.Seek(21, soFromBeginning);
SetLength(Buffer, 1);
Stream.ReadBuffer(Buffer, 1);
Buffer[1] := Chr(Ord(Buffer[1]) or $20);
Stream.Seek(-1, soFromCurrent);
Stream.WriteBuffer(Buffer, 1);
finally
Stream.Free;
end;
end;
Tested on Unicode version of Inno Setup (the only version as of Inno Setup 6). But it should, even more naturally, work on Ansi version too.

Related

Inno setup can not create desktop icon in windows 10

I have setup file for inno setup of latest version. It compiles and works great from windows xp to windows 8, but in windows 10 it fails on the moment when it creates desktop icon with next error:
IPersistFile::Save failed; code 0x80070002
This is how I create icon in setup file:
[Icons]
Name: "{userdesktop}\Forex Tester 4"; Filename: "{app}\ForexTester4.exe"; Tasks: desktopicon
Part of the installation log file:
2019-02-01 12:50:46.376 -- Icon entry --
2019-02-01 12:50:46.376 Dest filename: C:\Users\Mike\Desktop\Forex Tester 4.lnk
2019-02-01 12:50:46.376 Creating the icon.
2019-02-01 12:50:46.376 Exception message:
2019-02-01 12:50:46.376 Message box (OK):
IPersistFile::Save failed; code 0x80070002.
The system cannot find the file specified.
2019-02-01 12:50:59.066 User chose OK.
This folder exists and I can create files there manually. But inno setup fails to do this... All other icons except desktop one were created without problems.
Any ideas?
I had the same error on Windows 7 and Windows 10 because I was trying to create shortcut to file that didn't exist yet.
[Icons]
; Create icons for the app
Name: "{group}\{#AppName}"; \
Filename: "{app}\{#AppName}.lnk"; \
BeforeInstall: CreateAppRunLink();
Name: "{commondesktop}\{#AppName}"; \
Filename: "{app}\{#AppName}.lnk"; \
Tasks: desktopicon;
So I had to make sure that file "{app}{#AppName}.lnk" exists prior to creating the Icon:
This goes to [Code] section:
procedure CreateAppRunLink();
var
Filename: string;
Description: string;
ShortcutTo: string;
Parameters: string;
WorkingDir: string;
IconFilename: string;
begin
Filename := ExpandConstant('{app}\MyApp.lnk');
Description := 'Description';
ShortcutTo := 'Full path to file that will be run (MyApp.exe)';
Parameters := 'parameters if any';
WorkingDir := ExpandConstant('{app}');
IconFilename := ExpandConstant('{app}') + '\icon.ico';
CreateShellLink(Filename, Description, ShortcutTo, Parameters, WorkingDir,
IconFilename, 0, SW_HIDE);
end;
CreateAppRunLink will will be called after extracting any files from [Files] section which will make sure that our file is in place.
Hope that'll help.
It could be a relatively new (since version 1709) Windows 10 feature called Controlled folder access. See Allow a blocked app in Windows Security for instructions on turning it on or off.

Installing MS SQL Server Express 2017 with Inno Installer

I'm desperately trying to install SQL Server Express 2017 with Inno Installer.
Within my installer I include the extracted installer files.
That means that I already executed the common SQLEXPR_x64_ENU.exe, to avoid the "extract-temp-folder" prompt while my installer is running.
I execute the following on the cmd:
{somePath}\SQLEXPR_x64_ENU\setup.exe /ACTION=Install /Q /SKIPRULES=RebootRequiredCheck /SUPPRESSPRIVACYSTATEMENTNOTICE=1 /IAcceptSQLServerLicenseTerms=1 /SECURITYMODE=SQL /SAPWD=secretPW /ConfigurationFile=ConfigurationFileExpr.ini
The install succeeds.
But when I do the same within my InnoInstaller-File like this:
...
[Files]
Source: "SQLEXPR_x64_ENU\*"; DestDir: "{tmp}\SQLEXPR_x64_ENU"; Check: not SQLExpress_Check; Flags: recursesubdirs;
[Run]
Filename: "{tmp}\SQLEXPR_x64_ENU\setup.exe"; Description: "Installing SQL Server Express 2017..."; StatusMsg: "Installing SQL Server Express 2017..."; \
Parameters: "/ACTION=Install /Q /SKIPRULES=RebootRequiredCheck /SUPPRESSPRIVACYSTATEMENTNOTICE=1 /IAcceptSQLServerLicenseTerms=1 /SECURITYMODE=SQL /SAPWD=secretPW /ConfigurationFile=ConfigurationFileExpr.ini"; Check: not SQLExpress_Check; Flags: runascurrentuser;
...
SQL Installer fails with the following error:
Exception type: System.MissingMethodException
Message:
Method not found: 'Void Microsoft.SqlServer.Chainer.Infrastructure.RoleService.Initialize(Microsoft.SQL.Chainer.Product.RolesType)'.
HResult : 0x80131513
Data:
DisableWatson = true
Stack:
at Microsoft.SqlServer.Configuration.BootstrapExtension.InitializeRoleServiceAction.ExecuteAction(String actionId)
at Microsoft.SqlServer.Chainer.Infrastructure.Action.Execute(String actionId, TextWriter errorStream)
at Microsoft.SqlServer.Setup.Chainer.Workflow.ActionInvocation.<>c__DisplayClasse.<ExecuteActionWithRetryHelper>b__b()
at Microsoft.SqlServer.Setup.Chainer.Workflow.ActionInvocation.ExecuteActionHelper(ActionWorker workerDelegate)
Is this a permission error?
I do not have a clue.
On cmd-shell it works, but not on InnoInstaller.
Thanks in advance for your efforts and have a nice day.
Solution for me was provided by Gavin Lambert on the Inno Setup Forum :
If you're [installing from the directory of unpacked files], you need to use {sd}\shortname as the DestDir (usually combined with deleteafterinstall) -- you can't put the files in {tmp} or any similar path as the files are very deeply nested and the db installer ends up failing to access some files because the path is too long.
If you use an unpacked installer file, here is what should work absolutely perfect.
SQLEXPR_x64_ENU.exe /x:%temp%\SQLEXPR_x64_ENU\ /QS /ACTION=Install /SKIPRULES=RebootRequiredCheck /SUPPRESSPRIVACYSTATEMENTNOTICE=1 /IAcceptSQLServerLicenseTerms=1 /SECURITYMODE=SQL /SAPWD=secretPW /ConfigurationFile=ConfigurationFileExpr.ini
In the above command, /x:%temp%\SQLEXPR_x64_ENU\ is the very important switch where it describes the extraction location and with combination to /QS it will show you the progress on screen but will not ask for any input.
You may have to change %temp% to appropriate command to grab a windows temporary folder in your installer. The command I have posted is good for command-line execution.
Enjoy! :)

Inno Setup - Register components as an administrator

Based on the excellent Excel add-in installer (Daniel's XL Toolbox), I have built a setup file that among other things needs to register some ActiveX's
[Files]
; The include file makes adds all .XLA and .XLAM files contained in the
; SOURCEDIR to the project.
Source: "c:\source\path\MSCOMCTL.OCX"; \
DestDir: "\users\public\EzPasteFiles"; Flags: regserver
Source: "c:\source\path\DAS_AX_Knob.dll"; \
DestDir: "\users\public\EzPasteFiles"; Flags: regserver
Source: "c:\source\path\GIF89.DLL"; \
DestDir: "\users\public\EzPasteFiles"; Flags: regserver
I need the addin to install, then before starting to register the files a check is done about administrator rights and if the user has none, a message is displayed asking for entering the admin password so that registration can take place. I am aware that it can be done at the beginning of the setup, but then the addin will not be activated, if it is a standard user account. The addin needs registered components, a standard user can't install it properly.
I am looking for something like this to fire before the registration starts:
MyProgChecked := not(IsAdminLoggedOn or IsPowerUserLoggedOn);
if MyProgChecked = True then
begin
MsgBox(
'Kindly notice:' #13#13
'It seems as you are not looged as an administrator' #13#13
'Please abort and reinstall EzPaste AS an administrator' #13#13
'(To install As an Adminstrator, just save the exe setup anywhere then Right Click on it to get to this feature or ask your IT administrator for proper directives)',
mbConfirmation, MB_OK);
{ Popup message asking for Pwd }
ExitProcess(0);
end;
I am naturally open for any other approach
I'll be glad also to understand how a domain user (Windows server) without admin rights should proceed to install the addin.
You can execute regsvr32.exe "as administrator", this way:
[Files]
Source: "MyDll.dll"; DestDir: "{app}"; AfterInstall: RegMyDll
[Code]
procedure RegMyDll;
var
Path: string;
RegSvr: string;
Params: string;
Registered: Boolean;
ErrorCode: Integer;
begin
Path := ExpandConstant(CurrentFilename);
RegSvr := 'regsvr32.exe';
Params := Format('/s "%s"', [Path]);
Log(Format('Registering %s using "%s" %s', [Path, RegSvr, Params]));
Registered :=
ShellExec('runas', RegSvr, Params, '', SW_HIDE, ewWaitUntilTerminated, ErrorCode);
if Registered and (ErrorCode = 0) then
begin
Log(Format('Registered %s', [Path]));
end
else
begin
MsgBox(Format('Registering %s failed with code %d', [Path, ErrorCode]), mbError, MB_OK);
end;
end;
Alternative implementation is to create subinstaller for the registration only that will require Administrator privileges.
For a similar example, see Inno Setup - Access unprivileged account folders from installer that requires privileges.
Or use an opposite approach. Require administrator privileges using
[Setup]
PrivilegesRequired=admin
(which is default)
But deploy files to the original user folder.
See my answer to Inno Setup always installs into admin's AppData directory.

Adding to installer a form to choose a path where JDK is installed then changing environment variable

Update:
I have succeeded in making my installer work properly. ( added the release notes, license agreement section, choosing the location where the program is to be installed, having it generate me an destop shortcup etc.) what i want to add it, that during the instalation, I want the user to choose the path to where he has installed his JDK. I want to take this path, create a system environment variable with the name JAVA_HOME and have it's value be this chosen path. How do i do this? It is kind off diifficult to show my code because i am on my phone. But if i have to i will make an effort to do so.
Based on
How to set a global environment variable from Inno Setup installer?
Inno Setup FAQ Setting Environment Variables
[Setup]
ChangesEnvironment=yes
[Registry]
Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; \
ValueType: string; ValueName: "JAVA_HOME"; ValueData:"{code:GetJavaHome}"; \
Flags: preservestringtype
[Code]
var
JavaHomePage: TInputDirWizardPage;
procedure InitializeWizard();
begin
JavaHomePage :=
CreateInputDirPage(
wpSelectDir, 'Java Path', 'Where do you have Java installed to?', '', False, '');
JavaHomePage.Add('');
end;
function GetJavaHome(Param: string): string;
begin
Result := JavaHomePage.Values[0];
end;
If you want to offer some meaningful default, see also an answer by #Matthieu to Inno Setup - Setting Java Environment Variable.

PowerShell Script after install

Newbie question: I would like to run a powershell script (.ps1) at the end of the inno-setup install. Can anyone give me a tip on where to put this? I want the user prompted to be asked if he wants to run this script.
Oh yes, what this script does is run netsh.exe to open up a port, the script is clever and it grabs Env:username and Env:userdomain from the current context. Would the context be the admin who is running the setup? or would it be the original user that ran the setup.exe?
Another way is to run the script using the ShellExec from the code.
[Files]
Source: "yourPowershell.ps1"; DestDir: "{app}"; Flags: overwritereadonly replacesameversion promptifolder;
[Tasks]
Name: "runpowershell"; Description: "Do you want to run Powershell script?"
[Code]
procedure CurStepChanged(CurStep: TSetupStep);
var
ErrorCode: Integer;
ReturnCode: Boolean;
begin
if CurStep = ssPostInstall then begin
if(IsTaskSelected('runpowershell')) then begin
ExtractTemporaryFile('yourPowershell.ps1');
ReturnCode := ShellExec('open', '"PowerShell"', ExpandConstant(' -ExecutionPolicy Bypass -NoLogo -NonInteractive -NoProfile -WindowStyle Hidden -File "{tmp}\YourPowershell.ps1"'), '', SW_SHOWNORMAL, ewWaitUntilTerminated, ErrorCode);
if (ReturnCode = False) then
MsgBox('Message about problem. Error code: ' + IntToStr(ErrorCode) + ' ' + SysErrorMessage(ErrorCode), mbInformation, MB_OK);
end;
end;
[Run]
.....; Description: Run Script; Flags: postinstall
(See the help for more details.) By default this will display a checkbox and run under the original user's context (although it depends a bit on how the installer is run).
You might want to reconsider this approach, though; if you are performing a machine-wide install then you should probably open the port machine-wide too. You can do this with pure Inno code calling WinAPIs -- no powershell required. (Which is a good thing, because it might not be installed.)
Alternatively if you want to keep it a per-user setting you should consider making your application prompt the user for a decision on first run. After all, why give an option to only one of the many possible users of your app?

Resources