Installer options available in application? - c#

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.

Related

Use admin privilege on WIX custom action

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

Embed individualized code into ClickOnce setup.exe?

I know that it is possible to pass in parameters via URL to ClickOnce apps launched online. However, most users downloads setup.exe and launch it from their machine. Is there any way that I can re-write setup.exe at download, insert a code (let's say the user's email address), and then have the app launch with knowledge of the code? Assume that we can somehow re-sign setup.exe so that it is legit.
Assume .NET 3.5.
Update The goal here is to pass on either email address and/or referrer information to setup.exe so that even when the user runs the installer from a different machine and a different ip we can figure out who did the referral.
Update 2 Assume .NET 3.5 SP1, does it help? Apparently one can now pass parameters to .application while offline. Is it possible to embed parameters into the setup.exe so that it calls .application?ref=someone right when setup.exe is run?
Well, if your goal is to embed a customer id (email, code, etc) into the exe, the easiest way I can think of is using the IPropertyStorage and IPropertySetStorage interfaces. If you are feeling brave, you could call methods directly on IPropertySetStorage via p/invoke, or you could go the easy route and use Microsoft's prepared COM wrapper, which is called dsofile.dll.
Note that while dsofile is intended for office documents, it does indeed work on any file - including .exe files - you are just stuck with the pre-defined property names. Why not throw your customer id into something like the .Comments property. Just do it in such a way that you can parse it out again.
Here's a sample:
var doc = new OleDocumentPropertiesClass();
doc.Open(pathToFile);
doc.SummaryProperties.Comments = "joe#test.com";
doc.Save();
Of course, you need to first copy it to a temp location, and some time after the user downloads it you'll want to delete it.
You can bundle dsofile.dll with your application and register it as a dependancy and use it in your installer to read the property back out. Or if you can p/invoke the IPropertyStorage without it, then you won't have the dependancy.
The other thing to look into would be using the extended file properties that are read by the Shell32.dll. I just haven't been able to find a clean way to write them easily. If you go this route, please share how you wrote the properties to your .exe.
Have a look whether InPlaceHostingManager class can help you in this case. It won't probably do exactly what you have asked for. But may be able to help...
Any ClickOnce application based on an .exe file can be silently
installed and updated by a custom installer. A custom installer can
implement custom user experience during installation, including custom
dialog boxes for security and maintenance operations. To perform
installation operations, the custom installer uses the
InPlaceHostingManager class.
Walkthrough: Creating a Custom Installer for a ClickOnce Application
EDIT
I am not sure whether you could achieve what you want exactly in the way that you have described in the question. Check whether these threads help you.
Accessing Local and Remote Data in ClickOnce Applications
How to include custom data files in ClickOnce deployment?
How to: Retrieve Query String Information in an Online ClickOnce Application
How would you imagine to "rewrite" setup.exe at download? if instead of opening your application with the provided link (url) users are downloading the file locally directly from the network share, you can't intercept this.
I would try to play with permissions and have the users to execute it from the link provided to them, but unable to connect directly to the share or web address and download it. Not sure this is possible anyway.
You can try embedding that information as a resource into the exe.
Here's a c++ example of updating a resource of an exe. http://msdn.microsoft.com/en-us/library/ms648008(v=vs.85).aspx#_win32_Updating_Resources
You should combine approach by Charith and Josh - essentially, configure your web server so that you can generate a new setup based on URL parameters. Use custom installer to read from the referral information from resource for setup.exe. Check this link for how to manipulate resources for a native application in C# - you have to write to resource file while generating setup and need to read it from your custom installer.
Yet another way of generating custom setup would be to build your executable or helper assemblt from command line embedding the necessary information. And then build the setup from command line tools (see http://msdn.microsoft.com/en-us/library/xc3tc5xx.aspx). It appears to be quite cumbersome and will take long time to generate the custom setup as opposed to modifying the resource of already built setup.
A completely different approach would be to email the unique referral code (registration code) whenever user downloads the application. In the setup (or application), use custom installer to prompt user for this code. If the setup is invoked via URL then the code will be available from there and in such case Custom Installer need not ask for the code. The email that you send when user download the setup should inform user to preseve the code into some text file along with the setup file.

Store information in a file that persists after uninstall

I'm developing an application that will be published via ClickOnce. I've been using the Application.ExecutablePath folder to store some licensing information. Now, I want the application to check for updates, but whenever it does the license file get lost and the user needs to enter the information again. Seems like ClickOnce is basically uninstalling and reinstalling the new version of the application. I could save the license file somewhere in the System folder, but that's an ugly solution. Another file with the same name could already exist in there, or I might get access denied exceptions or stuff like that. I'm not trying to hide the file. And I need the folder to be accessible without administrator privileges.
So what's the best and safest way I can store a file that will stay after I uninstall (or update) the application?
I think you should store it in the common application data place. You can get the folder path by the following code
Environment.GetFolderPath(System.Environment.SpecialFolder.CommonApplicationData);
This folder is a built-in special folder. It's main purpose is to store shared application data for all the users using the same application. Usually, you would like to create a sub folder with your compaany name and product name under that folder so that it won't conflict with other application. If your don't want to share your license key to all users on the same machine, you can use System.Environment.SpecialFolder.ApplicationData instead.
I have seen many applications hide their license settings in the Registry - sometimes hiding under clever key names. I hate it as a user, but if you like to keep the data I think registry is the best.

C# -> Updating an AppSettings.config file on Win7/Vista

I can't see anything on here but I do remember being told that If you want an application to update a config file then it needs to be under ...
**C:\Users\Ibrar Mumtaz\AppData**
Well somewhere there, the reason being is that the user should have permisions to update a config file here and not under the applications install folder. This is the impression that I am under and I'm fairly certain that this is definately the case. As I think I read that on here = p
My question is, is there anybody on here that can shine some light on this as this is the last feature I want to implement before I give my application out to test.
1) First thing is, an installer is needed to set up the folder and then drop my apps config file into it. I already am using the visial studio installer so I have my app packaged up but this point is throwing me off? How do I do this then? I just need someone to show how to do this and I should be O.K reconfiguring my app to look for the new home of the config file.
2) I should be able to work out how to find the folder and locate the config file found within it. As once I know the installer is chucking the config file out into the right folder where the user has permissions then it should be straight forward from there.
Thanks for reading.
UPDATE:
It was pretty straight foward, as the VS Installer has an option to add a special folder so all that was left was to access the folder programmatically and read and write to the config file. ONE PROBLEM? The ConfigurationManager class which I have used to create my config file for my application expects my config file to be local to the application and not miles away in a completey different part of the local FileSystem? Errr help here Plz?
Ibrar
If you are using the VS Settings file to create application setting keys, and have values that the user might want to change in runtime, and save his preferences, just set the scope of those settings to "User" instead of "Application".
That way you will have a setter method for them, and you can edit the Settings.Default instance, and when you are done call the Save() method to persist them to disk.
The file will be saved in the user's "AppData" folder, wherever it is, under some cryptic folder. But you needn't worry about it's location most of the time, since it will be read automatically on the next execute, and persisted to the same location on subsequent runs.
Afaik the installer can be extended with classes that do things.
On INSTALL-action to do could be to
var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"My app name");
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
And vice-versa on uninstall.
App.config files are related to where the physical assembly is located I think.
Actually, if your app is running on the user's machine- it will have whatever permissions that user has. So most likely, you can expect to be able to write anywhere on the file system.
However it is possible the user would be running under a restricted acct, and thus not have the permissions. So you could just use the registry to store where your config file is (install folder), then when you try to update it, if it fails for permissions, ask the user to grant it.
Or you could use the Windows standard folders, as you were getting at, because doing so also separates out user data from application data.
Use the Environment.GetFolderPath () method to get the 'special folder' paths in your app.
http://www.programmersheaven.com/2/Les_CSharp_15_p2
http://msdn.microsoft.com/en-us/library/14tx8hby.aspx
If you are talking about application settings found on project Properties -> Settings tab, then there're two different types of settings: user-level and application-level.
If you need to change any settings in run-time, these would be user-level settings (http://msdn.microsoft.com/en-us/library/cftf714c.aspx) and all changes would be buried somewhere in the private folder in your user profile.
If you want to update application-level settings, your best shot would be to do that during software installation. In this case you don't need to look for the configuration file (YourApp.exe.config) anywhere but in the application folder. More likely you would need to create some sort of post-install event handler in your setup package and run some script or another application which would update data in YourApp.exe.config. Everything in the setup package will be executed with elevated priviledges and thus that configuration file would be writeable. BTW, this scenario applies to 2000 and XP, if the user is using limited user account type priviledges.
Because I did not technically find the answer I was looking for, after 6 months I have come back to my application and have managed to produce a solution that does not break my current architecture.
If you are implementing an application to make use of some of the features on offer by the ConfigurationManager then it offers a static method called:
ConfigurationManager.OpenMappedExeConfiguration(); // takes two arguements.
It can be used like this:
ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
fileMap.ExeConfigFilename = returnUsersAppDataFolderPath();
Configuration config = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
e.g
fileMap.ExeConfigFileName = #"some file path external to your applications install folder."
remember to use '#' symbol in front to allow the compiler to literally treat the string on as is basis.
If the config file can be conveniently locally located then just use the:
ConfigurationManager.OpenExeConfiguration(string exePath)
Above is what you would typically use but for me i needed my config file to located under the users AppData folder so the first option is what I needed. And it does indeed work.
I hope this helps others as it does for me as I want to deploy my application to Win7 and vista environments therefore this question needed asking if I was to stick to using the ConfigurationManager it's a shame the method of choice in the end never really stood out in the first place = ).
If you want to read from your config file then leave a comment and I will show you how I managed to do that also.

Writing drive C: in Windows 7/Vista

I am developing an application that saves its settings in the install folder. If I install the app in the Program Files on drive C: and attempt to write the settings file I get an exception and that's it.
I know that the User Account Control (UAC) migth be the one that is not letting my app modify the content of the file.
I need help with the following issues:
Do the file editing in such a way that at least an UAC warning should be shown and if I answer yes the file becomes writable
If there is no way to edit the file on drive C: I need a method to store data somewhe
A more generic question would be:
How to create a C# program that after installing it to C:\Program Files\MyProgram under Windows Vista can manipulate (create/edit/delete) an .ini file in the installation directory? This file should be the same for all users.
Why don't you store the settings in a user-specific location like C:\Users\Username\AppData?
That way different users can have different settings on the same machine. Also, this is the recommended location for settings and the like.
Building on the answer from Ben S, check out the Environment.GetFolderPath method.
This method allows you to abstract away the specific location and just use a known SpecialFolder path instead (ie SpecialFolder.ApplicationData).
The fact that you are getting an exception means that your program is Vista/7 aware somehow. I am not totally sure what setting (in a C# project) triggers that. But if you can make your app 'pretend' it is an XP application, Vista will let it write to a shadow file located elsewhere.
But the proper way to get a writeable path shared by all users:
string path = Environment.GetFolderPath(
Environment.SpecialFolder.CommonApplicationData);
I'm surprised it hasn't been mentioned yet, but a viable C# option is to ditch INI files (yuck) and embrace the Settings facilities provided by .Net. They work very well across all Windows versions, they are directly supported by Visual Studio, and finally they are overridable at both the User and Machine level.
We've had no real problems to speak of utilizing this feature (this includes XCopy deployments, Installed applications, Citrix, etc).
Building on the answers of Ben S and akmad, you should put the ini file in the appDataFolder.
If you want the settings to be unique to each user, create an ini file for each user and put it in their AppData folder, which can be retrieved with the following code:
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)
If you want the settings to be common to all users, but the ini file in the common AppData folder.
Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)

Categories