Licence CC BY-SA

Création de son propre installeur

Si toutes les méthodes de personnalisation vues précédemment ne vous suffisent pas, nous allons voir ici comment créer votre propre installeur. Cela vous permet, entre autres, de créer une UI personnalisée pour votre installeur, d'ajouter des évènements lors de l'installation de l'application…

L'utilisateur pourra, grâce à celui-ci, télécharger n'importe quelle application ClickOnce en entrant l'URL de son manifeste dans une boîte de texte.
Nous rajouterons quelques instructions pour personnaliser notre installation, par exemple en affichant des boîtes de dialogue à un certain moment.


Design de l'application

Avant tout, il nous faut designer notre installeur personnalisé. On peut utiliser Windows Forms, WPF ou même une application console. Ici, nous utiliserons trois contrôles Windows Forms :

  • Une TextBox où l'on écrit l'URL du manifeste ClickOnce ;
  • Une RichTextBox affichant les informations sur l'application ;
  • Un Label affichant les informations sur le statut de l'installation ;
  • Et enfin, une ProgressBar affichant la progression de l'installation.

Installeur

Ces contrôles devront être accessibles par une classe à part, celle qui se chargera de l'installation de l'application. Vous devrez donc les définir comme publics.

Création de notre classe installeur personnalisée

Afin de créer notre propre installeur, je recommande de mettre toute la logique liée à l'installation dans une classe à part. Cela simplifiera grandement votre code, surtout si vous comptez faire un installeur plus élaboré que le nôtre.

Créez donc un fichier CustomClickOnceInstaller.cs dans lequel vous insérerez ce code :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
using System;
using System.Deployment.Application;
using System.Windows.Forms;

namespace VotreNamespace
{
    /// <summary>
    /// Installeur personnalisé 
    /// </summary>
    public class CustomClickOnceInstaller
    {
        InPlaceHostingManager iphm;
    }
}

Vous remarquerez que nous avons importé trois namespaces :

  • System ;
  • System.Deployement.Application, permettant de créer un comportement personnalisé pour notre application ClickOnce ;
  • Windows.Forms puisque nous intéragirons avec des contrôles WinForms.

L'InPlaceHostingManager nous permettra de télécharger/mettre à jour notre déploiement.

Il nous faut maintenant écrire une méthode installant notre application. Je recommande de faire passer une URL de manifeste dans les paramètres de la méthode afin de rendre votre installeur réutilisable pour d'autres logiciels.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/// <summary>
/// Installe une applicaiton ClickOnce.
/// </summary>
/// <param name="manifestUrl">URL du manifeste ClickOnce.</param>
public void Install(string manifestUrl)
{
    // :: Blocs try-catch
    // Permet de gérer les différents types d'erreur qui peuvent
    // être engendrés.
    try
    {
        // Création d'une classe Uri à partir de l'URL du manifeste
        var manifestUri = new Uri(manifestUrl);

        // :: Initialisation du InPlaceHostingManager
        // Le deuxième paramètre sert à spécifier si l'application ClickOnce est
        // téléchargée depuis un hôte (comme un navigateur web).
        iphm = new InPlaceHostingManager(manifestUri, false);
    }
    catch (UriFormatException)
    {
        MessageBox.Show("Erreur : L'URL du manifeste spécifiée est invalide.");
        return; // On arrête la fonction
    }
    catch (ArgumentException)
    {
        MessageBox.Show("Erreur : L'URL du manifeste spécifiée est invalide.");
        return; // On arrête la fonction
    }
    catch (PlatformNotSupportedException)
    {
        MessageBox.Show("Erreur : Votre système d'exploitation est trop ancien pour installer cette application.");
        return; // On arrête la fonction
    }

    // :: Ajout d'un évènement
    // La méthode iphm_GetManifestCompleted() se déclenchera lorsque
    // le manifeste de déploiement sera récupéré.
    iphm.GetManifestCompleted += iphm_GetManifestCompleted;

    // Récupère le manifeste sans bloquer le thread principal,
    // garantissant une meilleure expérience utilisateur.
    iphm.GetManifestAsync();
}

Lorsque le manifeste sera téléchargé, cette méthode sera exécutée :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/// <summary>
/// A lieu lorsque le manifeste de l'application ClickOnce est téléchargé.
/// </summary>
void iphm_GetManifestCompleted(object sender, GetManifestCompletedEventArgs e)
{
    // Vérification des erreurs
    if (e.Error != null)
    {
        MessageBox.Show("Erreur lors du téléchargement du manifeste : " + e.Error.Message);
        return; // Annulation de l'installation.
    }

    // Vérifie si les privilèges requis sont satisfaits.
    try
    {
        // Si des élévations de privilèges sont requises, elles auront lieu. 
        iphm.AssertApplicationRequirements(true);
    }
    catch (Exception)
    {
        MessageBox.Show("Erreur lors de la vérification des privilèges.");
        return;
    }

    // Mise à jour du titre de la fenêtre Main
    ((Main)Application.OpenForms[0]).Text = String.Format("Installation de '{0} [v{1}]'", e.ProductName, e.Version);

    // Vérifie si l'application est déjà installée.
    if (e.ActivationContext.Form == ActivationContext.ContextForm.StoreBounded)
    {
        ((Main)Application.OpenForms[0]).statusLabel.Text = "Application déjà installée.";
return;
    }

    // Enregistrement des évènements :
    // - Changement de la progression du téléchargement ;
    // - Téléchargement terminé.
    iphm.DownloadProgressChanged += iphm_DownloadProgressChanged;
    iphm.DownloadApplicationCompleted += iphm_DownloadApplicationCompleted;

    // Téléchargement de l'application de manière asynchrone.
    iphm.DownloadApplicationAsync();
}

Ajoutons les deux derniers évènements nécessaires au fonctionnement de l'installeur :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/// <summary>
/// A lieu lorsque l'application est installée.
/// </summary>
void iphm_DownloadApplicationCompleted(object sender, DownloadApplicationCompletedEventArgs e)
{
    // Vérifier les erreurs
    if (e.Error != null)
    {
        MessageBox.Show("Erreur lors de l'installation de l'application : " + e.Error.Message);
        return; // Annulation de l'installation.
    }

    // Dire à l'utilisateur que l'application est installée
    // puis fermer le logiciel.
    MessageBox.Show("Application installée.");
    Application.Exit();
}


/// <summary>
/// A lieu lorsque la progression du téléchargement de l'application change.
/// </summary>
void iphm_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
    // Mettre à jour le pourcentage de l'installation.
    ((Main)Application.OpenForms[0]).progressBar.Value = e.ProgressPercentage;
}

Bonus : exécuter certaines actions en fonction de la progression

Notre classe est bien belle, mais comment la rendre meilleure que l'installeur ClickOnce par défaut ?

Nous pouvons par exemple exécuter certaines actions lors Reprenons notre méthode iphm_DownloadProgressChanged :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/// <summary>
/// A lieu lorsque la progression du téléchargement de l'application change.
/// </summary>
void iphm_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
    // Mettre à jour le pourcentage de l'installation.
    ((Main)Application.OpenForms[0]).progressBar.Value = e.ProgressPercentage;

    // Exécuter certaines actions en fonction du pourcentage
    switch(e.progressPercentage) {
        // Si un quart (25%) de l'application est installée, en informer l'utilisateur
        case 25:
            MessageBox.Show("Un quart de l'application installé !")
            break;

        // etc
        case 50:
            MessageBox.Show("La moitié de l'application installée !")
            break;

        case 75:
            MessageBox.Show("Les trois quarts de l'application installés !")
            break;
    }
}

Vous n'êtes pas obligé d'ouvrir des boîtes de dialogue, vous pouvez par exemple afficher des informations sur l'installeur pour le faire patienter…

Lier la classe installeur à notre fenêtre

Pour ce faire, rien de plus simple, créez des évènements liés à chaque contrôleur et appelez les méthodes de notre classe. Par exemple, exécutez ce code lorsque le bouton est pressé :

1
2
CustomClickOnceInstaller installer = new CustomClickOnceInstaller();
installer.Install(textBox.Text);

J'espère que cela vous suffira, car il est impossible de personnaliser son application ClickOnce davantage.
Je vous invite à lire la page MSDN à ce sujet d'où le code ici présent est en parti tiré.