49.1. Installers

The traditional way to deploy applications is to use an installer, which can typically be used to install and, in most cases, uninstall an application. Visual Studio 2008 comes with a rich user interface for building an installer for any type of .NET application. That said, there are a number of tricks to building installers for services and mobile applications. Building installers for mobile applications is covered in the next chapters.

49.1.1. Building an Installer

To build an installer with Visual Studio 2008 you need to add an additional project to the application that you want to deploy. Figure 49-1 shows the available setup and deployment project types. The Setup Project should be used for Windows Forms or service applications, and the Web Setup Project should be used for ASP.NET web sites or web services. If you want to build an installer that will be integrated into a larger installer, you may want to build a merge module. Alternatively, a CAB Project can be used to create an alternative package that can be deployed via a web browser. The Setup Wizard steps you through the process of creating the correct project for the type of application you're deploying.

Figure 49.1. Figure 49-1

In this case we are going to use the Setup Wizard to create an installer for a simple Visual Basic Windows Forms Application, Simple Application. After acknowledging the Setup Wizard splash screen, the first decision is specifying whether you want to create an installer or a redistributable package. For an installer, you need to choose between either a Windows application or a web application installer. The basic difference is that the Windows application installer places the application in the appropriate folder within Program Files, whereas the web application installer creates a virtual directory under the root folder for the specified web site. In the case of a redistributable package, the choice is between a merge module, which can be integrated into a larger installer, or a CAB file.

Regardless of the type of deployment project you are creating, the next step in the Setup Wizard is the most important because it determines the set of files to be deployed. Figure 49-2 shows the third screen in the Setup Wizard, which prompts you to select which files or project outputs will be included in the deployment project. In this case, the primary output for our Sample Application has been selected, because you want to include the main executable and any assemblies on which this executable depends. The Content Files item has also been selected, which will include any files with the build action set to Content. In the remaining step in the Setup Wizard, you can choose to add files that were not part of any existing project. For example this might include release notes, licensing information, getting started samples, or documentation and README files.

Occasionally you may choose to deploy debug symbols with your application, because this can aid you in diagnosing a failing application in production. However, it is not generally deemed good practice to do this, because you should incorporate sufficient logging or other diagnostic instrumentation for this purpose.

Figure 49.2. Figure 49-2

Once the deployment project has been created, it is added to the Solution Explorer, as shown in Figure 49-3. Although you didn't explicitly add any files or output from the SharedResources class library to the deployment project, it has been added as a calculated dependency. If the dependencies are guaranteed to exist on the target computer, they can be manually excluded from the deployment project by selecting the Exclude item from the right-click context menu. For example, if this were an add-in for another application that already has a copy of the SharedResources assembly, you could exclude that from the dependency list. The resulting installer would be smaller, and thus easier to deploy.

Figure 49.3. Figure 49-3

If the dependencies between your projects vary, it may be necessary to force a recalculation of these dependencies. You can do this by selecting Refresh Dependencies from the right-click shortcut menu on the Detected Dependencies node.

When a deployment project (DeploymentInstaller) is selected, a number of new icons appear across the top of the Solution Explorer window, as can be seen in Figure 49-3. Unlike other project types, where the project properties appear in the main editor area, clicking the first icon (Properties) opens the Property Pages dialog, as shown in Figure 49-4. This can be used to customize how the deployment module is built. This dialog can also be accessed via the Properties item on the right-click context menu for the deployment project in the Solution Explorer.

Figure 49.4. Figure 49-4

By default, the Package files property is set to "In setup file," so all executables and associated dependencies are placed into the .msi file that is created. The deployment project also creates a Setup.exe file that checks for minimum requirements, such as the presence of the .NET Framework, prior to calling the .msi file to install the application. Although the compression can be adjusted to optimize for file size, including everything into a single distributable might be an issue for large projects. An alternative, as shown in Figure 49-4, is to package the application into a series of CAB files. In this scenario, the size of the CAB file is limited to 100Kb, which will aid deployment over a slow network. Another scenario where this would be useful is if you were planning to deploy your application via CD, and your application exceeded the capacity of a single CD.

The final property on this page is the Installation URL. If you are planning to deploy your application via a web site, you can elect to package everything into a single file, in which case you do not need to specify the Installation URL because you can simply add a reference to the Setup.exe file to the appropriate web site and a user can install the application simply by clicking on the link. Alternatively, you can package your application into smaller units that can be incrementally downloaded. To do this you must specify the Installation URL from which they will be installed.

As just discussed, the default deployment project creates a Setup.exe file. The Prerequisites button opens a dialog like the one shown in Figure 49-5, where you can configure the behavior of this file. You can indicate that a setup file should not be created, in which case the application can be installed by double-clicking the .msi file. This, of course, removes the initial check to ensure that the .NET Framework has been installed.

Figure 49.5. Figure 49-5

In addition to the .NET Framework, you can also specify that other components, such as SQL Server Compact 3.5, need to be installed. These checks will be carried out, and the user prompted to install any missing components before the main installer file is invoked. Depending on how you want to deploy your application, having all the prerequisites in the same location as your application may be useful and will eliminate time spent looking for the appropriate download.

Returning to the Solution Explorer and our DeploymentInstaller project (and just to confuse matters), there is an additional Properties window for deployment projects that can be opened by selecting the appropriate project and pressing F4. This opens the standard Properties window, shown in Figure 49-6, which can be used to tailor the deployment details for the application it is installing.

Figure 49.6. Figure 49-6

The properties for the deployment project shown on this screen configure the appearance, icons, and behavior of the installation wizard. It is highly recommended that you adjust these properties so your application is easily identifiable in the Add/Remove Programs dialog, and so that the installation looks professional rather than half-finished. As you can see from Figure 49-7, some of these properties are used to tailor the installer dialog.

Figure 49.7. Figure 49-7

Once the application has been installed, some of these properties also appear in the Programs and Features dialog (Add/Remove Programs under Windows XP) accessible via the Control Panel, as shown in Figure 49-8. Here you can see the AddRemoveProgramsIcon, the ProductName, and the Manufacturer properties. More properties can be displayed by right-clicking the header bar and selecting More.

Figure 49.8. Figure 49-8

In order to test your installer, you can select the Install (and subsequently Uninstall) item from the shortcut menu that is displayed when you right-click the setup Project in the Solution Explorer. If this option is disabled, you may need to build the setup Project first.

49.1.2. Customizing the Installer

The remaining icons at the top of the Solution Explorer are used to customize what is included in the deployment package. In addition to the shortcut icons, these views of the deployment project can be accessed via the View item on the right-click context menu. Start with the File System view, which indicates where files will be installed on the target machine. By default, the primary output for a Windows application is added to the Application Folder, as shown in Figure 49-9. Selecting this node and looking at the Properties window shows that this folder has a default location of [ProgramFilesFolder][Manufacturer][ProductName]. This location is made up of three predefined installation variables: ProgramFilesFolder, Manufacturer, and ProductName, which will be evaluated and combined during installation. As you can see in Figure 49-7, the installation wizard allows users to change this location when they install the application.

Earlier you saw that the Sample Application had a dependency on the SharedResources assembly. Here this assembly has been removed from the Application Folder and placed instead in the Global Assembly Cache Folder. When this application is installed, the main executable will be installed in the relevant directory under Program Files, but the SharedResources assembly will be installed in the Global Assembly Cache so it is available to any .NET application. To achieve this, you first need to create the new folder in the File System view by selecting the Global Assembly Cache Folder from the Add Special Folder item on the right-click context menu. You can install files to a number of other special folders as part of the installer. The next step is to move the SharedResources assembly to the Global Assembly Cache by selecting the assembly in the right pane of the File System view and changing the Folder property from Application Folder to Global Assembly Cache Folder. Alternatively you can drag the item from the Application Folder to the Global Assembly Cache Folder.

Figure 49.9. Figure 49-9

In addition to installing files on the target machine, you can also add keys to the registry. Some developers argue for and other developers argue against the use of the registry. Although it can provide a convenient store for per-user configuration information, the new application settings with user scope are an alternative that makes them easier to manage. The Registry view, as shown in Figure 49-10, can be used to add registry keys and values. To add a new key, right-click the appropriate node in the Registry tree and select Add Key from the context menu. To add a new value, select the appropriate key in the Registry tree and select the type of value from the New item on the right-click context menu off the right pane shown in Figure 49-10. The Name and Value can then be set using the Properties window.

Figure 49.10. Figure 49-10

Figure 49-11 shows the File Types view of the deployment project. This view is used to add file extensions that should be installed. For example, in this case you are installing the extension .nic. You can specify an icon for this type of file as well as specify the executable that should be called for this file type. In most cases this will be the primary output for your application. To add a new file type, right-click the root node of the File Types tree and select Add File Types from the context menu. This creates a node for the new file type and for the default action (in bold) for that file type. For the .nic extension, the default action is Open, and it can be executed by double-clicking a file of the appropriate file type. The Open action also appears, again in bold, in the right-click context menu for a file with the .nic extension. Other actions can be added for this file type by selecting Add Action from the right-click context menu for the file type. An alternative action can be made the default by selecting Set as Default from that action's context menu. You can change the order in which the actions appear in the context menu by moving the action up or down in the tree.

Figure 49.11. Figure 49-11

.NET applications can be autonomous so that their list of dependencies may only contain the .NET Framework. However, web applications require IIS, and more complex applications may require SQL Server to be installed. You can check for these dependencies by using a launch condition via the view shown in Figure 49-12. By default, the .NET Framework is added to this launch condition. Previously you saw that Setup.exe also did a check for the .NET Framework and would install it if it was not found. Launch conditions are embedded in the .msi file and, unlike conditions in the Setup.exe file, are validated even if the .msi file is installed directly. The only limitation is that the launch conditions only provide a warning message and a URL reference for more information.

Figure 49.12. Figure 49-12

The tree in the left pane of Figure 49-12 is actually split into two sections. The top half of the tree is used to specify searches to be performed on the target machine. Searches can be carried out for files, for installed components or applications, and for registry values. Properties for a file search include the search folder, version and modification dates, and file size. To search for an installed component, you need to know the Component ID, which is embedded in the .msi file used to install the product. This information can be retrieved using a product such as MSI Spy, which is included in the Windows Installer SDK that can be downloaded from the Microsoft web site (www.microsoft.com/downloads/). A registry search requires properties indicating the key, name, and value to search for. In each of these cases the search needs to be assigned a Property identifier. If the search is successful, the installer property with that identifier is True.

The Property identifiers assigned to searches on the target machine can be used by a launch condition in the lower half of the tree. As you can see in Figure 49-12, there are conditions that check for the .NET Framework, as well as a custom launch condition. The Condition property is set to a logical AND operation across the three search results. If any of the searches fails, the associated property identifier is replaced with False, making the whole logical expression false. This will prevent the application from installing, and a warning message will be displayed.

Note that some other views have a Condition property for some of the tree nodes. For example, in the File System view, each file or output has a Condition property that can be specified. If this condition fails, the file is not installed on the target machine. In each of these cases the syntax of the Condition property must be valid for the MsiEvaluateCondition function that is called as part of the installation process. This function accepts standard comparison operators, such as equals (=), not equals (<>), less than (>), and greater than (<), as well as Boolean operators NOT, AND, OR, and XOR. There are also some predefined Windows installer properties that can be included in the condition property. The following is a subset of the full list, which you can find in the documentation for the Windows Installer SDK:

  • ComputerName: Target computer name

  • VersionNT: Version of Windows on the target computer

  • ServicePackLevel: The service pack that has been installed

  • LogonUser: The username of the current user

  • AdminUser: Whether the current user has administrative privileges

  • COMPANYNAME: The company name, as specified in the installation wizard

  • USERNAME: The username, as specified in the installation wizard

One of the main reasons for creating an installer is to make the process of deploying an application much smoother. To do this you need to create a simple user interface into which an end user can specify values. This might be the installation directory or other parameters that are required to configure the application. Clearly, the fewer steps in the installer the easier the application will be to install. However, it can be better to prompt for information during the installation than for the user to later sit wondering why the application is not working. The User Interface view, shown in Figure 49-13, enables you to customize the screens that the user sees as part of the installation process.

Figure 49.13. Figure 49-13

Two user interfaces are defined in this view: the standard installation and an Administrative install (not visible). Both processes follow the same structure: Start, where you typically collect information from the user before installing the product; Progress, used for providing a visual cue as to the installation's progress; and End, at which point the user is presented with a summary of the installation. The Administrative install is typically used when a network setup is required, and can be invoked by calling msiexec with the /a flag.

You can customize either of the installation processes by adding and/or removing dialogs from the user interface tree. To add a new dialog, right-click any of the three stages in the installation process and select Add Dialog from the context menu. This displays a list of the predefined dialogs from which you can choose. Each of the dialogs has a different layout; some are used for accepting user input and others are used to display information to the user. Input controls are allocated a property identifier so that the value entered during the installation process can be used later in the process. For example, a checkbox might be used to indicate whether the tools for a product should be installed. A condition could be placed on an output in the File System view so the tools are installed only if the checkbox is enabled.

49.1.3. Adding Custom Actions

It is often necessary to perform some actions either before or after the application is installed. To do this, you can create a custom action to be executed as part of the install or uninstall process. Adding a custom action entails creating the code to be executed and linking the appropriate installer event so that the code is executed. Custom actions use an event model similar to what Windows components use to link the code that you write to the appropriate installer event. To add a custom action to an installer event, you need to create a class that inherits from the Installer base class. This base class exposes a number of events for which you can write event handlers. Because writing custom installer actions is quite a common task, the Add New Item dialog includes an Installer Class template item under the General node. The new class (added to the SharedResources project) opens using the component designer, as shown in Figure 49-14.

Figure 49.14. Figure 49-14

From the Events tab of the Properties window, select the installer event for which you want to add an event handler. If no event handler exists, a new event handler will be created and opened in the code window. The following code is automatically generated when an event handler is created. A simple message box is inserted to notify the user that the AfterInstall event handler has completed:

Imports System.ComponentModel
Imports System.Configuration.Install

Public Class InstallerActions

    Public Sub New()
        MyBase.New()

        InitializeComponent()
   End Sub

    Private Sub InstallerActions_AfterInstall(ByVal sender As Object, _
                                           ByVal e As InstallEventArgs) _
                                                            Handles Me.AfterInstall
        MessageBox.Show("Installation process completed!")
    End Sub
End Class

As with forms and other components, the rest of this class is stored in a designer class file where you can see that the partial InstallerActions class inherits from the Installer class and is attributed with the RunInstaller attribute. This combination ensures that this class is given the opportunity to handle events raised by the installer.

The InstallerActions class you have just created was added to the SharedResources assembly. For the events to be wired up to the InstallerActions class, the installer needs to know that there is a class that contains custom actions. To make this association, add the SharedResources assembly to the Custom Actions view for the deployment project by right-clicking any of the nodes shown in Figure 49-15 and selecting Add Custom Action from the context menu. In this case, you want to wire up the SharedResources. In Figure 49-15, this association has been made only for the Install action. If you want to wire up the Custom Action class for all of the actions, you need to add the custom action to the root Custom Actions node.

Figure 49.15. Figure 49-15

To complete this discussion, understand that it is important to be able to pass information collected from the user during the Start phase of the installation process to the custom action. Unfortunately, because the custom action is invoked after the installer has finished, you have to use a special channel to pass installer properties to the custom action event handler. In the Custom Actions view (refer to Figure 49-15), select Properties Window from the right-click context menu for the Primary output node. The CustomActionData property is used to define name/value pairs that will be sent through to the custom installer. For example, you might have /PhoneNumber= "+1 425 001 0001", in which case you can access this value in the event handler as follows:

Private Sub CustomActions_AfterInstall(ByVal sender As Object, _
                                       ByVal e As InstallEventArgs) _
                                                            Handles Me.AfterInstall
    MessageBox.Show("Number: " & Me.Context.Parameters("PhoneNumber").ToString)
End Sub

Of course, hard-coded values are not a good idea and it would be better if this were a user-specified value. To use a property defined in the installer user interface, replace the specified string with the property identifier in square brackets. For example, /PhoneNumber=[TXTPHONENUMBER] would include the text in the TXTPHONENUMBER text box.

49.1.4. Service Installer

You can create an installer for a Windows Service the same way you would create an installer for a Windows application. However, a Windows Service installer not only needs to install the files into the appropriate location, it also needs to register the service so it appears in the services list. You can do this using the ServiceInstaller and ServiceProcessInstaller components from the System. ServiceProcess namespace (you'll probably need to add these to the Toolbox, because they are not visible by default). An instance of each of these components needs to be dragged onto the designer surface of a custom installer, as shown in Figure 49-16.

Figure 49.16. Figure 49-16

The ServiceInstaller class is used to specify the display name (the name of the service as it will appear in the Windows services list), the service name (the name of the service class that will be executed when the service is run), and the startup type (whether it is manually started or automatically started when Windows starts up). For each service you want to install you need to create a separate instance of the ServiceInstaller class, specifying a different display and service name. Only a single instance of the ServiceProcessInstaller class is required, which is used to specify the account information that the service(s) will run as. In the following example, the InstallerforService constructor specifies that the class Service1 should be installed as a service, and that it should automatically start using the NetworkService account:

Imports System.ComponentModel
Imports System.Configuration.Install

Public Class InstallerForService

    Private Const cServiceDisplayName As String = "My Generic Service"
    Private Const cStartAfterInstall As String = "STARTAFTERINSTALL"
    Private Const cNETProcessName As String = "Net"
    Private Const cNETStart As String = "Start ""{0}"""
    Private Const cNETWaitTimeout As Integer = 5000
    Private Const cNETWaitError As String = "WARNING: Process took longer than
expected to start, it may need to be restarted manually"

    Public Sub New()
        MyBase.New()

        'This call is required by the Component Designer.
        InitializeComponent()

        'Add initialization code after the call to InitializeComponent
        Me.ServiceInstaller1.DisplayName = cServiceDisplayName
        Me.ServiceInstaller1.ServiceName = GetType(Service1).ToString
        Me.ServiceInstaller1.StartType = ServiceProcess.ServiceStartMode.Automatic

Me.ServiceProcessInstaller1.Account = _
                                       ServiceProcess.ServiceAccount.NetworkService
    End Sub

    Private Sub InstallerForService_AfterInstall(ByVal sender As Object, _
                                                 ByVal e As InstallEventArgs) _
                                                            Handles Me.AfterInstall
        'Retrieve the user input (unchecked box can return empty string,
        'whereas a checked box will return "1")
        Dim startString As String = Me.Context.Parameters(cStartAfterInstall)
        If startString = "" Then Return
        Dim shouldStart As Boolean = CBool(startString)
        If Not shouldStart Then Return

        'Service should be started, so create a process and wait for completion
        Dim proc As Process = Process.Start(CreateNetStartProcessInfo)
        If Not proc.WaitForExit(cNETWaitTimeout) Then MsgBox(cNETWaitError)

    End Sub

    Private Function CreateNetStartProcessInfo() As ProcessStartInfo
        Dim x As New ProcessStartInfo(cNETProcessName, _
                                     String.Format(cNETStart, cServiceDisplayName))
        x.WindowStyle = ProcessWindowStyle.Hidden
        Return x
    End Function
End Class

Also included in this listing is an event handler for the AfterInstall event that is used to start the service on completion of the installation process. By default, even when the startup is set to automatic, the service will not be started by the installer. However, when uninstalling the service, the installer does attempt to stop the service.

The user interface for this deployment project includes a Checkboxes (A) dialog using the User Interface view for the project. Refer to Figure 49-13 for a view of the default user interface. Right-click the Start node and select Add Dialog from the context menu. Highlight the dialog titled Checkboxes (A) from the Add Dialog window and click OK. This will insert the new dialog at the end of the installation process. The order of the dialogs can be adjusted using the Move Up/Down items from the right-click context menu on the nodes in the User Interface window.

Selecting Properties Window from the right-click context menu on the new dialog will bring up the Properties window. Set the property identifier for Checkbox1 to STARTAFTERINSTALL and then set the Visible property for the remaining checkboxes to false. As discussed earlier in the chapter, you also needed to add /STARTAFTERINSTALL=[STARTAFTERINSTALL] to the CustomActionData property for the assembly in the Custom Actions view of the deployment project. With this user input you can decide whether to start the service when the installer completes.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.144.248