Introduction
One of the things I hear all the time when companies talk about building reliable Windows Installer (MSI) for their product is –> Visual Studio Setup Project is not enough and we need much much more, my first response is – Wait, VS Setup project is not like Advanced Installer, InstallShield but still can do a lot more outside the box.
Setup projects are used to create Windows Installer (.msi) files, which are used to distribute your application for installation on another computer or Web server. There are two types of setup projects:
- Standard setup projects create installers that install Windows applications on a target computer.
- Web setup projects create installers that install Web applications on a Web server.
In this article, I’ll show how to extend your VS Setup Project and help to understand how VS Setup makes it easy for developers to build reliable Windows Installer (MSI) in Visual Studio. (All my screenshots will take from Visual Studio 2010.)
- Getting Started With VS Setup Project
- Adding New User Dialog and Deployments Conditions
- Run External Application during Setup
Getting Started
Open Visual Studio and create new Setup Project called – "DemoSetup
", the Setup project item can be found under "Other Project Types"-> "Setup and Deployment" –> "Visual Studio Installer".
Also create a WPF application called – "DemoWpfApplication
", we need some project to work with.
Two things need to be done in order to get the MSI ready for deployment.
- On the
DemoSetup
project, Add Project Output from DemoWPFApplication
– Setup will automatically find all related dependencies. - Modify Setup Project properties (See picture below)
The Version
Property is very important for various MSI, in order to identify old installation and overwrite existing files with a newer version.
Adding New User Dialog and Deployments Conditions
Now, this is one of most common improvement users always want – Add an additional Dialog during the setup and add your own questions or input.
In this step, I’ll show how to add conditions and add a new user Dialog, here are the requirements: Add the following file (Dummy Files):
- Blue.bmp,Red.bmp, Green.bmp
- License.rtf
- "Readme for 2000.txt", "Readme for Windows 7.txt"
1. First Let's Add Our Product License Agreement
Select the Setup Project and click "User Interface Editor" icon:
This will open a view showing all the dialogs the user will see during the Setup process.
Click "Add Dialog" and choose "License Agreement", after adding this dialog view dialog properties and select the License.rtf file.
2. Add Deployment by Condition
Add deployment condition for "Readme for 2000.txt", "Readme for Windows 7.txt", the propose is to copy each file only for specific operation system.
Select "Readme for 2000.txt" to see his properties, locate Condition
and add this command –
WindowsBuild = 2195 or VersionNT = 500
Now select "Readme for Windows 7.txt" and add this in the Condition property –
WindowsBuild >= 7100 or VersionNT = 601
(More information on Operating System Property Values)
The condition will allow you to copy specific file depending on the user OS.
3. Add your User Choose Condition
This step will show you how to add your own questions (using Radio Buttons) and allow the user to define a Favorite Color and use User choose as condition for deployment.
Add a new Dialog and choose "RadioButton
(3 buttons)" add fill the information as below:
To add the condition, you need to select each of the following files: Blue.bmp, Red.bmp, Green.bmp and add the proper value in the condition property as follow - FAVORITECOLOR="Blue"
, FAVORITECOLOR="Red" etc
.
Build the setup project and run it, select Green in "Favorite Color" and the result should look like:
Run External Application during Setup
In this step, I’ll show how to run an external application before the actual install process using "Installer Class".
Create new WPF Application project called – "SetupHelper
" and add additional Item of type "Installer Class" called – "MyInstallerHelper
".
The InstallerClass
will allow you to override the following events:
Rollback
Install
OnAfterInstall
Commit
OnAfterRollback
OnAfterUninstall
OnBeforeRollback
OnBeforeUninstall
OnCommitted
OnCommitting
Uninstall
OnBeforeInstall
First, add your code for "SetupHelper
" WPF application, I’ve added code to show current processes and process title, but you can add whatever you want.
Now we need to add "SetupHelper
" WPF app to our setup project, select the Setup Project and "Add Project Output" of SetupHelper
.
Select the Setup Project and click on the "Custom Actions Editor" icon:
On the install, add new "Custom Action" and from the "Application Folder", pick "Primary output from SetupHelper
(Active).
Now select the new Custom Action and in the "CustomActionData
", add the following -
/Run=SetupHelper.exe /WaitForExit=true
Back to MyInstallerHelper
, override OnBeforeInstall
and paste the code below in order to get the CustomActionData
you wrote above:
protected override void OnBeforeInstall(IDictionary savedState)
{
try
{
base.OnBeforeInstall(savedState);
FileInfo fileInfo = new FileInfo
(System.Reflection.Assembly.GetExecutingAssembly().Location);
string sProgram = Context.Parameters["Run"];
sProgram = Path.Combine(fileInfo.DirectoryName, sProgram);
Trace.WriteLine("Install sProgram= " + sProgram);
OpenWithStartInfo(sProgram);
}
catch (Exception exc)
{
Context.LogMessage(exc.ToString());
throw;
}
}
OpenWithStartInfo
will run SetupHelper
application and will pause the setup process while the SetupHelper
is open.
void OpenWithStartInfo(string sProgram)
{
ProcessStartInfo startInfo = new ProcessStartInfo(sProgram);
startInfo.WindowStyle = ProcessWindowStyle.Normal;
string[] ExcludeKeys = new string[] { "run", "WaitForExit" };
startInfo.Arguments = ContextParametersToCommandArguments(Context, ExcludeKeys);
Trace.WriteLine("run the program " + sProgram + startInfo.Arguments);
Process p = Process.Start(startInfo);
ShowWindow(p.MainWindowHandle, WindowShowStyle.Show);
SetForegroundWindow(p.MainWindowHandle);
BringWindowToTop(p.MainWindowHandle);
Trace.WriteLine("the program Responding= " + p.Responding);
if ((Context.IsParameterTrue("WaitForExit")))
{
p.WaitForExit();
}
}
ContextParametersToCommandArguments
gets the CustomActionData
arguments you added in the Custom Action.
public static String ContextParametersToCommandArguments
(InstallContext context, string[] ExcludeKeys)
{
ExcludeKeys = ToLower(ExcludeKeys);
StringBuilder sb = new StringBuilder();
foreach (DictionaryEntry de in context.Parameters)
{
string sKey = (string)de.Key;
bool bAdd = true;
if (ExcludeKeys != null)
{
bAdd = (Array.IndexOf(ExcludeKeys, sKey.ToLower()) < 0);
}
if (bAdd)
{
AppendArgument(sb, sKey, (string)de.Value);
}
}
return sb.ToString();
}
public static StringBuilder AppendArgument(StringBuilder sb, String Key, string value)
{
sb.Append(" /");
sb.Append(Key);
if (value != null)
{
sb.Append("=");
sb.Append(value);
}
return sb;
}
#region "FS library methods"
public static string[] ToLower(string[] Strings)
{
if (Strings != null)
{
for (int i = 0; i < Strings.Length; i++)
{
Strings[i] = Strings[i].ToLower();
}
}
return Strings;
}
#endregion //"FS library methods"
#region "showWindow"
[DllImport("user32.dll")]
static extern bool BringWindowToTop(IntPtr hWnd);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, WindowShowStyle nCmdShow);
private enum WindowShowStyle : uint
{
Hide = 0,
ShowNormal = 1,
ShowMinimized = 2,
ShowMaximized = 3,
Maximize = 3,
ShowNormalNoActivate = 4,
Show = 5,
Minimize = 6,
ShowMinNoActivate = 7,
ShowNoActivate = 8,
Restore = 9,
ShowDefault = 10,
ForceMinimized = 11
}
#endregion
Try It
Now Run the Setup project and you will notice that during the installation process, your Setup Helper will pop up and prevent the Setup from continuing until you will close the application.
After the setup is complete, you will see your setup output as below:
Summary
As you saw in this article, Visual Studio Setup Project can be extended a lot more and this can be good for many applications.
History
- 16th January, 2011: Initial post