How do you detect that the install is running in silent mode?
I have a custom application that I've added a .msi setup project. I run the .msi file with the /qb switch, and in my custom installer c# code I would like to be able to detect this.
Edit: nobugs says to test the UILevel property. How do I access the UILevel property from a class derived from the System.Configuration.Install.Installer class?
Taking the hint from nobugz, I did the following:
On the Custom Actions view of the .msi setup project, I added the following to my CustomActionData (to pass the UILevel through to my custom installer):
/UILevel="[UILevel]"
Within my C# code for the code derived from base class Installer, I added code to get the value:
string uiLevelString = Context.Parameters["UILevel"];
It was then simple to parse the string for an int value. If the value is <= 3, it is a silent install.
First I would point out that InstallUtil is a very bad pattern. They run out of process, tatoo the process with a CLR version and when they fail they raise a 1001 error modal dialog even during a silent install.
Instead you should use WiX's DTF pattern.
MsiGetMode ( Session.Mode ) is limited during deferred execution so you will have to serialize and deserialize the UILevel.
http://www.msifaq.com/a/1044.htm
Related
I have created a Web service. I need to add the ability to install it from the command line.
I created the installer for the web service like this:
I created a new Web Setup Project and added my web service to it. An installer user interface has been automatically created. It included a form for entering the parameters "Site", "Virtual directory" and "Application Pool". I added another form for typing two text parameters. Then I created a new project and called it "Deployment". In this project, I created the class "InstallAction". In it, I redefined the Install method to process my text parameters.
Now I need to add the ability to run the installer from the command line (Cmd.exe). The user interface should not be displayed! All parameters (including "Site", "Virtual directory" and "Application Pool") should be passed as parameters to the command. How to do this?
VS Setup Projects: Visual Studio Setup projects are limited. There are many alternative MSI tools. WiX is free and open source. Advanced Installer has great IIS features.
Parameters: I just commented on setup parameters in another question. Essentially your fields in your dialogs should have PUBLIC properties assigned to them (only PUBLIC - or UPPERCASE properties can be set at the command line). You must also mark these properties secure by adding them to the SecureCustomProperties list.
These pre-existing answers contain more detailed descriptions of how to deal with parameter passing:
How to make better use of MSI files
Parameterizing an MSI setup (just overview)
Mock-Up: for your case, maybe something like this:
msiexec.exe /i setup.msi SITE="mysite.com" VIRTUALDIR="mysite.com" APPPOOL="mysite.com" /qn
I am developing a visual studio setup project. So far all has been good except uninstalling. The program did not create its own uninstaller, but instead the uninstall feature is inside of the setup.msi. So what I need to do is during the install, I need to copy the running setup.msi to the [TARGETDIR]. The easiest way I can think of is to use custom actions. I'm pretty lost on custom actions though, I don't understand where they go and every time I try to code one, its full of errors. Looking at other questions and answers, I have come up with the following:
[RunInstaller(true)]
public partial class CustomInstaller : System.Configuration.Install.Installer
{
public override void Install(System.Collections.IDictionary stateSaver)
{
base.Install(stateSaver);
string path = this.Context.Parameters["targetdir"];
// Do something with path.
}
}
But I don't understand it. I see how it gets targetdir as that gets passed via the customactiondata. But, RunInstaller isn't known, nor is System.COnfiguration.Install.Installer. Where am I supposed to put this .cs file to make it work? Very confused.
This class should be placed in your application project. I believe it can be included in any project whose DLL is packaged by the setup project, but it usually makes the most sense to put it in the application project.
Make sure your application project references System.Configuration.Install.dll. That will resolve the reference to Installer.
RunInstaller is in the System.ComponentModel namespace (System.dll), so make sure you have a using System.ComponentModel statement at the top of the class file.
This question may help you understand custom actions more:
Why won't my Setup Project Perform my Custom Registration Process
This might help you get started, although it doesn't cover installer classes.
https://www.simple-talk.com/dotnet/visual-studio/getting-started-with-setup-projects/
Error 2835 is a bogus error related to trying to show a dialog, probably the error dialog, so it's masking whatever went wrong, most likely your code.
Make a log using msiexec /I /l*v and look for your error before the 2835.
You pass that targetdir value to your custom action like an argument list, so you'd say /targetdir="[TARGETDIR]\" and that's case-sensitive and including the brackets. It's picky because it gets merged with other arguments.
Beware that your code is not running in an application context. It's a call from the msiexec process running with the system account and a working directory of sysetm32. That means full paths need to be specified and any use of a profile-specific item (like Desktop, user profile location) will fail.
I know We can uninstall a windows application using its MSI by passing command arguments as follows,
Process p = new Process();
p.StartInfo.FileName = "msiexec.exe";
p.StartInfo.Arguments = "/x \"C:\\MyApplication.msi\"/qn";
p.Start();
But what i want to know is how can we uninstall the application without using MSI ? In the above scenario I should have the MSI in the specific location to uninstall this application, If i could unstaill using product code then I dont need to have the MSI in the target machine.
According to MSDN, You can uninstall it using the product code:
msiexec.exe /x {your-product-code-guid}
When you use the product code, it uses the cached MSI from C:\WINDOWS\Installer.
Along the lines of PhilmE's answer, Windows Installer XML (WiX) ships the Microsoft.Deployment.WindowsInstaller interop library as part of Deployment Tools Foundation (DTF). This skips the COM interop and encapsulates the Win32 API instead.
using Microsoft.Deployment.WindowsInstaller;
public static void Uninstall( string productCode)
{
Installer.ConfigureProduct(productCode, 0, InstallState.Absent, #"REBOOT=""R"" /l*v uninstall.log");
}
Probably for your case, knowing the "/x" Parameter was sufficient. Two remarks on that:
More secure is adding a "REBOOT=R" part to your commandline. And you can add a logfile path:
msiexec /x "..." /qn REBOOT=R /L*v "c:\mylogdir\mymsi.log"
Second, don't try to change anything to "the caching". You don't need even to understand it. If the cached package would be broken, a regular uninstallation is no longer possible, which could bring the computer in a "support needed" state.
Because your question was originally talking about C# .. You don't have to use msiexec for it:
a) Use the original C/C++ API with the function MsiInstallProduct() or MsiConfigureProduct().
MSDN ref:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa370315(v=vs.85).aspx
You have to use interop to use that in C#.
or b)
Use the Windows Installer Object.
For example, this related case was already answered here in stackoverflow:
Programmatically installing MSI packages
But using this function needs the physical package, also for uninstallation.
With a slight indirection, here is the better code for uninstallation:
First, add a reference to COM object "Microsoft Windows Installer Object Library" to your project.
using WindowsInstaller;
public static class MyMsiLib
{
public static void Uninstall(string productCode)
{
Type type = Type.GetTypeFromProgID("WindowsInstaller.Installer");
Installer installer = (Installer)Activator.CreateInstance(type);
installer.UILevel=msiUILevelNone;
installer.ConfigureProduct(productCode, 0, msiInstallStateAbsent);
}
}
The UILevel property before is set here hardcoded to determine the UI level silent as you seem to want. Same for other properties. See MSDN documentation e.g. mentioned in the link above.
Of course "real programmers" work with the original API instead of the "Installer Object" :-)
But for small purposes it is sufficient. And easier.
This command works on the command line:
msiexec /x {3A40307D-6DF2-4412-842F-B1D848043367} /quiet
I haven't tried it in C#, but replacing your arguments with the key shown above should work. You can find the GUID in the registry key for the app you are trying to uninstall.
I'm very new in C# world though I'm trying to modify an installer using custom action. What I'm trying to do is to run a batch script after installation finishes. I'm using the following Installer class:
namespace PostInstall
{
[RunInstaller(true)]
public partial class Installer1 : Installer
{
public Installer1()
{
InitializeComponent();
System.Diagnostics.Process.Start("PostInstall.bat");
}
}
}
A project named PostInstall contains a *.cs file with the code above. The project is created following this link's section "To create the custom action". In the setup project, I've added the primary output of PostInstall project in Install & Commit node as described in the link.
Yet at the end of the installation the following error is received:
Error 1001. Unable to create an instance PostInstall.Installer1
installer type -> Exception has been thrown by the target of an
invocation. -> The system can't find the file specified.
I verified that PostInstall.bat exists in the installation directory. Why the error is occurring and how to resolve it?
I think the installation directory is not automatically the directory in which the installer is run, so you cannot assume that Process.Start("PostInstall.bat") will find the batch file.
This DevCity article is a very good introduction to this topic, and tells you how to get the installation directory passed to your custom action objects so that you can create the full pathname for your batch file.
Have you thought about what will happen while your batch file is running? Do you want to wait until it is finished before proceeding to the next stage of your installation?
Ideally the custom actions should be written in native code, if it's not possible in your case try to run the .msi as Administrator
I have an InstallShield MSI project. When I pass an MSIHANDLE from an InstallScript custom action to a managed assembly initialized via DotNetCoCreateObject(), the value received within my managed code is -2.
Does anyone know if it is possible to access an MSIHANDLE from an InstallScript custom action that calls into managed code via DotNetCoCreateObject()? I'd like to log my custom action results to the same log file as the rest of the installation. I am using InstallShield 2010, Windows Install 4.5 and .Net 3.5.
It is only possible via a managed custom action and requires use of InstallShield's InstallShield.Interop.Msi.dll to get at the actual handle.
To write to the MSI log file from a managed custom action, this works:
using (Msi.Install msi = Msi.CustomActionHandle(_msiHandle))
{
using (Msi.Record record = new Msi.Record(100))
{
record.SetString(0, "LOG: [1]");
record.SetString(1, entry.Message);
msi.ProcessMessage(Msi.InstallMessage.Info, record);
}
}
NOTE: As of IS2010, InstallShield.Interop.Msi.dll is not digitally signed, so the assembly with your managed custom action must also be unsigned.
No, it's not possible. You'll have to manage your log output yourself.
Another pt of clarification is that IS has two project types, InstallScript & MSI. You can only access the MSI handle within MSI projects.