Use admin privilege on WIX custom action - c#

Glytzhkof: This question relates to a common problem for new MSI users, the belief that one needs a custom action for something MSI supports natively by a feature they are unaware of.
In this case the IniFile table in MSI that allows merging, rollback and updating of shared Ini files on the system. All updates to Ini files should be performed via this table since you get a lot of features for free and you will be in line with all MSI guidelines.
Always avoid custom actions if there is a way to achive the same effect by native features.
I have a c# customaction on my WIX msi.
When msi start require admin account and all works fine excepted for my customaction, it can't write inside programdata folder.
I have set impersonate="no" according to wix reference:
This attribute specifies whether the Windows Installer, which executes as LocalSystem, should impersonate the user context of the installing user when executing this custom action. Typically the value should be 'yes', except when the custom action needs elevated privileges to apply changes to the machine.
But does not works.
This is my custom action configuration:
<CustomAction
Id="MyCustomActionCA"
BinaryKey="mycustomactionDLL"
DllEntry="MyCustomAction"
Execute="immediate" Return="check" Impersonate="no" />
<InstallUISequence>
<Custom Action="MyCustomActionCA" After="CustomDialog1">NOT Installed</Custom>
</InstallUISequence>
MyCustomAction is launched after CustomDialog1 is closed, and this is OK, my code run fine but in MyCustomAction i have a line of code like this:
File.WriteAllText(mypath, mycontent);
mypath refers to ProgramData folder and throw an exception becouse user can't write inside it.

There seems to be two problems here as far as I can tell:
The first is that the CA needs to be deferred if it's impersonate="no", and it's not clear that you've done that.
The second problem is that ProgramData is a user profile notion. For example it's in C:\Users\ on my system. If you run with impersonate="no" you're running with the system account, so it's going to try to write to some weird (non existent) C:\Users\System folder.
You might have a design issue here in that you say you need elevated user privileges to write to that folder, but you can't elevate unless you are deferred with system account privilege. You may need to describe the exact problem you're trying to solve to see if there is another way.

Check this thread for information on how to use MSI's built-in support for writing to INI files:
Wix modify an existing ini file
Some further reference:
http://wixtoolset.org/documentation/manual/v3/xsd/wix/inifile.html

Best option: redesign your application to write this ini file as a per-user file (any user can write to his own user profile) on application launch from internal defaults in the exe itself. Solves the deployment problem too.
As to what is happening in your deployment context: Windows installer runs only part of the installation as LocalSystem - what is referred to as the installation transaction. The rest of the setup is run as the user who launches the MSI - and this includes the entire user interface sequence which is where you have added your custom action.
Try setting the action deferred, schedule it prior to InstallFinalize and set impersonation to no.
With regards to per-user data management, see this post on a slightly different topic for a way to handle per-user data that needs management between deployed versions of the application: http://forum.installsite.net/index.php?showtopic=21552

Related

Why does WScript work when I run my script from the desktop, but not when run as part of Setup.exe?

I've been working on this issue for about 6 hours now and feel like I'm getting nowhere. I have a vbscript to write to the windows registry that I want to use during the installation of a .net Visual Studio program. It writes to the Local Machine part of the registry, so permissions have to be edited to accomplish that. The code in question is as follows:
If Not WScript.Arguments.Named.Exists("elevate") Then
CreateObject("Shell.Application").ShellExecute WScript.FullName _
, WScript.ScriptFullName & " /elevate", "", "runas", 1
WScript.Quit
End If
The code works perfectly when I just run the script from my desktop, and does exactly what I need it to do. But when I run the setup.exe that includes the script (even if I run setup.exe from my desktop), I get an Object Required error from the WScript code above. I need to know either 1) why I am getting this error and how to allow access to the WScript object, or if that is impossible 2) how to give my script the proper admin privileges required to write to the Local Machine Registry without using WScript.
If you are running this script as a VBScript custom action, then Windows Installer uses a custom VB script host. Unlike wscript.exe and cscript.exe, this host does not provide the WScript object, so any references to it will fail. You could choose to launch it as an EXE action via wscript.exe instead. I would not recommend this, at least not without reading my third paragraph.
Also, as a general recommendation, you should avoid elevating within a custom action, as doing so leads to the potential of multiple UAC prompts during a single installation. So to write to per-machine areas of the registry you should prefer using the Registry view, and thus entries in the Registry table. If you need custom values, note that you can specify property references in the view/table, and they will be evaluated when writing the values.
If you absolutely need to write to the registry through a custom action, consider carefully the rollback scenarios, and prefer to use an action that is "deferred in system context," as this kind of custom action will have access to administrative privileges if the installation is per-machine. Note that deferred custom actions do not have access to most properties.

Shortcuts changing the relative path?

I have used Flexera's Installshield and it has seemed to work for the most part except the shortcuts it placed in the start menu and on the desktop.
The issue arises when the program starts and returns an error with opening the sqlite database file.
The issue is fixed by running the actual .exe from the program files folder, and if I create a manual shortcut.
I have noticed that the automatic shortcuts they have a target of what the manual shortcut has for a target location.
Any ideas?
I am not in a position to propose a direct solution for your specific problem. On the other hand, I have had some paths/relative-reference problems in the past when working with different Windows installer packages which I solved by running a program passing all the required information to the installer.
Example of what I mean:
A small program is executed right before starting the installation. It retrieves all the required information (e.g., desktop's path) and writes it to a temporary location (e.g., Windows Registry).
The main installation package is started, reads/deletes the
information from the temporary location and brings it into account
while performing the installation (e.g., path for the shortcuts).
Depending upon the available setup options, you might perform the aforementioned actions in different ways. The simplest alternative I see is: creating an .exe file (install.exe), only one to be executed by the user; it will be in charge of retrieving all the required information, writing it to the temporary location and calling the main package (\installation files\setup.msi). Right after being started, the installation package will read the information from the corresponding temporary locations, delete them and bring this information into account while performing the corresponding actions (setting up the shortcuts).
The main reason why I prefer to rely on this kind of configurations (preliminary .exe calling the main package or script/.exe being called by the installation package as the first action) is because it offers a high flexibility. It does not only accont for eventual problems/incompatibilities from the installation software, but also sets up a framework allowing me to perform more complex actions (e.g., security system) whose implemention is much easier by relying on the corresponding programming language (C# in this case).

Unable to write registry in custom action of MSI installer

I have an MSI installer that installs my Windows Service and in my custom actions I need to write some values into the Registry in the HKEY_LOCAL_MACHINE/SOFTWARE/MYSoftware key.
I am trying to do this and it's not working, but from my Windows Service it's working fine. Can anybody tell me where I am going wrong?
string registryLocaltion = AgentProperties.TMAGENT_REGISTRY_LOCATION
+ #"\" +AgentProperties.TMAgentVersion;
tmKeyMain = Registry.LocalMachine.OpenSubKey(registryLocaltion, true);
if (tmKeyMain == null)
{
log.Error("Unable to open registry key " + registryLocaltion);
}
tmKeyMain.SetValue("UseProxySettings", settings.UseProxySettings);
if (settings.UseProxySettings)
{
tmKeyMain.SetValue("ProxyHost", settings.ProxyHost);
tmKeyMain.SetValue("ProxyPort", settings.ProxyPort);
tmKeyMain.SetValue("ProxyUsername",
GenericHelper.ConvertToBase64Encoding(settings.ProxyUsername));
tmKeyMain.SetValue("ProxyPassword",
GenericHelper.ConvertToBase64Encoding(settings.ProxyPassword));
tmKeyMain.SetValue("ProxyExclusion", settings.ProxyExclusion);
tmKeyMain.SetValue("BypassProxy", settings.BypassProxy);
}
This code is working fine in my Windows Service, but if I do some thing very similar in my custom action in the MSI installer it doesn't work.
Can anybody tell me where I am going wrong?
You are up against a couple problems. The most obvious problem is that Visual Studio Deployment projects incorrectly schedule custom actions to impersonate the client context. This means in a UAC scenario you won't have permissions. The quick work around is to run the MSI from an already elevated command prompt context.
The second problem is that Visual Studio Deployment Projects abstract / hide the underlying MSI too much. For Custom Actions, it only gives you the options of "install, uninstall, rollback, commit" without exposing any additional settings. It hides the ServiceInstall and ServiceControl tables. This causes you to write a custom action that reinvents the wheel.
See, all your custom action should be doing is performing the business logic and setting properties. Then you should be using the Registry table to set the data based on the properties. This way you leverage as much of Windows Installer as possible and all of it's free transactional / rollback capabilities.
This problem repeats over and over and is why Microsoft killed of the setup project types in VS2012.
If it was my install, I'd be refactoring the design to use AppSearch/Reglocator to read in the data, have a minimalist custom action to do the processing and then use the Registry table to apply the data.
That will require you at a minimum to look at Windows Installer XML to create a merge module that has all of this logic and gets merged into your existing setup project. That takes awhile to learn though.

Installer options available in application?

I have a basic installer with an option to install for all users or only for the current user. Based on this selection I copy several files in the common app data or the local app data.
My question is, how do I let my application know where have these files been installed. And I don't mean providing a hard coded path but more like providing the ability to choose between Environment.SpecialFolder.LocalApplicationData (Single User) and Environment.SpecialFolder.CommonApplicationData (All Users).
You should send the ALLUSERS custom action data to a installer class using the installer Custom Actions.
Here is a great custom action installer example that uses Regasm to register .NET assemblies.
Once you have your installer custom action and custom action data - you can store it somewhere where your application can retrieve it - either in the registry, application config, or to a fixed location on disk.
An easy way to figure out which folder your data is in from your application is to check the LocalApplicationData, and if something is there, use it, else check the CommonApplicationData folder.

Win7 UAC problems when running application designed for WinXP

Application2 was orginally developed an application for WinXP. Now I have to contend with User Account Control (UAC) on Win7. The Application2 runs fine on Win7 as long as the user is logged into an Administrator account and they select "Run as administrator." However, I am trying to evaluate whether it is possible to refractor Application2 so that it does not require an Administrator account and "Run as administrator." I would also like to avoid any dialog boxes requiring users to make a choice if possible.
Application2 features that currently require "Run as administrator"
Create System Data Sources (ODBC)
Read/write/delete registry keys
Copy/delete files in C:\Program Files\MyApp
Encypt/decrypt files in C:\Program Files\MyApp
Start .exe processes located in a shared folder on a remote machine
Start VBScript processes that install Office Add-ins and read/write/delete registry keys
Copy/delete/modify files in C:\Program Files\MyApp\MyData
Number 6 can be solved by relocating this folder to My Documents (its location is stored in the registry*), or granting the user "Full control" permissions to C:\Program Files\MyApp\MyData, or letting Win7 make the necessary modifications in the VirtualStore folder.
Numbers 3 and 4 is more difficult to deal with because Application1 has to read files in C:\Program Files\MyApp. Application1 will not be using "Run as administrator," and I need to do more research to figure out how the VirtualStore folder can be used to bridge the gap between Application2 and Application1.
As for the other points, I am trying to figure out options:
Please let me know if/how it is possible to perform these actions under a non-Administrator user account and without "Run as administrator."
Please let me know if any of the actions are impossible without being an Administrator.
Feel free to point out any technical mistakes that I may have made in the contents of this question.
Again, I am trying to avoid dialog boxes during application runtime.
*Thinking out loud here: I would need to delete C:\Program Files\MyApp\MyData after copying to C:\My Documents\MyData, and I would need to modify the registry key storing the folder path, maybe these steps could be done during installation.
Discoveries
On Windows 7, even without "Run as administrator" you can:
Create/delete User Data Sources
Create/modify/delete files in C:\Users\Public\Documents or C:\Users\currentuser\Documents
Create/delete registry keys in HKEY_CURRENT_USER
Furthermore, I believe it is dangerous to have a Windows 7 application without a UAC Application Manifest file. Because, if you don't have one, Windows 7 decides for you whether to run the application in Compatibility Mode or not. If you have one, you can specify how your application needs to run.
No, you can't do these things without getting elevated. The point of UAC is not to stop you from doing this, it is to let the user know that you're about to do this. An obvious and valuable property of UAC is that it doesn't provide a backdoor to do these things anyway without the user knowing about it. This is not a problem, it is a feature.
What you describe doesn't strike me as something that needs to happen frequently. This should not wear out the user. If that's an issue, you can run it from a scheduled task using an admin account.
The best thing to do is to change your application - only slightly, mind you - so that it will run as a non-elevated user. To help you do that, I have to point out some subtleties.
For example, point 1, "read and write registry keys", is overly general. Applications that are non elevated can and do read and write registry keys with no problem at all. The restriction is that you can't write registry keys in HKLM. So you can avoid UAC issues if you change your code to use a key under HKCU, or if you refactor some of your code into an admin/config/setup app that runs elevated and writes keys to HKLM, and an ordinary app that reads those keys but never writes them.
Point 2 and 3 are both "can't write to anything under Program Files." Change your code so that the files you need to copy/delete/encrypt/decrypt are not under Program Files. AppData is a popular (per user) location for this.
Stay away from the whole virtualization thing and deliberately trying to use the Virtual Store. It confuses users tremendously. Instead, gain a firm understanding of the behaviours that non-elevated apps can't execute, and adjust your code to perform the (very similar) replacement behaviours, such as using HKCU instead of HKLM or AppData instead of Program Files.

Categories