I'm running Win7 Pro x64.
I made a small console application, which gets the parameters from the command line and makes some actions based on those parameters and the config from registry.
The main goal is it should be portable, I mean one EXE file.
I wanted to make something like "setup" - on the user's demand, the app should fill the registry with default config values, which can be changed later. So I decided that my app should run "setup" when user starts it with a parameter. Let's say it should be
myapp.exe --setup
What I need to do is (in a short way):
RegistryKey k = Registry.CurrentUser.OpenSubKey("SOFTWARE\\something\\else");
// the key exists, it's checked elsewhere
k.SetValue(optionName, optionValue);
When I did this, it threw an exception System.UnauthorizedAccessException. OK, I caught it so I could inform user with MessageBox that he should run it as an Administrator.
PROBLEM 1: How to easily run the app with commandline argument as an Administrator?
I mean, will every user need to run it from RUNAS /user:userName "myapp.exe --setup" ? Is there simplier method to do it (simple like RMB->Run as Administrator but with parameter)?
I tried making .bat files with myapp.exe --setup and run them as Administrator, but it needed to have the whole path to the EXE, which is totally not portable.
OK, there's another one thing. I finally ran it with RUNAS and there was exception thrown (catched). Why? I made a simple test and turned off the "--setup" switch - so the app should run "setup" without any commandline parameters. I clicked it with RMB and choose "Run as Administrator" - the same! So:
PROBLEM 2: Why running as Administrator doesn't give me the permissions to create/edit keys in registry?
My domain account is in "Administrators" group and I've never had any problems with that.
Related
Due to some circumstances, I was ask to develop a page that uses Process in .Net C# to call the function through command prompt.
The code works perfectly Fine on visual studio, but when deployed on an IIS server. the Process doesn't seems to work, I've debugged and find out the process itself works. but the open Babel command doesn't work, i did double check and the identity i supply with should be alright, i did even supply it with admin privilege but still doesn't work. it doesn't even pop or error, it just simply ignored it and go through the commands.
I put the command into a .bat file, with some extra command to test if the .bat works. everything is fine until the command with open babel, then process just doesn't seem react with the command.
obabel %1 -O %2 --gen2d
this is the command i supply it with, %1 is the input file while %2 is the output file name. A very simple conversion. Note: everything is fine, just only the open babel command got ignored in the entire file. and I've tried to supply it with admin privilege, still doesn't work. the open babel can be used if directly used with command prompt on the server, or through visual studio. but it doesn't work if i deployed it through IIS server.
Found the solution, after all kinds of modification.
it seems like i have to provide a physical path to Open Babel.exe in the .bat file, and same as the parameter file i supply it with.
It's risky solution, but at least it solves the problem.
The problem is following: I have my custom uninstaller called before MSI uninstall. After shutting down my application properly it calls msiexec to use Windows Installer to uninstall MSI.
It's done by executing something like "msiexec /x{PRODUCT_CODE} /promptrestart".
And here is important thing - if the system is not restarted after uninstallation, and then the user installs the app again, some of its files will be deleted after next restart, so it's not acceptable. The restart is required, however, I need prompt, automatic and unconditional restart is evil and should never ever be used.
So, the invocation above displays STUPID "uninstall / repair" dialog. I do not want it. When I use "msiexec /x{PRODUCT_CODE} /qr /promptrestart" - then it uninstalls nicely, however it refuses propt for restart afterwards.
I have read about setting ARPNOREPAIR property.
But the idiots who gave that answer wouldn't care to say WHERE and HOW that property could be set. Even... Where the property belongs, it's the property of what? MSI file?
Then, maybe is it another way to achieve this, like invoke the prompt for restart from my code, but... how? The uninstaller should remove all my files until that moment. Maybe it's possible to execute a kind of script after the uninstallation process is complete?
(One more problem, the Windows Installer doesn't delete ALL files and directories created by my app. It would be nice if I could execute some code to clean up better.)
UPDATE
I see 2 paths ahead: make a script to be run once the uninstallation ends (like using Registry or Task Scheduler or IDK), use Win32 API to modify MSI file, because AFAIK it's possible to change its properties that way.
Questions: Some questions first.
Restart Manager: Are you familiar with the Restart Manager feature of MSI? Intended to help shut down and restart
applications without the need for reboots. I would give it a quick
skim? I think this is your real solution?
Alternative MSI Tools: There are many tools available for creating MSI setups. This link also contains further links to a summary of the shortcomings of Visual Studio Installer Projects.
Using the free, open-source WiX toolset - for example - you can change MSI dialogs. Github sample. SO question 1. SO question 2. And here is the official WiX toolset site.
Adding my own answer from SO: Changing text color to Wix dialogs (please do skim)
I am not familiar with how to change dialogs in Visual Studio Installer Projects. Commercial products Advanced Installer and Installshield can certainly change dialogs.
Services: What is the nature of the product you are installing? Does it has a lot of services for example? Services can be shut down and restarted via MSI tables if you use a proper tool to build the MSI.
REINSTALLMODE: Do you use a custom REINSTALLMODE for your setup? (some settings can cause more reboot prompts).
Custom Uninstaller: How do you invoke that custom uninstaller of yours? Manually or do you register an uninstall command line with Add / Remove Programs? (this latter approach is not recommended).
ARP Applet vs MSI Dialogs: The ARPNOREPAIR property is set in the MSI itself - in the property table. It affects only what is seen in Windows' Add / Remove Programs applet (ARP = Add / Remove Programs), and not what you see when your MSI is invoked via command line. Then you see the dialogs defined in that MSI itself (which can be changed - not entirely trivial to do).
ARP / Add Remove Programs Applet: A quick review of this applet below:
Hold Windows Key and Tap R. Type: appwiz.cpl and press Enter. This opens the Add /Remove Programs Applet.
Select the different package entries in the list to see different settings for ARPNOREPAIR, ARPNOMODIFY, etc...
If ARPNOREPAIR is set in the MSI's property table then the Repair entry is missing.
If ARPNOMODIFY is set in the MSI's property table then the Change entry is missing.
If ARPNOREMOVE is set in the MSI's property table then the Remove entry is missing.
If the special ARPSYSTEMCOMPONENT property is set, then the MSI will be missing from ARP altogether.
Links:
In-use files not updated by MSI-installer (Visual Studio Installer project)
So, there is an "ugly hack" which solves the exact problem:
First - we need an executable, that isn't affected by the installer. This one is easy, we just copy that one installed exe to a TEMP directory and run it from there.
The next step is to that file must wait unit the uninstall phase is done. There are a couple of ways of doing so. You can observe the installer process, you can observe the file system if main program file is deleted. Considering the pace of common installer operations, polling once a second is a good enough option.
The next step is optional - you remove remaining files created by application, empty directories and such.
The next step is reboot itself, MessageBox.Show() from PresentationFramework is fine to ask user, when user answer OK or YES, then reboot itself can be performed in many ways, I use ExitWindowsEx() from user32.dll since it's probably what msiexec calls internally.
Here's example code:
if (MessageBox.Show(RestartPromptMsg, "", MessageBoxButton.OKCancel, MessageBoxImage.Exclamation) == MessageBoxResult.OK) {
NativeMethods.ExitWindowsEx(
NativeMethods.Flags.Reboot,
NativeMethods.Reason.MajorApplication | NativeMethods.Reason.MinorInstallation | NativeMethods.Reason.FlagPlanned
);
}
Of course some parameters could be passed to our special clean up executable, so it could do some extra things, like skip the restart prompt if it's not really required.
The last step is to delete our executable itself. It's easy, but it's tricky. Again I hope my example code would help:
var cleanUpTempPath = Path.Combine(Path.GetTempPath(), CleanUpExe);
File.Copy(CleanUpPath, cleanUpTempPath, overwrite: true);
Process.Start(new ProcessStartInfo {
FileName = "cmd",
Arguments = $"/c (\"{cleanUpTempPath}\" -purge \"{InstallerDir}\") & (del \"{cleanUpTempPath}\")",
UseShellExecute = false,
CreateNoWindow = true
});
We use cmd.exe special feature, the power of & and (). Commands separated with & gets executed when previous command exits. So when our clen up exe completes, it's gets deleted by the same cmd instance which called it. Remember to quote all paths, they can contain spaces. Remember to enclose a command with arguments in parentheses, because otherwise the & operator would be seen as a parameter to the previous command, not the cmd.exe.
I tested it in my big production application and it works as charm. The code examples don't work when just pasted, if you're looking for complete code, just google for it, there are plenty of working examples on pinvoke.net and StackOverflow.
Have an msi file that is run by the user manually. They need to be able to choose the install directory in most cases however we need to forbid certain install locations. E.g. Installing it to the root directory C:\ will cause all kinds of problems, so we need to either overwrite that decision (i.e. overwrite C:\ with C:\Program Files (x86)\xxx) or pop up with an error. Is there some way I can enforce this?
The msi in question has custom actions already however there doesn't seem to be a way to edit the install location from there.
Alternatively, the msi in this case is wrapped up in a WiX bundle so if we can forbid certain directories from there that would also be good. Cannot find a way to do this either though (only know how to edit the default with <Variable Name="InstallFolder" ...>)
Only other solution I can think of would be rather horrible: make a separate application that selects a directory that then runs the installer with acceptable directory.
Can this be done either through an msi or a WiX Bundle?
I am using the "Visual Studio 2013 Installer Projects" extension to build the msi.
As a contrary view:
In general this is a bad idea. In most cases the correct answer will be to install the application code to the appropriate Program Files folder (64-bit or x86) and the data files to data locations and so on, and the user should get no choice. It is not clear to me that a choice is a good idea when (for example) the Windows Certification rules say that your code must go to the Program Files location, so just do it right. Users simply care that the installed application works correctly, and if it fails when installed to some locations then the answer is to either 1) Fix the application so that it works or 2) use Program Files and give the user no choice.
Also, if you are using Visual Studio Installer projects then you can't write custom actions to do this because they all run too late to change the install location. You seem to have discovered this already. But you CAN hide the browse folder dialog and install to the default correct location.
The other issue is that it's not clear how you would define an "allowed" location. If it's not C:\, then can it be D:\SomeOtherLocation? Can it be an attached USB drive? Can it be a network share such as \\Servername\share? A mapped drive to a network share? There are likely to be any number of chosen locations that will fail the install or the app when it runs, and I don't think there can be a useful list of what's allowed. On top of that, let's say you have a 32-bit install and the user chooses the native Program Files folder on a 64-bit system, then it won't even go there - it will be redirected to the Program Files(x86) location. Finally, it's not clear what you do in silent install mode assuming the user specifies a location on the command line, it fails your test, then the install silently fails (because silent means silent, and the install might be unattended).
In other words, just install to Program Files and have done with it.
Custom Action: This will be short. Will check back later. I can't say I have bothered implementing this recently, but a custom action can certainly inspect the installation location and abort the setup or halt it - if the path selected is found to not be satisfactory. It should also be noted that MSI actively resists installing directly to the root of C:\ and stuff like that due to the way the Directory table is implemented.
GUI: I guess one way would be to run a custom action when the user clicks the Next button in the setup's destination path customization dialog which then does "whatever you want" in terms of checking the path, and then reports any errors. This involves a DoAction event hooked up to the OK or Next button on the path customization dialog.
Silent Mode:You can also hook up the same custom action to run in silent mode (or another custom action calling the same path check function) - to account for the fact that an undesirable path could also be specified for a silent installation. In that case the custom action should abort the setup after writing into the log file, instead of reporting the path problem to the user - which is what you would do from the dialog event mentioned above - obviously.
Github: I do not have WiX code for you to implement this available on this computer. I would hit github.com and search for other projects that use WiX - you will probably find something quickly - no money for nothing and WiX for free.
Based on the users being able to manually install it (and hence using the UI sequence), it might be easier to:
In the InstallUISequence, sequence the LaunchCondition action to just before the ExecuteAction action.
Then in the LaunchCondition table, add a condition like so:
Condition:
TARGETDIR~<<"C:\Program Files\"
Description:
You must install to the Program Files folder
What we're saying in the condition is:
If the TARGETDIR starts with "C:\Program Files\" (therefore the user can install anywhere under this folder) continue with the install. Otherwise throw an error.
Rather then preventing certain locations, I'd probably just enforce the Program Files folder as a best practise.
So I've got an OPC-DA server in my program. When the user first runs the program or (even better) installs the program I want just one line of code to be run: myServer.registerServer;. And similarly when they want to uninstall the program it should run the opposite command: myserver.unregisterServer.
Now I'm guessing to do this I'm going to have to do something with a command line argument? maybe check to see if the passed value is equal to something and then if so I can register/unregister accordingly. Then if the passed value matches neither it just continues my program like normal. Issue is I'm not quite sure how to work this into the installer I'm using. Speaking of, I'm using Visual Studio Installer and using their 'Setup Project' project. Can what I'm asking for be done with this or do I need to make a separate installer. I've never had to do anything like this so any information you guys have is really appreciated.
The way you'd do this with a Visual Studio setup is to run the program as an install custom action with the command line that registers it, and an uninstall custom action with the command line that unregisters it.
Ideally you wouldn't run the program at all. Many (if not most) installation programs know what the required registry entries are and they add them to the registry entries that (in Visual Studio's case) are in the Registry view in the IDE. This data is typically always static and can be added to the system without running code.
Is it possible to find out whether your current .Net app has been launched using a shortcut or a Clickonce application reference (*.appref-ms) file? If so, how?
Some background: I am running into an issue using Microsoft Clickonce in which I cannot pass command line arguments to the application. It seems that this is the way the technology works by design. I was exploring different ways of passing this parameter; one of them was to have a set of different Clickonce Start Menu shortcuts.
Try testing out the ApplicationDeployment.IsNetworkDeployed property. I know this will be true if it is a ClickOnce app but I'm not sure if it will be false in your situation.
I'm not sure what an "application reference file" is; do you mean like double-clicking the EXE file in Explorer or running the file from a command line?
There isn't any a priori way to detect how your program was started. The usual workaround is to configure the shortcut file to pass a parameter on the command line. Then, check for the existence of that parameter at run time. If you find it there, assume the program was started from a shortcut. The key to this approach is the fact that you can't include a parameter when double-clicking the EXE file in Explorer, so if you find a command-line parameter, you know the program wasn't started that way.