1.png

One of the things I hear all the time when companies talks for 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.)

  1. Getting Started With VS Setup Project
  2. Adding New User Dialog and Deployments Conditions
  3. 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.

  1.  On the DemoSetup project Add Project Output from DemoWPFApplication. – Setup will automatically find all related dependencies.
  2.  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.

2.png

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 is 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 add our Product License Agreement

Select the Setup Project and click "User Interface Editor" icon
3.png
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.

4.png

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 depends 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:

5.png

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:

6.png

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
 7.png
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);
        //Take custom action data values
        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); //otherwise it is not activated 
    SetForegroundWindow(p.MainWindowHandle);
    BringWindowToTop(p.MainWindowHandle); // Make sure the user will see the new window above of the setup.
    Trace.WriteLine("the program Responding= " + p.Responding);
    if ((Context.IsParameterTrue("WaitForExit")))
    {
        p.WaitForExit();// Have to hold the setup until the application is closed.
    }
}

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);
    //Note that if value is empty string, = sign is expected, e.g."/PORT="
    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"

// <a href="http://pinvoke.net/default.aspx/user32.BringWindowToTop">http://pinvoke.net/default.aspx/user32.BringWindowToTop</a>
[DllImport("user32.dll")]
static extern bool BringWindowToTop(IntPtr hWnd);

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetForegroundWindow(IntPtr hWnd);

//from <a href="http://pinvoke.net/default.aspx/user32.SwitchToThisWindow">http://pinvoke.net/default.aspx/user32.SwitchToThisWindow</a> 
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, WindowShowStyle nCmdShow);

/// <summary>Enumeration of the different ways of showing a window using 
/// ShowWindow</summary>
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 to continue until you will close the application.

8.png

After the setup is complete you will see you setup output as below  

9.png

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.

推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架
新浪微博粉丝精灵,刷粉丝、刷评论、刷转发、企业商家微博营销必备工具"