I'm trying to figure out how to run a custom action from an elevated WiX installation setup program. The msi file generated by WiX is executed by DotNetInstaller.
In WiX I have a Package section with InstallScope set to perMachine and this custom action:
<CustomAction Id='RegisterPlugin' FileKey='RegisterPluginExe' ExeCommand='' Return='asyncNoWait' Impersonate='yes' />
<InstallExecuteSequence>
<Custom Action='RegisterPlugin' After='InstallFinalize'>NOT Installed</Custom>
</InstallExecuteSequence>
Impersonate set to yes should run the action exe as the user who run the DNI bootstrapper.
The custom action should read a file in the user AppData directory, using the value obtained by System.Windows.Forms.Application.LocalUserAppDataPath, but the directory used is wrong because is the one of the Administrator user.
I tried to use a manifest to run DNI unelevated, but the problem remains. If I run the msi directly, it correctly does some registry operations which require administration rights, while the custom action runs as expected.
Edit:
Looking at the task manager I can see that, if the msi is executed from DNI, the user becomes Administrator when the msi is started. If I run the msi directly, the user is always the same, even during the files installation on the system directory. It seems that in this case what change is not the identity, but the privileges.
Any ideas?
Thanks,
Stenio
Related
I created a Setup with WiX Installer for my C# application.
I use a Class CustomAction to install a SQL data base and setting rights to a folder.
There is my Class :
public class CustomActions
{
[CustomAction]
public static ActionResult AllowAppData(Session session){...}
[CustomAction]
public static ActionResult Install_SetupDataBase(Session session){...}
}
And the product.wxs :
<Binary Id="CASetup.dll" SourceFile="$(var.SetupCustomAction.TargetDir)$(var.SetupCustomAction.TargetName).CA.dll" />
<CustomAction Id="CustomActionSetupAllow" BinaryKey="CASetup.dll" DllEntry="AllowAppData" Execute="immediate" />
<CustomAction Id="CustomActionSetupBase" BinaryKey="CASetup.dll" DllEntry="Install_SetupDataBase" Execute="immediate" />
<InstallExecuteSequence>
<Custom Action='CustomActionSetupAllow' After='InstallFinalize' />
<Custom Action='CustomActionSetupBase' After='InstallFinalize' />
</InstallExecuteSequence>
The build is ok but when the Install perform I have the message :
I've try with only CustomActionSetupAllow and it's works fine. But with CustomActionSetupBase I got the message.
How can I know what DLL are missing ? And where should I add a reference ?
The error "could not be run" doesn't mean it's missing. It's in the Binary table of the MSI file and will be streamed out onto disk to run. The error means that it doesn't load and run, or is crashing. It's possible that there are missing dependent Dlls.
One potentially serious problem is that you've marked the custom action (CA) to be immediate. That means it runs before anything has been installed, such as perhaps the empty database you might be populating, as well as any dependent Dlls it might need. Apart from that, if you create the SQL DB with an immediate CA and the install subsequently fails so you'll have a DB but no installed product. It should be a deferred custom action, ideally with a rollback CA to remove the DB so the user can run the install again and you don't populate the same DB again, if that's an issue.
There are other areas you may want to look at. An immediate CA runs with the installing user's credential and not elevated, so if the code requires elevation it will fail. A deferred CA will run elevated with the system account, so access to a SQL DB with the system account might be an issue. Code in a CA needs to be very defensive because it's not the same environment as an interactive test. For these reasons it's often better to do this with a startup program that runs in a true interactive environment when the app first runs.
I have an installer with InstallPrivileges="elevated". I include inside the file table an app.EXE
<Component Id="myapp" Guid="*">
<File Id="myapp" Source="myapp.exe"/>
</Component>
using these CA to run it in an elevcated state:
<CustomAction Id="SetProp" Property="Launch" Value =""[INSTALLDIR]myapp.exe""/>
<CustomAction Id="Launch" BinaryKey="WixCA" DllEntry="CAQuietExec64" Execute="deferred" Return="ignore" Impersonate="no"/>
scheduled like this:
<Custom Action="SetProp" Before="Launch">NOT Installed</Custom>
<Custom Action="Launch" Before="InstallFinalize">NOT Installed</Custom>
but according to Uachelper class (c#) and the missing result this process is not elevated BUT verbose log and myapp.exe log shows no error.
I also tried to use a type 2 custom action (binarykey etc.), a type 18 custom action (filekey etc.) and to include an app.manifest into myapp.exe with requiredadministrator.
nothing worked to elevate myapp.exe.. during installation.
Workaround I found is to start myapp.exe manually after installation completed (go to [INSTALLDIR] and doubleclick myapp.exe, will prompt uac etc.) but I want to avoid that.
I also thought about trying to use runas but am not sure if this would work or if this is feasible.
All of this is based on http://wixtoolset.org/documentation/manual/v3/customactions/qtexec.html , https://stackoverflow.com/a/10028939/4096653 and many more questions here on SO.
What am I missing or what else can I try?
By default a custom action will be elevated only if it is deferred, not impersonated, and sequenced between InstallInitialize and InstallFinalize, and if it's a per machine install, and then it will run elevated with the system account. Whether it will actually work or not depends on whether the code is prepared to deal with being aware that it's not running as the interactive user with access to HKCU, user profile items like PersonalFolder, and the code knows where its data is. When a custom action executable gets fired off it is via the CreateProcess() type of call (NOT a shell execute call) and so there is no UAC dialog/manifest checking stuff going on. So your WiX posted looks correct if you are deferred and before InstallFinalize. I have no idea what a UACHelper class thinks if are running as system and elevated. ProcessMonitor/Explorer might tell you what your rights/tokens are.
It should work if you create an impersonated launcher custom action that does an explicit Shell Execute of your exe, because that behaves like a run from Explorer by the interactive user. It will run the app which will then ask for elevation as long as the user doing the install is admin. In code that's something like ProcessStartInfo.UseShellExecute=true, ShellExecute() Win32.
If you need an impersonated CA to be elevated the MSI needs to be running as administrator or started from a program with an elevation manifest that then runs the MSI.
You'll see lots of advice (I hope) saying you should arrange to run this automatically the first time your app runs, just have it launched and remember whether that it has been run. Running as elevated user from an MSI is too complicated unless you have an elevated launching bootstrapper.
I assume you want this to run in Execute sequence and have scheduled your custom actions in InstallExecuteSequence
It might be so that your app is not able to run as Local system user so try to switch the Impersonate to yes Impersonate="Yes"
I am using the script from this answer https://stackoverflow.com/a/1681410/22 to insert a launch application checkbox at the end of the MSI installer.
Everything builds ok and I get the launch checkbox just fine, however the application does not launch when the installer is complete.
Not sure if this is the cause but my app does require admin (app.manifest)
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
Installer Build Output:
------ Starting pre-build validation for project 'MyAppInstaller' ------
------ Pre-build validation for project 'MyAppInstaller' completed ------
------ Build started: Project: MyAppInstaller, Configuration: Release ------
Building file 'C:\path\to\MyAppInstaller.msi'...
Packaging file 'MyApp.exe'...
Packaging file 'Icon.ico'...
Starting post-build events...
Microsoft (R) Windows Script Host Version 5.8
Copyright (C) Microsoft Corporation. All rights reserved.
Updating the Control table...
Updating the ControlEvent table...
Updating the CustomAction table...
Updating the Property table...
Done Adding Additional Store
Successfully signed: MyAppInstaller.msi
Edit:
If I right click the setup project in Visual Studio and select "Install". The app runs when the installer closes.
However, if I just double click the generated MSI. The app will not open after the MSI closes.
I've also tried to change the custom action to this, but I still get the same results:
sql = "INSERT INTO `CustomAction` (`Action`, `Type`, `Source`, `Target`) VALUES ('VSDCA_Launch', '226', 'TARGETDIR', '[TARGETDIR]\\MyApp.exe')";
Update:
I ended up using a slightly modified version of "DJ KRAZE" answer. In my Main method I check for a "frominstaller" argument and then just launch the app in a new process and exit. Which then allows the installer to continue normally. Then I add the exe in the "Install" custom action with the "/frominstaller" argument.
if (frominstaller)
{
Process p = new Process();
p.StartInfo.FileName = System.Reflection.Assembly.GetExecutingAssembly().Location;
p.Start();
Application.Exit();
}
Have you tried these steps as listed in the post from the link that you referenced..?
To run any application after the installation is complete, right-click on your setup project, click on Custom Actions. Then right-click on Commit, Add Custom Action, and choose the file you would like to run. Note that it has to be in your application folder already, which shouldn't be a problem in your case since you are running your program anyway. Simply choose the output of your project.
Then, click on this added .exe, and change InstallerClass to false. This is crucial because it will look for an installer program otherwise.
You could even pass parameters to your .exe by adding them to the Arguments property
I have a batch file that I have been using to install my C# Windows Services for awhile now, never had a problem until Windows 7. I have attempted to run the batch file with Administrator privileges. I have attempted to run the command prompt with admin privs, navigate to the windows service EXE and run InstallUtil there. Still doesn't work.
After reading some other suggestions I tried moving my files out of the /bin folder and running them from another location but that also didn't work.
The batch file looks like this
#ECHO OFF
REM The following directory is for .NET 2.0
set DOTNETFX2=%SystemRoot%\Microsoft.NET\Framework\v2.0.50727
set PATH=%PATH%;%DOTNETFX2%
echo Installing IEPPAMS Win Service...
echo ---------------------------------------------------
InstallUtil /i IEPPAMS_WinService1.exe
echo ---------------------------------------------------
echo Done.
and I have a install log file that I dump info to. If I just double click the .bat file I get
Running a transacted installation.
Beginning the Install phase of the
installation. See the contents of the
log file for the
C:\Users\Justin\Desktop\service
test\IEPPAMS_WinService1.exe
assembly's progress. The file is
located at
C:\Users\Justin\Desktop\service
test\IEPPAMS_WinService1.InstallLog.
An exception occurred during the
Install phase.
System.InvalidOperationException:
Cannot open Service Control Manager on
computer '.'. This operation might
require other privileges. The inner
exception
System.ComponentModel.Win32Exception
was thrown with the following error
message: Access is denied.
The Rollback phase of the installation
is beginning. See the contents of the
log file for the
C:\Users\Justin\Desktop\service
test\IEPPAMS_WinService1.exe
assembly's progress. The file is
located at
C:\Users\Justin\Desktop\service
test\IEPPAMS_WinService1.InstallLog.
The Rollback phase completed
successfully.
The transacted install has completed.
When I run the .bat file with admin privileges nothing is written to the log file, and the service is still not installed.
Any thoughts? Is there a new way to install services in Windows 7?
Right click on the batch file and run it as Administrator.
You are most likely running into battle with the new security model (User Account Control) from Windows Vista and Windows 7. Even if you are running as an account that has Admin rights you will still need to elevate to do some (most) administrative activities. (Yes it is possible to disable this feature, but don't)
UAC (MSDN)
UAC (Wikipedia)
InstallUtil (MSDN)
Edit... The correct commandline is InstallUtil YourApp.exe. The /i does not look to be a vaild switch for InstallUtil.
So I was able to fix the problem by typing in the command line the entire path to InstallUtil and it worked. So after navigating to the folder that had my EXE I typed the following:
C:\Windows\Microsoft.NET\Framework\v4.0.21006\installutil.exe
IEPPAMS_WinService1.exe
Not sure why I have to do that in Windows 7 now when I never had to in XP, but oh well. Thanks for all the suggestions!
When I run the .bat file with admin privileges nothing is written to the log file, and the service is still not installed.
First off, you HAVE to run as admin permissions.
Second, when you "Run as Administrator", it actually changes the directory to c:\windows\system32 as the initial directory ( no idea why ), which would probably explain why running as admin causes no log file. Manually change to the path IEPPAMS_WinService1.exe resides in that the start of your script.
I have a CustomAction as part of an MSI.
It MUST run as a domain account that is also a member of the local Administrators account.
It can't use the NoImpersonate flag to run the custom action as NT Authority\System as it will not then get access to network resources.
On Vista/2008 with UAC enabled if NoImpersonate is off then it will run as the executing user but with the unprivileged token and not get access to local resources such as .installState. See UAC Architecture
Anyone know of a way to either
Force the MSI to run with the elevated token in the same way that running from an elevated command prompt does?
Force the CustomAction to run elevated (requireAdministrator in manifest doesn't appear to work)?
Work out if UAC is enabled and if it hasn't been ran elevated and if so warn or cancel the installation?
Answering my own question for any other poor s0d looking at this.
You can't add a manifest to an MSI. You could add a SETUP.EXE or bootstrapper to shell the MSI and manifest that with requireAdministrator but that defeats some of the point of using an MSI.
Adding a manifest to a CustomAction does not work as it is ran from msiexec.exe
The way I have tackled this is to set the MSIUSEREALADMINDETECTION property to 1 so the Privileged condition actually works and add a Launch Condition for Privileged that gives an error message about running via an elevated command prompt and then quits the installation.
This has the happy side effect - when an msi is ran from an elevated command prompt deferred CustomActions are ran as the current user with a full Administrator token (rather than standard user token) regardless of the NoImpersonate setting.
More details - http://www.microsoft.com/downloads/details.aspx?FamilyID=2cd92e43-6cda-478a-9e3b-4f831e899433
[Edit] - I've put script here that lets you add the MSIUSEREALADMINDETECTION property as VS doesn't have ability to do it and Orca's a pain.
requireAdministrator in the manifest should work.
You can also use a bootloader .exe file which can use ShellExecute with "RUNAS" as the verb (you can use 7-zip to create the bootloader, or there are many other ways).
You can creating a simple sfx archive file for msi file with Winrar and these options:
Setup tab > Run after execution input: your msi file name
Advanced tab > Mark Request Administrative access option checkbox