Click here to Skip to main content
15,867,686 members
Articles / DevOps / Deployment

Deployment, because WPF desktop applications aren't dead yet

Rate me:
Please Sign up or sign in to vote.
5.00/5 (17 votes)
18 Dec 2015CPOL50 min read 68.8K   540   33   6
This article describes how you can deploy your application using ClickOnce and Windows Installer technology.

Available downloads:

Contents

Introduction

Recently I created a PDF split and merge tool that can be found here. After I finished making the PDF tool, I wanted to make it readily available using a deployment kit. In this article I am going to create two deployment kits for this PDF tool using ClickOnce technology and the classic Windows Installer. The resulting ClickOnce deployment kit can be started from my Github repository using this link. The classic Windows Installer kit can be found on top of this article.

Background

After you have finished creating your WPF application, it must reach your customers. There are several strategies that can be used to accomplish this.

  • ClickOnce deployment: ClickOnce technology enables you to create self-updating applications that can be installed easily using a web browser. One drawback of this technology is that it does not allow for much configuration of the setup wizard.
  • Web browser deployment (XAML Browser Applications, XBAPs): This technology enables you to run applications in the web browser without installing them on the client computer. One disadvantage of this technology is that XBAPs usually run with a very limited set of privileges. However, this is configurable.
  • Traditional setup programs: For more detailed deployments, Windows Installer technology provides a highly configurable deployment environment. Currently there are two Installer technologies widely used. The first one is InstallShield, which can be downloaded here. The second one is the classic setup project template in Visual Studio, which can be downloaded here.

In this article I will focus on two deployment strategies, which are the ClickOnce deployment and the classic Windows Installer.

ClickOnce deployment

Introduction

As mentioned in the introduction, applications can be deployed with either the Windows Installer or ClickOnce technology. For WPF applications, ClickOnce is considered the preferred technology. It is a powerful and easy to use deployment technology that offers an easy install experience for the end user when properly configured.

ClickOnce technology works as follows: The developer uses Visual Studio to publish the ClickOnce application to a web server, after which the application user can surf to the generated web page called publish.htm. This web page provides a link to install the application. When the user clicks this link, the application is downloaded and installed, as illustrated in figure 1. From a security perspective, ClickOnce applications use certificates to verify authenticity of the application publisher. 

Image 1

Figure 1: Clickonce deployment overview.

You can configure a ClickOnce deployment in Visual Studio using the Publish page. To open the Publish page, right click the project name in Solution Explorer, choose Properties, and then select the Publish page. In the Publish page, you can specify the publishing location of your application in the "Publishing Folder Location" combo box. The publishing location is the location where users will go to install the application. In addition to this location, you can specify an "Installation Folder URL". Specifying an "Installation Folder URL" is usually done when you want to deploy your application to the "Publishing Folder Location" first and then manually copy the files to their final installation location from where users can download the application.

Image 2

Figure 2: Publish page used to configure ClickOnce deployment properties.

In case of the PDF tool, the application uses the default publish directory to publish the application, which is a subfolder of the project folder. Using Github windows desktop, the local code of the PDF tool is synchronised with the Github repository. This step results in copying the local publish directory to the installation location from where users can download the application.

https://raw.githubusercontent.com/Mohamed1976/EasySplitAndMergePdf/master/EasySplitAndMergePdf/publish/ 

Code snippet 1: Installation URL of the PDF split and merge tool. 

As you can seen in the Publish page in figure 2, you can configure your application to be available online only or to also be available offline. By selecting "The Application Is Available Online Only", the application must be launched from the ClickOnce web page. The application is cached locally for optimum performance, but users will not be able to run the application unless they are able to connect to the site where the application was published. This ensures that application users always run the most recent available version of the application. Alternatively, you can select "The Application Is Available Offline As Well" to make the application available both online and offline. In this case, the application is copied to the local computer and added to the Start menu. The application can be removed or restored to its previous version using the Add/Remove menu in the Control Panel.

Furthermore, you can specify the "Publish Version" in the Publish page. It should be noted that the "Publish Version" specifies the publish version of the application, this should not be confused with the application build version. When "Automatically increment revision with each publish" is checked, the "Publish Version" will be incremented automatically when you publish your application.

Configuring publish options

You can specify information about your ClickOnce application by clicking the Options button in the Publish page. This opens the "Publish Options" dialog box, which contains four pages as can be seen in figure 3.

Image 3

Figure 3: Publish Options dialog box used to configure deployment settings of your ClickOnce application.

The topmost page in the Publish Options dialog box is the Description page. Most settings in the this page are self-explanatory, you can set "Publisher name", "Product name", and the Support URL. When the ClickOnce application is installed for offline use, an entry is made in the All Programs folder in the Start menu. The entry appears under "Product name", which in turn appears under "Publisher name", so these values must be set in the Description page. 

Image 4

Figure 4: PDF tool shortcut appears in the Start menu under the Publisher's name.

In the Deployment page you can specify the "Deployment web page", which is generated when the application is published. When you create a custom deployment web page, you should uncheck "Automatically generate deployment web page after every publish" to avoid that your custom web page is overwritten each time the application is published. Instead of generate a new deployment web page, you modify your custom deployment web page so that it points to the new published application. Furthermore, if you intend to sign your application manually using Mage.exe, you can uncheck the "Use .deploy" file extension to avoid errors generated by Mage.exe. If you are using an IIS web server to publish your ClickOnce application, the .deploy file extension is used by the IIS web server to lockdown files to prevent others from modifying them.

Image 5

Figure 5: Deployment page used to configure deployment settings of your ClickOnce application.

Configuring application files and prerequisites

When publishing a ClickOnce application, all data files in the Visual Studio project are deployed along with the application files. In some cases, you may not want to publish all files included in the Visual Studio project, or you may want to install certain files based on conditions. Visual Studio provides the capabilities to exclude files, mark files as data files or prerequisites, and create groups of files for conditional installation. Details on how to do this can be found here. The use of file groups is especially useful when you want to deploy satellite assemblies based on the culture settings of the client computer, an example on how to use file groups to deploy culture-specific satellite assemblies can be found in this section.  

Image 6

Figure 6: Application Files dialog box used to configure files that are included in the installation.

By clicking the Prerequisites button in the Publish page you can configure the prerequisites. In the Prerequisites dialog box, you can choose package that must be installed on the client computer before your project will deploy. All applications created with Visual Studio 2015 require .NET Framework 4.5 to run. The .NET Framework is configured to be installed by default, as can be seen in the prerequisites list below. When installing the ClickOnce application, the ClickOnce Bootstrapper checks to see if .NET Framework 4.5 is installed, if not, it installs it, thereby eliminating the extra steps needed to separately download and install the .NET Framework on the client computers.

Image 7

Figure 7: Prerequisites dialog box used to configure prerequisites needed to run ClickOnce application.

Configuring updates

One of the main advantages of the ClickOnce technology is the way it supports updating. A ClickOnce application is able to check whether updates of the application are available. If available, the new version of the application is downloaded and used. For efficiency, only those files that have changed are downloaded. 

ClickOnce enables you to configure how your application checks for updates. There are three strategies that you can use to check for updates:

  • Checking for updates on application startup.
  • Checking for updates after application startup using a background thread.
  • Programmatically check for updates using the ApplicationDeployment class.  

Programmatically checking for updates is discussed in the next section. In this section I will discuss how you can configure update checking using the Application Updates dialog box. This dialog box is displayed when you click on the Updates button in the Publish page, shown in figure 2. To enable update checking, select the check box labeled "The application should check for updates" in the Application Updates dialog box, shown in figure 8. Doing so enables the other options in the dialog box.

Image 8

Figure 8: Application Updates dialog box used to configure how your application checks for updates. 

You can specify whether the ClickOnce application should check for updates before startup or after. If you select "Before the application starts", the application checks for new updates every time the application starts. This ensures that the user always runs the most recent version of the application, one drawback of this setting is that it can slowdown the performance of the application at startup.

If you select "After the application starts", the application will attempt to read the deployment manifest file in the background while the application is running. If an update is available, the next time that the user runs the application, he will be prompted to download and install the update. This strategy works best for large updates that require lengthy downloads or for slow network connections.

As shown in the Application Updates dialog box (figure 8), you can specify a minimum required version for the application. This setting forces the installation of an update. When the application detects that an update is available and that it has been set to the minimum required version, the update is installed and cannot be skipped by the user. In addition the user cannot rollback to the previous version using the Add/Remove menu in the Control Panel. The minimum required version is usefull to ensure that the application used by different users is up to date, especially if the previous version of the application contained a serious bug.

Furthermore, as can be seen in the Application Updates dialog box (figure 8), you can specify a location for updates if your updates are hosted in a location other than the install location. This is particularly useful when your ClickOnce application is initially installed from a CD/DVD or a file share, but the application must check for updates on the web. Using the specified update location, your application can update itself from the web after its initial installation.

Programmatically check for updates

Alternatively to scheduling updates, you can programmatically check for updates. By using the ApplicationDeployment class you can check for updates from code. After obtaining a reference to the current deployment, you can check for updates by using the CheckForUpdate method, if updates are available, you can download and install the update using the Update method. 

C#
System.Deployment.Application.ApplicationDeployment deployment = 
    System.Deployment.Application.ApplicationDeployment.CurrentDeployment;
if (deployment.CheckForUpdate())
{
    deployment.Update();
}

Code snippet 2: Programmatically check for updates using synchronous calls .

The CheckForUpdate and Update methods also have asynchronous counterparts, called CheckForUpdateAsync and UpdateAsync, respectively, which can perform updates asynchronously. When CheckForUpdateAsync returns, it raises the CheckForUpdateCompleted event. By handling this event, you can check the CheckForUpdateCompletedEventArgs argument in the event handler to determine whether an update is available. If an update is available, you can call UpdateAsync to download and install the update asynchronously. Both synchronous and asynchronous updates are applied to the application after application restart.

C#
System.Deployment.Application.ApplicationDeployment deployment;

public UpdateApplication
{
    InitializeComponent();
    deployment = 
        System.Deployment.Application.ApplicationDeployment.CurrentDeployment;
    deployment.CheckForUpdateCompleted += UpdateCheckCompleted;
}

public void UpdateApp()
{
    deployment.CheckForUpdateAsync();
}

private void UpdateCheckCompleted(object sender, 
    System.Deployment.Application.
    CheckForUpdateCompletedEventArgs e)
{
    if (e.UpdateAvailable)
    {
        deployment.UpdateAsync();
    }
}    

Code snippet 3: Programmatically check for updates using asynchronous calls.

Publish wizard

As can be seen in the Publish page (figure 2), you can also configure ClickOnce deployment settings using the Publish Wizard. This Wizard allows you to configure the following properties: 

  • Publishing Folder Location: Location where Visual Studio will copy the files (local computer, network file share, or FTP server).
  • Installation Folder Location: Location where users will install from (network file share, Web site, or CD/DVD).
  • Online or Offline availability: This setting determines whether the users can access the application online only or that the user can access the application offline as well.

Details on how to use the Publish Wizard can be found here.

Sign application

You can use Visual Studio to sign your application. When your application is digitally signed, it ensures that any modification made to the application files after signing will be detected by the receiver. To sign your application select the Signing page, as shown in figure 9. In order to enable certificate signing in Visual Studio, check the "Sign The ClickOnce manifests" check box. You can then choose a certificate, either from your local certificate store or from a particular file, in my case I selected a private key (*.pfx file) that I created for application signing. Note that in general most companies would just buy their application signing certificates from a trusted third party certificate authority such as Verisign, which is already a Root Certificate Authority in most computers.

Image 9

Figure 9: Signing page used to sign your application.

As mentioned earlier, in order to sign the PDF split and merge tool, I created a self-signed root certificate and an application signing certificate. The certificates are created using the Windows Makecert.exe tool, as shown in code snippet 4. Details on how to create your own certificates can be found here.

#Creating a self signed root CA certificate
makecert.exe -n "CN=Moccasoft,OU=(c) 2015 Moccasoft - The name you can trust,O=Moccasoft,C=NL"
-r -pe -a sha512 -len 4096 -cy authority -sv CARootMoccasoft.pvk CARootMoccasoft.cer

#Copy the public(.cer) and private(.pvk) key into a .pfx (personal information exchange) file.
pvk2pfx.exe -pvk CARootMoccasoft.pvk -spc CARootMoccasoft.cer -pfx CARootMoccasoft.pfx -po pfxPassword

#Create certificate to sign applications.
makecert.exe -n "CN=Moccasoft" -iv CARootMoccasoft.pvk  -ic CARootMoccasoft.cer -pe -a sha512 -len 4096 -b 01/11/2015 -e 01/11/2017 -sky exchange -sv ApplicationSigning.pvk ApplicationSigning.cer 

#Copy the public(.cer) and private key(.pvk) into a .pfx (personal information exchange) file.
pvk2pfx.exe -pvk ApplicationSigning.pvk -spc ApplicationSigning.cer -pfx ApplicationSigning.pfx -po pfxPassword

*) Note that the absolute paths to the files are omitted for clarity.
*) pfxPassword is your password to protect the pfx file.
*) During the creation of the private key (.pvk), you will be prompted to enter a password to protect this file. 

Code snippet 4: Create certificates using Makecert.exe tool.

This self-signed root certificate can be downloaded here  and you can verify its authenticity by check its thumbprint, which is sha1 = ‎43 7c 1c 86 e0 d1 16 21 1a 2e cb 6e bd 42 ed 09 56 38 a0 ed. After installation of this root certificate, you can use it to verify the publisher of the PDF split and merge tool. Furthermore you can install the application signing certificate as a trusted publisher. This application signing certificate can be downloaded here  and you can verify its authenticity by check its thumbprint, which is sha1 = ‎‎9f 16 d5 f3 b9 5b 0a 01 55 59 a2 8c 7c da 3d 72 3b 48 bc af. After installation of the application signing certificate as a trusted publisher, you can install the ClickOnce PDF tool without being prompted.

In the Signing page (figure 9) you can specify a "Timestamp server URL" to timestamp the application release. The main benefit of timestamping your application is that it extends application trust beyond the validity period of your application signing certificate. If you do not timestamp your application, the application signature will be treated as invalid when your application signing certificate expires. In contrast a signed and timestamped application remains valid indefinitely, as long as the verifiable timestamp (issued by a trusted timestamp authority) marks that the application was signed during the validity period of the signing certificate. In the event that a signing certificate must be revoked due to a compromise, signed applications with a timestamp before the revocation date will remain to be valid.

Sign application using Mage.exe

Any application deployed through ClickOnce will have a deployment manifest and an application manifest. If you decide to manually sign your application, you need to sign these two manifest files using the command-line tool Manifest Generation and Editing Tool (Mage.exe).

Depending on the type of application, the deployment manifest can have the following file extensions:

  • .application: Applications such as Winforms or Wpf application
  • .vsto: Office solution.
  • .xbap: Xaml browser application (xbap) 

The application manifest will always have the .manifest extension. Both the deployment manifest and the application manifest can be found in the Publish directory, the structure of the Publish directory is shown below. As can be seen in figure 10, the most recent application deployment manifest is stored in two locations, which are the root Publish directory and the most recent Application Files directory. The application manifest is only stored in the Application Files directory.

Image 10

Figure 10: Structure of the publish directory.

As discussed in the previous section, Visual Studio is capable of signing the deployment manifest and the application manifest. So the question arises, why manually sign the files. Manually signing is needed when you want to change something about your application after it has been published. For example you may want to obfuscate your executable or you may want to change a particular file in your release like the .config file.

Image 11

Figure 11: The security link between application files, the application manifest and the deployment manifest.

As mentioned earlier, ClickOnce application signing involves signing two manifest files in a particular sequence. This is because the deployment manifest contains the hash value of the application manifest, as illustrated in figure 11. Hence if the application manifest changes, the hash value included in the deployment manifest will no longer be valid. Similarly, the application manifest includes all the hash values of the application files. So in case one of the application files is changed after the application has been published, you need to manually resign the application manifest followed by the deployment manifest, as illustrated in code snippet 5. After the deployment manifest in the Publish directory is signed, you need copy this file to the most recent Application Files directory.

mage -update EasySplitAndMergePdf.exe.manifest -certfile ApplicationSigning.pfx -Password PfxPassword   -TimestampUri http://timestamp.digicert.com
EasySplitAndMergePdf.exe.manifest successfully signed

mage -update EasySplitAndMergePdf.application -appmanifest EasySplitAndMergePdf.exe.manifest -certfile ApplicationSigning.pfx" -Password PfxPassword -TimestampUri http://timestamp.digicert.com
EasySplitAndMergePdf.application successfully signed

*) Note that the absolute paths to the files are omitted for clarity'.

Code snippet 5: Sign application using Mage.exe.

During the signing process, you might see some errors about files not found. This could happen if you are using the .deploy extensions for you application files. To avoid this, you must rename the .deploy extensions back to their original extensions and then run the update command with mage. Once you have updated the application manifest and deployment manifest, you must rename the application files back with the .deploy extension. Alternatively you can uncheck the "Use .deploy" file extension in the Deployment page, as described in this section.   

Security

In the discussion about security, you need to make a distinction between code access security (CAS) and user-based security. By design, the privileges of a ClickOnce application are limited to the privileges of its invoker. Therefore, for ClickOnce applications, the only valid value for the requestedExecutionLevel element is asInvoker. The consequence of all of this is that a ClickOnce application cannot be run with administrator privileges, unless the application invoker has system administrator privileges.

XML
<requestedExecutionLevel level="asInvoker" uiAccess="false" />

Code snippet 6: The only valid requestedExecutionLevel for a ClickOnce deployment is asInvoker.

A ClickOnce application can have one of the two possible permissions sets, full trust or partial trust. Full trust means that you do not want your application constrained by CAS at runtime on the client computer. Hence a full trust application can do anything that the user-based security allows it to do. In contrast, a partial trust application is granted less privileges than its invoker. The principle of partial trust means that the application developer requests only those privileges which are necessary for the application to perform its tasks. When a partial trust application is correctly configured, it cannot be used to exploit the rest of the client computer.

There are two partial trust zones with predefined permissions sets, the intranet and internet zone. The permissions set of the internet zone is relatively more restrictive than that of the intranet zone, as illustrated in figure 12. In this article you can see which permissions are requested for the internet and intranet zone. In addition to these two predefined permissions sets, you can define your own custom permissions set. Details on how to do this will be discussed later in this section.

Image 12

Figure 12: ClickOnce application permissions sets in different zones.

By default, a ClickOnce application requests full trust permissions when it is installed or run on a client computer. The reason for this is that a ClickOnce application requires full trust permissions to create a window (unmanaged code permission). In contrast to a window based ClickOnce application, a XBAP can run in a partial trust environment because these type of applications are hosted and run inside a web browser.

The Security page shown in figure 13 can be used to configure CAS settings for your ClickOnce application. If you want to configure your XBAP to run under partial trust, you need to check the "This is a partial trust application" radio button. By selecting this radio button, the "Zone your application will be installed from" drop down list is enabled, which allows you to select one of the predefined permissions sets or a custom permissions set. If you select custom, you can configure your custom permissions set by clicking the "Edit Permissions XML" button, which opens the application manifest. You can then add IPermission elements to this manifest to define your custom permissions set.

 Image 13

Figure 13: The Security page can be used to configure code access security settings.

In case of a full trust application, the application manifest states that the application has no restrictions. The application manifest indicates a full trust application by the value of the Unrestricted attribute of the PermissionSet element, which is set to True for a full trust application, as shown below.

XML
<trustInfo>
    <security>
        <applicationRequestMinimum>
            <PermissionSet Unrestricted="true" ID="Custom" SameSite="site" />
            <defaultAssemblyRequest permissionSetReference="Custom" />
         </applicationRequestMinimum>
    </security>
</trustInfo>   

Code snippet 7Full trust application have their Unrestricted attribute set to true.

For a partial trust application, the Unrestricted attribute is not set and each requested permission is declared separately, as shown below.

XML
<trustInfo>
    <security>
          <applicationRequestMinimum>
                <PermissionSet class="System.Security.NamedPermissionSet">
                  <IPermission class="System.Security.Permissions.EnvironmentPermission" />
                  <IPermission class="System.Security.Permissions.FileDialogPermission"/>
                  <IPermission class="System.Security.Permissions.IsolatedStorageFilePermission"/>
                  <IPermission class="System.Drawing.Printing.PrintingPermission"/>                  
                </PermissionSet>
            <defaultAssemblyRequest permissionSetReference="Custom" />
          </applicationRequestMinimum>
    </security>
</trustInfo>
*) Note that several IPermission attributes are omitted for clarity.

Code snippet 8For a partial trust application, each requested permission is declared separately.

When defining a custom permissions set, you can use the ToXml method of a Permission class to generate the required IPermission element needed to populate the PermissionSet of the application manifest. After the IPermission element has been exported to file, you can copy it to the PermissionSet of the application manifest. The code below shows the use of the ToXml method to generate the IPermission element.

C#
FileIOPermission fileIOPermission = new FileIOPermission(PermissionState.Unrestricted);
SecurityElement securityElement = fileIOPermission.ToXml();
StreamWriter streamWriter = new StreamWriter("@C:\permissionXml.txt");
streamWriter.Write(securityElement.ToString());
streamWriter.Flush();
streamWriter.Close();

Code snippet 9A permission set exported to file using the ToXml method.

XML
<IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=4.0.0.0,                         Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>

Code snippet 10: The exported permission set that can be added to the application manifest.

When writing XBAPs that are deployed in a partial trust  environment, you need to avoid using the WPF features listed in this article. For XBAPs that might be run in both partial trust and full trust, you can use CAS to check at runtime whether a specific operation is permitted. If the requested operation is not permitted, your application can recover in a controlled manner from that attempt rather than crash. The following example demonstrates creating a demand for FileIOPermission to determine whether it is safe to write to a file. If the application does not have permission to write to the specified file, an exception is thrown.

C#
try
{
    FileIOPermission fileIOPermission =
        new FileIOPermission(FileIOPermissionAccess.AllAccess, @"C:\security.txt");
    fileIOPermission.Demand();
    MessageBox.Show("fileIOPermission.Demand() succeeded");
    // proceed with writing the file
}
catch(Exception ex)
{
    MessageBox.Show("fileIOPermission.Demand() failed: " + ex.Message);
    // recover from being unable to write the file
}

Code snippet 11Check at run time whether a specific operation is permitted.

A final note that I want to make, is that ClickOnce security settings will also be applied to the Visual Studio debugger. Therefore your debug runtime environment will have the same security restrictions as your target environment. This is helpful in debugging and fixing problems that would otherwise only occur in the deployed environment.

Deployment of a localized ClickOnce application

Localization is the process of making your application ready for different cultures. This process involves translating application resources into other cultures. A localized application is culture aware and displays the user interface (UI) in a language  based on the culture settings of the client computer. Localization of a ClickOnce application results in the creation of one or more satellite assemblies. Each assembly contains UI strings, images, and other resources specific to a given culture. When a ClickOnce application is deployed, its corresponding satellite assemblies need to be deployed along with it. This can be accomplished in three ways:

  • Include all satellite assemblies in a single ClickOnce deployment: This option allows you to deploy a single ClickOnce deployment that includes all satellite assemblies. At the end of this section, I will discuss an example application that illustrates this approach. The first step in the deployment process is to set the culture of your application to neutral in Visual Studio after which you need to publish your ClickOnce application. Next, copy all the satellite assemblies to the most recent Application Files directory, which is a subfolder of the Publish directory. You then need to manually add all satellite assemblies to the application manifest using MageUI.exe. Open the application manifest in MageUI.exe and select the Files page, browse to the satellite assemblies and add them to the application manifest using the Populate button. Next, save the application manifest and resign the application manifest followed by the deployment manifest. The benefit of this deployment approach is that it simplifies deployment by creating a single ClickOnce deployment kit. When the user installs the ClickOnce application, all satellite assemblies are downloaded to the client computer. At runtime, the appropriate satellite assembly is used, depending on the culture settings of the client computer. A disadvantage of this approach is that it downloads all satellite assemblies whenever the application is installed or updated on a client computer, which could affect performance during application update.
  • Download satellite assemblies on demand: If you decide to include all satellite assemblies in a single deployment,  you can improve performance by using on demand downloading, which enables you to mark assemblies as optional. The optional marked assemblies will not be downloaded when the application is installed or updated. You can install the assemblies when you need them by calling the DownloadFileGroup method in the ApplicationDeployment class. To enable on demand downloading you need to manually add all the satellite assemblies to your deployment using MageUI.exe in a similar way as discussed in the previous point. After you have added all the satellite assemblies to the application manifest using MageUI.exe, you need to mark all the satellite assemblies, except the Neutral Language assembly as optional in the Files page of MageUI.exe. In addition you need to set the satellite assembly group name to its corresponding culture as shown below (figure 14). The application code that enables on demand downloading of satellite assemblies is shown in code snippet 12.

Image 14

Figure 14: Using MageUI.exe to add optional satellite assemblies to the application manifest.

C#
protected override void OnStartup(StartupEventArgs e)
{
    try
    {
        GetSatelliteAssemblies(Thread.CurrentThread.CurrentCulture.ToString());
        MainWindow mainView = new MainWindow();
        mainView.Show();
    }
    catch (Exception ex)
    {
        Debug.WriteLine(string.Format("OnStartup Exception: {0}", ex.ToString()));
    }
}

static void GetSatelliteAssemblies(string groupName)
{
    if (ApplicationDeployment.IsNetworkDeployed)
    {
        ApplicationDeployment deploy = ApplicationDeployment.CurrentDeployment;
        if (deploy.IsFirstRun)
        {
            try
            {
                deploy.DownloadFileGroup(groupName);
            }
            catch (DeploymentException ex)
            {
                //Logs that there is no satellite assembly for the user's default culture
                Debug.WriteLine(string.Format("DeploymentException: {0}", ex.ToString()));
            }
        }
    }
}

Code snippet 12: Code responsible for downloading optional satellite assemblies.

  • Generate one deployment for each culture (satellite assembly), each in a separate location: In this deployment strategy you generate multiple deployments, one deployment for each culture. In each deployment, you include only the satellite assembly needed for that specific culture and the neutral language assembly, and you mark the deployment as specific to that culture. After you have published your application you need to add the culture specific assembly and the neutral language assembly to the application manifest using MageUI.exe. To do this, you need to open the application manifest in MageUI.exe and select the Files page, browse to the two satellite assemblies and add them to the application manifest using the Populate button. After you have done this, you need to set the culture field, which can be found in the Name page of the deployment manifest. Finally, save the application manifest and resign the application manifest followed by the deployment manifest.

To illustrate the localization process of a ClickOnce application, I created a simple ClickOnce application, which is shown in figure 15. The deployment strategy for this application is to include all satellite assemblies as discussed earlier in this section. In the example application, the user can select one of the three cultures. The selected culture is then applied the Login window when the user clicks the Show dialog button.

  Image 15

Figure 15: Example application to illustrate usage of satellite assemblies.

The localization process of a ClickOnce application involves several steps:

  1. Add a UICulture attribute to the project file.
  2. Mark localizable properties in XAML files with the x:Uid attribute to identify them uniquely.
  3. Extract the localizable content from your application using a tool such as LocBaml.exe.
  4. Translate the localizable content.
  5. Create subdirectories to hold satellite assemblies for the new cultures.
  6. Generate satellite assemblies using a specialized tool such as LocBaml.exe.

After you have created the satellite assemblies, you need to add them to the application manifest using MageUI.exe.

Image 16

Figure 16: Using MageUI.exe to add satellite assemblies to the application manifest.

The last step of the deployment process is that you need to save the changes and resign the application manifest followed by the deployment manifest. The source code of the example application can be found on top of this article.

ClickOnce storage location

One of the advantages of ClickOnce applications is that they are isolated from the rest of the file system and 
therefore share no components with any other application installed on the client computer. Due to this isolation, ClickOnce applications and data are stored in special locations. These locations are summarized below:

  • ClickOnce application directory
  • ClickOnce data directory
  • Application and user settings that can be configured in the settings page
  • Isolated Storage
  • Remote locations such as web service or database.

All ClickOnce applications, whether they are installed locally or hosted online, are stored on the client computer in the ClickOnce application directory. The ClickOnce application directory can be found in a location that follows the following pattern:

c:\users\username\AppData\Local\Apps\2.0\[...]\[...]\[...]\

Code snippet 13: Location ClickOnce application directory. 

The final three portions of this path are automatically generated strings such as "\RRECT6Z4.EOT\CG8AWZZE.B19\easy..tion_7a9ade79bb74c459_0001.0000_3c138a878cc17ccc". As you might expect from the resulting path name, application users and developers are not expected to directly access or modify files in the ClickOnce application directory. Its therefore recommended that you do not use this directory to store data.

Instead of using the ClickOnce application directory, you should use the ClickOnce data directory to store application settings. Any file included in a ClickOnce application and marked as a data file is copied to this directory when the application is installed. Data files can be of any file type, such as XML and database files. Code snippet 14 shows the path of the ClickOnce data directory.    

c:\users\username\AppData\Local\Apps\2.0\data\[...]\[...]\[...]

Code snippet 14: Location ClickOnce data directory pattern similar to the application directory.

Each version of a ClickOnce application has its own isolated data directory. You can use this data directory to store data files in. Figure 17 shows the presence of two ClickOnce data directories, one for application version 1 and one for version 2. When a new version of a ClickOnce application is installed, ClickOnce will copy all the existing data files from the previous data directory into the new data directory. For example if you include a readme text file in your application that is changed by the user. ClickOnce will copy the changed readme text file into the new data directory as long as the readme text file included in the newer version of the ClickOnce deployment kit is not changed relative to the previous ClickOnce deployment kit. Also, if an earlier version of a ClickOnce application created a data file that has the same name as a file included in the newer version of a ClickOnce deployment kit, ClickOnce will overwrite the old file with the new file. In both cases, the old data files from the previous version will be moved in a subdirectory inside the data directory named .pre, so that the application can still access the old data for migration purposes. A final note I want to make about the ClickOnce data directory, is that when the ClickOnce application is uninstalled, its data directory is also removed. Therefore you should not use the data directory to store permanent data, such as documents.

Image 17

Figure 17: ClickOnce deployment directory structure.

Assuming that your ClickOnce application has full trust permissions, it can access the data directory by using method calls on classes within the System.IO. You can obtain the path of the data directory within a ClickOnce application by using the DataDirectory property defined on the CurrentDeployment property of the ApplicationDeployment class. This is the most convenient and recommended way to access your data. In addition you can also obtain the data directory path using the LocalUserAppDataPath property of the Forms.Application class. Code snippet 15 shows how to read a text file named readme.txt that was marked as a data file and included in the application manifest file of the ClickOnce application.

C#
private void ReadFileFromDataDir()
{
    if (ApplicationDeployment.IsNetworkDeployed)
    {
        try
        {
           using (StreamReader sr = 
              new StreamReader(ApplicationDeployment.CurrentDeployment.DataDirectory + @"\readme.txt"))
           {
                MessageBox.Show(sr.ReadToEnd());
           }
        }
        catch (Exception ex)
        {
            Debug.WriteLine(string.Format("ReadFileFromDataDir Exception: {0}", ex.ToString()));
        }
    }
}

Code snippet 15: Programmatically read data file from ClickOnce data directory. 

Alternatively to using the data directory, you can also use Isolated Storage for creating and accessing files from your application. Using the Isolated Storage API you can store and retrieve data for your application. Isolated Storage also works in partially trusted applications without the need for additional permission grants. Therefore you should use Isolated Storage if your application runs in partial trust environment, but must maintain application-specific data. Details about Isolated Storage API can be found here and here

WPF applications also enable you to create and access values called settings that persist from application session to application session. User settings are stored in the user.config file which can be found in a subdirectory of the ClickOnce data directory. When a ClickOnce application is updated, user settings are automatically migrated to the new version. Application settings are stored in the DataFiles.exe.config file located in a subdirectory of the ClickOnce application directory. Both the application and user settings can be viewed and configured in the settings view, as shown in figure 18.

Image 18

Figure 18: User and application settings that can be configured in the settings page. 

One important property in the settings page is the scope property which can be set to either application or user. A setting with application scope represents a value that is used by the entire application regardless of the user, whereas an application with user scope is more likely to be user-specific and less crucial to the application. An important distinction between user settings and application settings is that user settings are read and write. They can be read and written to at run time, and newly written values can be saved by the application. In contrast, application settings are readonly and the values can be changed only at design time or by editing the settings file between application sessions. I created a simple application that illustrates the usage of user and application settings. This example application can be found on top of this article.

  Image 19

Figure 19: Example application illustrates the usage of user and application settings.  

C#
private void ColorChangedEvent(object sender, RoutedEventArgs e)
{
    RadioButton ColorChosen = sender as RadioButton;
    if ((ColorChosen != null) && (ColorChosen.Tag != null))
    {
        switch (ColorChosen.Tag.ToString())
        {
            case "Red":
                Properties.Settings.Default.BackgroundColor = new SolidColorBrush(Colors.Red);
                break;
            case "Green":
                Properties.Settings.Default.BackgroundColor = new SolidColorBrush(Colors.Green);
                break;
            case "Blue":
                Properties.Settings.Default.BackgroundColor = new SolidColorBrush(Colors.Blue);
                break;
        }
        Properties.Settings.Default.Save();
    }
}

Code snippet 16: Save chosen background color as a user setting.

Furthermore you can also use remote data from a webservice or database  in your ClickOnce application, details can be found here.

Install and uninstall the application

After deployment, the user can install the PDF split and merge tool by clicking to this link, which displays the webpage below. If you do not have the .Net 4.5 framework installed, you can install it using the install button. To install the application you can click the launch link.

Image 20

Figure 20: Publish.htm page with link to install PDF tool

If you have installed the self-signed root certificate as a trusted root certification authority and the application signing certificate as a trusted publisher, the ClickOnce application will be installed without prompting. In case the application signing certificate was not installed as a trusted publisher, the user will be prompted with an installation dialog box, as shown below. 

Image 21

Figure 21User prompted with an installation dialog box.

As can be seen in the installation dialog box above, a publisher link is displayed, by clicking this link you can see the certificate chain of trust, which leads you to the installed self-signed root certificate, discussed earlier. The certificate  chain of trust is shown below, where the self-signed root certificate is the root.

Image 22

Figure 22Chain of trust of the application signature.

You can uninstall the application or restore it to a previous state using the Control Panel, by clicking Programs, and then clicking Programs and Features and click uninstall program. Next double click the Easy Split & Merge PDF entry after which a dialog box is shown to the user. In this dialog box the user can remove the application, or restore it to the previous version, as can be seen below. If the user selects the "Restore the application to its previous state" option and clicks OK, ClickOnce will roll back the application to the previous installed version. Note that ClickOnce retains only one previous version of the application that the user can revert to. If the user wishes to completely uninstall the application they would select the “Remove the application from this computer” option and click OK. This removes the application from the client computer. 

Image 23

Figure 23: You can uninstall a ClickOnce application or you can roll the application back to its previous state.

ClickOnce limitations

As mentioned earlier, ClickOnce deployment lacks the flexibility for customizing the setup wizard. Therefore you cannot use ClickOnce deployment for sophisticated applications that need a detailed setup to guide the user through a set of configuration steps. In these cases, you can use a Windows Installer, which will be discussed in the next chapter. Finally, there is one more point I want to mention about ClickOnce deployment, .NET makes it possible to build a primitive custom installer that uses ClickOnce. By using the InPlaceHostingManager class you can create a custom installer that uses ClickOnce, examples can be found herehere and here.

In addition the ClickOnce technology has the following limitations:

  • ClickOnce applications are installed for a single user, you cannot install an application for all users.
  • A ClickOnce deployment cannot perform custom actions such as creating a database, configuring registry settings, adding a new font, putting files in the Global Assembly Cache (GAC).
  • ClickOnce application cannot be run with administrator privileges, unless the application invoker has system administrator privileges.
  • ClickOnce applications and updates are published by hosting them on the web or on a fileshare. There is no way for controlling who can download the updates or when. This is particularly a problem when you publish a new version and the next day when the office opens a great number of people download the application at the same time.

Windows installer (MSI)

As discussed in the previous section, ClickOnce technology provides a simple and easy way to deploy a variety of applications.  For more complex deployments, you can use Window Installer technology. A Window Installer project is highly configurable and enables you to create directories on the client computer, copy files, modify the registry, and execute Custom Actions during installation. When compiled, a Window Installer project produces an MSI package, which incorporates a setup wizard for the application. When this package is clicked, it launches the application setup wizard and installs the application. An MSI package can be distributed by disk, hosted on a website, or file share. 
 
You can add a setup project to your solution, from the File menu of Visual Studio, choose Add and then click New Project to open the Add New Project dialog box. In the Project Types pane, expand Other Project Types and then click Visual Studio Installer. In the Templates pane, click Setup Project, and then click OK.
 
A setup project includes six editors that can be used to configure the contents and the behavior of the setup project. You can open any of these editors by right clicking the setup project in the Solution Explorer and then selecting the appropriate editor from the View menu. The six editors are:
  • File System Editor: This editor enables you to configure which files are included in the setup.  
  • Registry Editor: This editor can be used to add entries to the registry upon installation.
  • File Types Editor: This editor enables you to set file associations between applications and file types.
  • User Interface Editor: This editor enables you to configure the user interface seen during installation for both regular installation and administrative installation.
  • Custom Actions Editor: This editor enables you to to define Custom Actions to be performed during installation.
  • Launch Conditions Editor: This editor enables you to set conditions required for launching the installation.

Adding files to the setup project using the File System Editor

The File System editor represents the file system of the client computer. You can add output files to various directories, create new directories and add shortcuts to the client computer using this editor.

Image 24

Figure 24: File System editor represents the file system on the client computer.

As can be seen in the figure above, the File System editor is split into two panes. The left pane represents the directory structure of the client computer. Each folder in the left pane represents a folder on the client computer that exists or will be created upon installation. The right pane displays the contents of the in the left pane selected directory. Initially, the File System editor consists of three folders: 

  • Application Folder
  • User’s Desktop
  • User’s Program Menu.

You can change the folder for a particular file by selecting the file in the right pane and dragging it to the appropriate folder. You can add folders to the File System editor by right clicking the left pane and choosing Add Special Folder. Using this menu, you can add a special folder to the File System editor or create your own custom folder. If you choose to create a custom folder, this folder will be created on the client computer upon installation.

To add files from the PDF split and merge project to the setup project, I right clicked the Application Folder in the left pane of the File System editor, next, I choose Add and then I choose Project Output. The resulting dialog box is shown below.

Image 25

Figure 25: Primary output contains all .exe and .dll files of your application. 

I added the "Primary output" to the Application Folder, which contains all .exe and .dll files of the PDF split and merge project. You can also add other project files to your setup project, such as localized resources, content files, documentation, debug symbols, source files, or Extensible Markup Language (XML) serialization assemblies. After you have selected the output to be added to the Application Folder, click OK to confirm your choice.

Add shortcut icon to setup

I created a shortcut to the PDF split and merge tool executable and added it to the target computer. To create a shortcut, go the right pane of the File System editor, right click the file for which you want to create a shortcut and choose Create Shortcut. A shortcut to the file is created and added to the pane. Drag the shortcut from the right-side pane to the appropriate folder in the left-side pane, in my case I added it to the User's Desktop folder.

Furthermore, you can use the File System editor to associate an icon with shortcuts to your application. When you do this, shortcuts to your application will be displayed with the icon you specify. To add an icon, go the right pane of the File System editor, right click a folder (in my case I choose the Application Folder), choose Add, and then select File. The Add Files dialog opens. Next, browse to the .ico file you want to associate with your application shortcut and click Add to add it to your setup project. To associate the icon with the shortcut created at the beginning of this section, select the folder containing the shortcut in the left pane of the File System editor. After selecting the folder, the shortcut will then appear in the right pane of the File System editor. Right click the shortcut entry in the right pane of the File System editor and choose the Properties window, next select the Icon property and then choose (Browse…) from the drop-down list. Browse to the icon you want to associate with your application, select the icon and click OK.

User Interface Editor

The user interface editor enables you to configure the installation wizard interface. As shown below, the User Interface Editor contains two sections, which are the Install and Admin section. The Install section contains dialog boxes that will be displayed when the user runs the setup kit. Administrative installation is a feature of Microsoft Windows Installer that allows you to install a source image of an application on a network share. Users who have access to the network share can then install the application from the source image. The Administrative installation can be started via the command line using the /a command-line option (msiexec /a setup). Details about msiexec command-line options can be found here.  

Image 26

Figure 26: User interface editor enables you to configure the installation wizard interface.

Using the User Interface Editor you can customize the installation wizard by changing the messages and pictures that appear during installation of the application. Further customization of the installation wizard can be accomplished by adding or removing dialog boxes from it. To remove a dialog box right click on it in the User Interface Editor and select delete. You can add a dialog box to the installation wizard by right clicking on the root node such as start and select Add Dialog box. An example on how to add a new dialog box to the installation wizard can be found here.

Another example on how to customize the installation wizard can be found here. In this example the installation folder is set in code and the Folder dialog box is removed from the the installation wizard. An alternative to hardcoding the installation folder is to set the installation folder during the administrative installation so that the administrator chooses an installation folder that cannot be overridden by application users during their installation from the network share.

Conditional installation based on the operating system version

You can use the VersionNT system property to determine the operating system at installation time and to create installation conditions that allow the installation to continue or abort based on the operating system. The VersionNT property is an integer that is calculated by the following formula: MajorVersion * 100 + MinorVersion. Thus, Microsoft Windows 7 would report a VersionNT value of 601 (MajorVersion = 6, MinorVersion = 1) , as can be read here. To configure conditional installation based on the operating system version, go to the left Pane of the File System editor and select the Application Folder. Next go to the right Pane of the File System editor and select the file that contains the primary output for the application. In the properties window, select the Condition property and type a condition that evaluates the operating system based on the formula previously described. Because I wanted to restrict installation to Windows 7 or later, I used the condition VersionNT >= 601.

Setup project properties

There are project properties that can provide descriptive information about your application. These properties include:

  • AddRemoveProgramsIcon: Specifies an icon for the Add/Remove menu in the Control Panel.
  • Author: Contains information about the author of the program.
  • Description: Contains a description of the application.
  • Keywords: Contains keywords to be associated with the application.
  • Localization: Provides the locale information for the application.
  • Manufacturer: Contains information about the manufacturer of the application, which is used 
    to define the default install folder within the Program Files folder.
  • ManufacturerURL: Contains the URL of the manufacturer’s Web site.
  • ProductName: Contains the name of the product.
  • Subject: Contains information about the subject of the application.
  • SupportPhone: Provides a phone number for support for the application.
  • SupportURL: Contains a URL for support for the application.
  • TargetPlatform: Specifies the target platform of the installer: x86, x64, or Itanium.
  • Title: Contains the title of the application.

Other properties of the setup project determine the behavior of the setup project at installation time. These properties include:

  • InstallAllUsers: Specifies whether the package is installed for all users or only for the installing user.
  • PostBuildEvent: Specifies a command line that is executed after the build ends.
  • PreBuildEvent: Specifies a command line that is executed before the build begins.
  • RunPostBuildEvent: Specifies the condition under which the post-build event runs.
    The value is either On Successful Build or Always.
  • SearchPath: Specifies the path used to search for assemblies, files, or merge modules on the development computer.

Upgrade behavior of an installer is determined by the properties shown below. Using the settings below, the installer will remove any older version of the application and install a new one.  

  • RemovePreviousVersion: Should be true to remove the previous version of your application.
  • DetectNewerInstalledVersion: Should be true to look for a more recent version of the application on the target computer, if found installation is aborted.
  • Version: The version number should be incremented when making a new build. You will be prompted to change the product code, click yes confirm.

The ProductCode and UpgradeCode properties are used by the setup program and should never be altered manually. You can change these properties at design time by clicking the browse button followed by clicking the new code button.

Prerequisites

All applications created with Visual Studio 2015 require .NET Framework 4.5 to run.  You can configure your setup project to install prerequisites, such as the .NET Framework, as part of the installation. The .NET Framework is configured to be installed by default, as can be seen in the prerequisites list. The prerequisites list can be displayed by right clicking the setup project and selecting Properties, after which the properties dialog opens and you can click Prerequisites to open the Prerequisites dialog.

Custom actions

Although standard setup settings are sufficient to configure a setup in most cases, Custom Actions enable you to extend the capabilities of a setup by including executables, dynamic link libraries (DLLs) and scripts. Because of this flexibility, Custom Actions can be used for a wide variety of tasks, such as populating a database, restarting a background service, changing the permissions of the installation directory.

The Custom Actions editor allows you to configure Custom Actions that can be executed at different steps during installation of the application. Custom Actions can be executed upon four Installer events: Install, Commit, Rollback, or Uninstall. Install actions occur after the files have been installed but before the installation has been committed. Commit actions occur when an installation is committed on the client computer. Rollback actions are executed when an installation fails and is rolled back, and Uninstall actions are executed when an application is being uninstalled. You can use the Custom Actions editor to associate as many Custom Actions as required with these four Installer events. In figure 27 the OpenUrl.dll is added as a Custom Action to the Install and Commit event. The creation and configuration of this DLL will be discussed later in this section.

Image 27

Figure 27: Custom Actions editor allows you to configure Custom Actions that can be executed at different steps during installation.

As you can see in code snippet 19, the InstallerHelper class inherits from the Installer abstract class, and because of this, the InstallCommitRollback and the Uninstall methods can be overridden. When you are in the Custom Actions editor, you can configure a Custom Action by selecting the Custom Action and then setting the properties in the Properties window. Custom Action properties that can be configured are listed below.

  • Name: The name of the selected Custom Action.
  • Arguments: Command line arguments that can be passed to the Custom Action. This property is only applicable when the Custom Action is implemented as an executable.
  • Condition: A boolean statement that will be evaluated before the Custom Action is executed. If the statement is True, the Custom Action will executed. If the statement is False, the action will not be executed.
  • CustomActionData: Passes any additional required data to the Custom Action.
  • EntryPoint: Specifies the name of the method to execute. If left blank, the Custom Action will attempt to execute a method with the same name as the event to which it is associated. This property applies only to Custom Actions implemented in dynamic-link libraries (DLLs) and is ignored when the InstallerClass property is set to True.
  • InstallerClass: boolean value that represents whether your Custom Action is implemented in an Installer class. This property must be True if the Custom Action is implemented in an Installer class and False otherwise.
  • SourcePath: Contains the path on the developer’s computer to the file that implements the Custom Action. This property is read-only.

You can pass parameters entered in the installation wizard to Custom Actions. For example the installation target directory is stored in the variable TARGETDIR. This variable can be passed to the Commit Action as a  CustomActionData parameter. In the Commit Action, the passed parameter can be retrieved using the Context Parameter and the name chosen, as illustrated below.  

 Image 28

Figure 28: You can pass parameters to the Commit Action as a CustomActionData parameter.

The passed CustomActionData parameter can be retrieved from the Context Parameter using the name ESM_TargetDir.

C#
[System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Demand)]
public override void Commit(IDictionary savedState)
{
    base.Commit(savedState);
    string TargetDir = Context.Parameters["ESM_TargetDir"];
}

Code snippet 17: CustomActionData parameter can be retrieved using the name ESM_TargetDir.

As you might have noticed in the InstallerHelper class shown below, the argument name passed to the install event handler is different than the argument name passed to the other three event handlers. The stateSaver dictionary passed to the installation event handler allows you to save parameters that can be shared across the other three installation events. For example, you can save the [TARGETDIR] parameter in the Install event handler and retrieve it in the other three event handlers, as shown below.

C#
//Save the installation directory in dictionary  
public override void Install(System.Collections.IDictionary stateSaver) 
{ 
   base.Install(stateSaver); 
   stateSaver.Add("SavedDir", Context.Parameters["ESM_TargetDir"].ToString()); 
}

//Retrieve the installation directory from dictionary. 
public override void Commit(System.Collections.IDictionary savedState) 
{ 
   base.Commit(savedState);
   string installDir = savedState["SavedDir"].ToString();   
}

Code snippet 18: The stateSaver dictionary is shared with the other three event handlers.

Using this approach, the [TARGETDIR] parameter is passed to the Install event handler as a  CustomActionData parameter, after which the passed parameter is saved in the stateSaver dictionary which is shared with the other three event handlers. 

To illustrate Custom Actions, I created a Custom Action DLL to direct the user to a web page at the end of the installation. To create the Custom Action DLL, I undertook the following steps.

  • Right click on the Solution, select add->New Project and choose c# Class Library. Name the project OpenUrl and click OK, after which the project will be added to the Solution Explorer.
  • Remove the default Class1.cs, and add a new Installer Class to the project. You can add a new Installer Class by right clicking the OpenUrl project, select add->Class and choose Installer Class. Name the class InstallerHelper and click OK, after which the class will be added to the OpenUrl project.
  • The following code is added to the InstallerHelper class, after which the library is build.  
C#
public partial class InstallerHelper : System.Configuration.Install.Installer
{
    public InstallerHelper()
    {
        InitializeComponent();
    }

    [System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Demand)]
    public override void Install(IDictionary stateSaver)
    {
        base.Install(stateSaver);
    }

    [System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Demand)]
    public override void Commit(IDictionary savedState)
    {
        base.Commit(savedState);
        System.Diagnostics.Process.Start("http://www.codeproject.com/Articles/1040410/
                                          PDF-Split-and-Merge-Tool-using-itextsharp");
    }

    [System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Demand)]
    public override void Rollback(IDictionary savedState)
    {
        base.Rollback(savedState);
    }

    [System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Demand)]
    public override void Uninstall(IDictionary savedState)
    {
        base.Uninstall(savedState);
    }
}

Code snippet 19: Implementation of the InstallerHelper class.

After successful building the Custom Action DLL, it needs to be added to the installer project. The Custom Action DLL should be added to the installer project in the same way as application files were added to the project. To add the Custom Action DLL to the setup project, right click the Application Folder in the left pane of the File System editor, next choose Add and then choose Project Output, select the Primary output of the OpenUrl project and click OK. The resulting Project Output dialog box is shown below.

Image 29

Figure 29: Adding the Custom Action DLL to the installer project.   

To add the Custom Action to the installer project, I undertook the following steps.

  • Right click installer project and select View->Custom Action. The Custom Actions Editor is then displayed.
  • In the Custom Actions Editor, right click the Commit node and select Add Custom Action, and select the primary output of the OpenUrl, as shown below.

Image 30

Figure 30: Adding the Custom Action DLL as Custom Action.  

  • In the Properties window, make sure that the InstallerClass property is set to True (this is the default).
  • Finally, right-click the setup project and select build to create the setup kit.

During the execution of the setup kit, I got the following error message: Custom Action - Error 1001: Could not find file OpenUrl.InstallState. The error was caused due to the absence of the installation state file which is usually created in the Install event of the installer. The solution is to add the Custom Action to both the Install and the Commit Installer events, although no action is performed in the Install event.

Because custom actions are executed code, you also need to handle errors in Custom Actions. You can use a try/catch block to catch and correct any errors that can be corrected. If an error occurs that cannot be corrected, such as a missing file, throw a new InstallException exception. Throwing an InstallException exception causes the installation to be rolled back without leaving any lasting effect on the client computer. The following example demonstrates how to test for the existence of a file and throw a new InstallException exception if the file is not found. The code needs to be added to the Installer class.

C#
System.IO.FileInfo fileInfo = new System.IO.FileInfo("Readme.txt");
if(!(fileInfo.Exists))
    throw new System.Configuration.Install.InstallException("Readme file not found");

Code snippet 20: Throwing an InstallException exception causes the installation to be rolled back.

Alternatively you can use a try/catch, as shown below.

C#
try
{
   //Code
}
catch (Exception ex)
{
   throw new System.Configuration.Install.InstallException("unexpected error occurred", ex);
}

Code snippet 21: A general try/catch for all exceptions occur.

As mentioned earlier, If an InstallException is thrown in the installer, the installation process is rolled back, and a popup box is shown to the user with a description of the error. In addition the error is recorded in the computers's event log, which you can open via the Control Panel's Administrative Tools. 

Deployment of a localized MSI package

There are several strategies that can be used to deploy a localized application using Windows Installer. You can for example use conditional installation of localized resources using the SystemLanguageID property. Details about this approach can be found in this article. Another localization strategy is explained in detail by Microsoft in this article.

MSI signing with signtool.exe

You can use SignTool.exe to sign the resulting MSI package. For signing the MSI package I used the private key created earlier in the ClickOnce section. Using the ApplicationSigning.pfx private key, the MSI package can be signed as follows.

signtool sign /fd SHA512 /t http://timestamp.digicert.com /f ApplicationSigning.pfx /p PfxPassword Setup.msi
Done Adding Additional Store
Successfully signed: Setup.msi
*) Note that the absolute paths to the files are omitted for clarity.

Code snippet 22: Sign command to sign the MSI package. 

You can verify that the MSI package was signed correctly by right clicking on it and clicking Properties. The Digital Signatures tab shows the signature and timestamp of the MSI package.

Image 31

Figure 31: Signature properties of the MSI package.

Security

Elevated privileges are required both to install and to uninstall an MSI package. To run with elevated privileges, the MSI package must be deployed by a local administrator or advertised by a system administrator using Group Policy.

Application location

The TARGETDIR property specifies the installation directory. During an administrative installation (msiexec /a setup) the TARGETDIR property specifies the location where the installation package is copied to. During an ordinary installation this property specifies the installation directory.

   Image 32

Figure 32: Default installation location of Window Installer applications.

Install and uninstall the application

All Window Installer operations are transactional. Which means that the Window Installer creates a file detailing all the actions taken during installation. For each operation that Windows Installer performs, it generates an equivalent undo operation that would revert the change made to the client computer. In case the installation fails, or the installation is cancelled by the user, all the actions performed until that point are rolled back, restoring the state of the client computer to its original state. It should be noted that when you create custom actions that change the state of the client computer, you should also create corresponding rollback actions as well as uninstall actions.

Comparison of ClickOnce and Windows Installer

For simple applications that do not have complex installation requirements, ClickOnce is an excellent deployment technology that provides valuable benefits, such as auto updating and easy to use. For applications with more sophisticated requirements, Windows Installer remains the deployment technology of choice because it is highly configurable. The differences between these two technologies are illustrated in the table below.  

Image 33

Table 1: Comparison of ClickOnce and Windows Installer.

Points of Interest

If you cannot use Visual Studio to deploy your ClickOnce application, you can use Mage.exe or MageUi.exe to deploy your application, details on how to do this can be found here

You can elevate install privileges of ClickOnce applications by creating a separate process with administrator privileges. Details can be found in this article and in this article.

You can also automatically update Window Installer applications using custom code, details can be found here.

Installing a ClickOnce application from Internet Explorer is straightforward. You just browse to the publish.htm page and click the launch link. This starts the setup process immediately. If you want to install ClickOnce applications from other browsers such as Chrome, you need at least one extra step. After the download is complete, you need to launch the file explicite to start the setup process. Chrome offers a plugin that allow similar ClickOnce installation behavior as with Internet Explorer, it can be downloaded here.

The publish.htm page hosted on Github is displayed to the user using this website.

One disadvantage of using self-signed certificates, is that they cannot be revoked via CRL.  

The tools Mage.exe and MageUi.exe that can be used to configure ClickOnce deployment can be found in the following directory: "C:\Program Files\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6 Tools\".

Certificate sign tools such as signtool.exe and makecert.exe can be found in the following directory: "C:\Program Files\Windows Kits\10\bin\x86\".

Reference list

  1. MCTS Self-Paced Training Kit Exam 70-511
  2. Apress Pro WPF 4.5 in Csharp 4th Edition
  3. Example of a localized ClickOnce application.

History

  • December  19th, 2015: Version 1.0.0.0 - Created the article

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Netherlands Netherlands
Mohamed Kalmoua is a Microsoft Certified Solutions Developer (MCSD) with over a decade of programming experience. He creates software for the Windows platform using C#, WPF, ASP.NET Core, SQL and C++. Mohamed also loves to build websites using Wordpress and Google analytics.

Comments and Discussions

 
QuestionLocal database.mdf and update Pin
Member 1306979715-Jun-17 15:41
Member 1306979715-Jun-17 15:41 
QuestionUnknown publisher Pin
webmaster44219-Dec-15 7:36
webmaster44219-Dec-15 7:36 
AnswerRe: Unknown publisher Pin
Mohamed Kalmoua19-Dec-15 12:28
Mohamed Kalmoua19-Dec-15 12:28 
GeneralRe: Unknown publisher Pin
webmaster44220-Dec-15 6:15
webmaster44220-Dec-15 6:15 
QuestionNice article Pin
webmaster44219-Dec-15 3:30
webmaster44219-Dec-15 3:30 
AnswerRe: Nice article Pin
Mohamed Kalmoua19-Dec-15 5:56
Mohamed Kalmoua19-Dec-15 5:56 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.