Chapitre 1414 minIntermédiaire
Publier une application WinForms : dotnet publish
Par Josselyn C., prof de BTS SIO. Mis à jour le .
À la fin de ce chapitre, tu sauras
- générer un exécutable Windows avec dotnet publish ;
- choisir entre une publication autonome et une publication dépendante du runtime ;
- livrer la configuration à côté de l'exécutable ;
- installer et mettre à jour les postes avec ClickOnce ;
- choisir un mode d'installation et numéroter les versions.
5 exercices corrigés
Publier une application, c'est produire les fichiers à installer sur les postes des utilisateurs : l'exécutable, ses bibliothèques et sa configuration, compilés en mode Release. Avec .NET 10, la commande dotnet publish (ou l'assistant Publier de Visual Studio) produit un dossier prêt à copier, ou même un seul fichier .exe. Ce chapitre explique les choix à faire : runtime inclus ou non, fichier unique, configuration, installation et numéro de version.
Debug et Release
Pendant le développement, Visual Studio compile en configuration Debug : code non optimisé, informations de débogage complètes. Pour livrer, on compile en Release : le code est optimisé, et les vérifications réservées au débogage disparaissent. Ne livre jamais le contenu de bin\Debug.
Dépendante du runtime ou autonome ?
Une application .NET a besoin du runtime .NET pour s'exécuter. Deux modes de publication :
| Dépendante du runtime (framework-dependent) | Autonome (self-contained) | |
|---|---|---|
| Le runtime | Doit être installé sur le poste (« .NET Desktop Runtime 10 ») | Est inclus dans la publication |
| Taille, pour GSB Ordonnances en fichier unique | environ 16 Mo | environ 130 Mo |
| Mises à jour de sécurité du runtime | Par Windows Update ou l'administrateur | À refaire à chaque nouvelle publication |
| Quand le choisir | Parc géré par un service informatique | Postes que tu ne maîtrises pas, installation sans droits d'administrateur |
Les tailles ci-dessus ont été mesurées sur le projet complet du cours, qui embarque EF Core et deux pilotes MySQL ; une application qui n'utilise que MySqlConnector est nettement plus légère.
Les commandes
Depuis le dossier de la solution :
# Dépendante du runtime, un seul fichier .exe pour Windows 64 bits
dotnet publish Gsb.Ui -c Release -r win-x64 --no-self-contained -p:PublishSingleFile=true -o publication
# Autonome : le runtime .NET est inclus
dotnet publish Gsb.Ui -c Release -r win-x64 --self-contained -p:PublishSingleFile=true -o publication
| Option | Rôle |
|---|---|
-c Release | Configuration Release |
-r win-x64 | Le système cible : Windows 64 bits sur processeur Intel ou AMD ; win-arm64 pour un PC ARM |
--self-contained / --no-self-contained | Inclure ou non le runtime |
-p:PublishSingleFile=true | Regrouper l'application et ses bibliothèques dans un seul .exe |
-o publication | Le dossier de sortie |
Quelques options utiles en plus :
-p:IncludeNativeLibrariesForSelfExtract=true: les bibliothèques natives (des.dllnon .NET, comme celles qu'apporte MySql.Data pour Kerberos) restent sinon à côté de l'exécutable, même en fichier unique ;-p:EnableCompressionInSingleFile=true: compresse un fichier unique autonome ;-p:PublishReadyToRun=true: précompile le code pour un démarrage plus rapide, au prix d'un fichier plus gros.
Le découpage (trimming), qui retire le code inutilisé pour réduire la taille, n'est pas pris en charge pour WinForms : -p:PublishTrimmed=true produit l'erreur NETSDK1175.
Dans Visual Studio, l'assistant fait la même chose : clic droit sur Gsb.Ui > Publier > Dossier, puis Afficher tous les paramètres pour choisir le mode de déploiement, le runtime cible et l'option « Produire un seul fichier ». Le profil est enregistré dans Properties\PublishProfiles et se relance d'un clic.
La configuration à côté de l'exécutable
Le fichier appsettings.json du chapitre 12 doit être copié avec l'application. Dans le fichier projet :
<ItemGroup>
<None Update="appsettings.json" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
Il reste un fichier séparé, à côté de l'exécutable, même en fichier unique : l'administrateur peut ainsi changer le nom du serveur sans recompiler. Il ne contient aucun mot de passe.
Numéroter les versions
Indique la version dans le fichier projet, en suivant la gestion sémantique de version (majeure.mineure.correctif) :
<PropertyGroup>
<Version>1.2.0</Version>
</PropertyGroup>
L'application peut l'afficher dans sa fenêtre « À propos » avec Application.ProductVersion. Depuis .NET 8, le SDK ajoute à cette version l'identifiant du commit Git (1.2.0+3f2a9c1…) ; pour l'enlever, ajoute <IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>. Chaque version livrée correspond à une étiquette Git (v1.2.0) et l'exécutable se dépose dans une release GitHub, pas dans le dépôt (publier une version).
Installer sur les postes
| Méthode | Principe | Pour qui |
|---|---|---|
Copie d'un dossier ou d'un .zip | On dézippe, on crée un raccourci | Une démonstration, quelques postes |
| ClickOnce | L'assistant Publier de Visual Studio génère un installateur qui vérifie les mises à jour au lancement | Un parc de postes sans outil de déploiement, mises à jour fréquentes |
| MSIX | Un paquet Windows moderne, installé et désinstallé proprement | Distribution par Intune, Microsoft Store, postes Windows 10 et 11 |
| Installateur (Inno Setup, WiX) | Un setup.exe classique, avec raccourcis, entrées de registre et désinstallation | Distribution à des clients, installation personnalisée |
Installer avec ClickOnce pas à pas
ClickOnce est la technologie de déploiement de Microsoft pour les applications Windows Forms et WPF : l'application s'installe dans le profil de l'utilisateur, sans droits d'administrateur, et vérifie à chaque lancement si une version plus récente a été publiée. Pour GSB Ordonnances, installée sur les postes d'un cabinet qui partagent un serveur de fichiers, c'est la façon la plus simple d'obtenir des mises à jour automatiques.
Créer le profil dans Visual Studio 2026.
- Clic droit sur
Gsb.Ui> Publier ; si un profil existe déjà, crée-en un nouveau. - Choisis la cible Dossier, puis la cible spécifique ClickOnce.
- Emplacement de publication : le dossier où Visual Studio dépose les fichiers, ici le partage réseau du cabinet,
\\srv-cabinet\logiciels\gsb-ordonnances\. - Emplacement d'installation : d'où les utilisateurs installent l'application ; choisis le chemin UNC (le partage de fichiers).
- Paramètres : rends l'application disponible hors connexion, pour qu'elle soit installée avec un raccourci dans le menu Démarrer et qu'elle se mette à jour. Règle la version de publication (
1.2.0.0) et laisse cochée l'incrémentation automatique. Dans Prérequis, coche le .NET Desktop Runtime 10 (x64) : le programme d'installation l'installera s'il manque. - Signer les manifestes : avec un certificat de signature de code pour une vraie distribution.
- Configuration : Release,
net10.0-windows, le mode de déploiement et le runtime ciblewin-x64, comme dans les commandes plus haut. - Terminer, puis Publier. Le profil est enregistré dans
Properties\PublishProfiles\ClickOnceProfile.pubxml.
Ce que contient le dossier publié.
\\srv-cabinet\logiciels\gsb-ordonnances\
├── setup.exe le programme d'installation : prérequis, puis application
├── Gsb.Ui.application le manifeste de déploiement : quelle version est à jour
└── Application Files\
└── Gsb.Ui_1_2_0_0\ les fichiers de la version 1.2.0.0 et son manifeste
Sur chaque poste, l'utilisateur ouvre le partage et lance setup.exe une seule fois. Ensuite, il démarre GSB Ordonnances depuis le menu Démarrer. À chaque lancement, ClickOnce relit Gsb.Ui.application sur le partage : si une version plus récente a été publiée, il propose de la télécharger avant de démarrer l'application (avec .NET 10, la vérification en arrière-plan, pendant que l'application tourne, n'est pas prise en charge).
Publier une nouvelle version. Corrige, lance les tests, puis clique de nouveau sur Publier : la version de publication passe à 1.2.0.1, un dossier Gsb.Ui_1_2_0_1 s'ajoute et le manifeste de déploiement pointe dessus. Au prochain lancement, chaque poste propose la mise à jour. En ligne de commande, la documentation de Microsoft passe par MSBuild, avec le profil créé par Visual Studio : msbuild Gsb.Ui /t:publish /p:PublishProfile=ClickOnceProfile.
ClickOnce a ses limites : l'installation se fait par utilisateur (deux sessions Windows sur le même poste font deux installations), et elle ne sait ni écrire dans le registre de la machine ni installer un service Windows. Pour un parc géré par Intune ou une installation pour toute la machine, on choisit MSIX ou un installateur classique.
Avant de livrer, teste la publication sur un poste neuf, sans Visual Studio ni SDK : c'est là qu'on découvre le runtime manquant, le fichier de configuration oublié ou le chemin écrit en dur vers C:\Users\toi\….
Automatiser la publication avec GitHub Actions
Publier à la main, c'est risquer d'oublier les tests, de compiler depuis une copie modifiée ou de se tromper d'option. Une chaîne d'intégration continue le fait à ta place, toujours de la même façon. Ce workflow se déclenche quand tu pousses une étiquette de version (git tag v1.2.0 puis git push origin v1.2.0) : il lance les tests, publie l'exécutable autonome et le conserve comme artefact téléchargeable.
# .github/workflows/publier.yml
name: Publier GSB Ordonnances
on:
push:
tags: ['v*']
jobs:
publier:
runs-on: windows-latest # WinForms ne se compile que sous Windows
steps:
- uses: actions/checkout@v7
- uses: actions/setup-dotnet@v6
with:
dotnet-version: '10.0.x'
- run: dotnet test
- run: dotnet publish Gsb.Ui -c Release -r win-x64 --self-contained -p:PublishSingleFile=true -o publication
- uses: actions/upload-artifact@v7
with:
name: gsb-ordonnances-${{ github.ref_name }}
path: publication
Si un test échoue, l'étape dotnet test s'arrête en erreur et rien n'est publié. La syntaxe des workflows et la création automatique d'une release sont expliquées dans le cours Intégration continue.
Prévenir l'utilisateur d'une nouvelle version
Un client lourd installé ne se met pas à jour tout seul, sauf avec ClickOnce ou MSIX. Une solution simple : au démarrage, l'application lit la dernière version disponible (dans un petit fichier JSON publié sur un serveur, ou par l'API de l'application) et la compare à la sienne.
Version installee = typeof(Program).Assembly.GetName().Version!; // 1.2.0.0, tiré de <Version>
Version disponible = new Version("1.3.0"); // lue sur le serveur
if (disponible > installee)
{
MessageBox.Show($"La version {disponible} est disponible. Contacte le support pour l'installer.",
"Mise à jour", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
System.Version compare les numéros composante par composante : 1.10.0 est bien plus récente que 1.9.0, ce qu'une comparaison de chaînes de caractères donnerait à l'envers. Avec un seul argument, <Version>1.2.0</Version> donne une version d'assemblage 1.2.0.0 : new Version("1.3.0") et 1.2.0.0 se comparent correctement, la quatrième composante absente comptant comme inférieure à 0.
Exercices
Exercice 1 sur 5 Choisir le mode de publication Réussi À revoir
Pour chaque situation, choisis entre une publication dépendante du runtime et une publication autonome, et donne la commande.
- Les 15 postes du cabinet sont gérés par un prestataire qui installe les mises à jour Windows et le .NET Desktop Runtime 10.
- Tu envoies ton application à la commission de l'E6 pour qu'elle la lance sur un PC dont tu ignores la configuration.
Voir la réponseMasquer la réponse de l'exercice 1
-
Dépendante du runtime : plus légère, et le runtime est mis à jour par le prestataire.
dotnet publish Gsb.Ui -c Release -r win-x64 --no-self-contained -p:PublishSingleFile=true -o publication -
Autonome : elle fonctionne même si .NET 10 n'est pas installé.
dotnet publish Gsb.Ui -c Release -r win-x64 --self-contained -p:PublishSingleFile=true -o publication
Dans les deux cas, livre aussi appsettings.json, et teste sur un poste vierge.
Tu avais trouvé ?
Exercice 2 sur 5 « L'application ne démarre pas » Réussi À revoir
Tu as copié le contenu de bin\Debug\net10.0-windows sur le poste de la secrétaire. Un double-clic sur Gsb.Ui.exe affiche un message demandant d'installer .NET. Explique, et donne deux solutions.
Voir la réponseMasquer la réponse de l'exercice 2
Le dossier bin\Debug contient une application dépendante du runtime : elle a besoin du .NET Desktop Runtime 10, absent de ce poste. (Et c'est une compilation Debug, qu'on ne livre pas.)
- Solution 1 : installer le .NET Desktop Runtime 10 sur le poste, puis copier une publication Release dépendante du runtime.
- Solution 2 : publier en mode autonome (
--self-contained), qui embarque le runtime.
Tu avais trouvé ?
Exercice 3 sur 5 La version dans la fenêtre « À propos » Réussi À revoir
Ton fichier projet contient <Version>1.3.0</Version> et la fenêtre « À propos » affiche Application.ProductVersion. Elle montre 1.3.0+8c41d2e5…. D'où vient la fin, et comment n'afficher que 1.3.0 ?
Voir la réponseMasquer la réponse de l'exercice 3
Depuis .NET 8, le SDK ajoute à la version « informationnelle » l'identifiant du commit Git à partir duquel l'application a été compilée. C'est utile pour retrouver le code exact d'une version livrée. Pour ne garder que 1.3.0, ajoute dans le fichier projet :
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
Tu avais trouvé ?
Exercice 4 sur 5 La mise à jour qui n'arrive pas Réussi À revoir
GSB Ordonnances est installée par ClickOnce depuis \\srv-cabinet\logiciels\gsb-ordonnances\. Tu corriges un bug, passes <Version> de 1.2.0 à 1.2.1 dans le fichier projet, décoches l'incrémentation automatique « pour garder des numéros propres » et republies : la version de publication du profil est toujours 1.2.0.0. Le lendemain, la secrétaire lance l'application : aucune mise à jour n'est proposée. Explique, et corrige.
Voir la réponseMasquer la réponse de l'exercice 4
ClickOnce ne lit pas <Version> : il compare la version de publication inscrite dans le manifeste de déploiement (Gsb.Ui.application) à celle qui est installée. Elle vaut toujours 1.2.0.0 des deux côtés, donc, pour ClickOnce, le poste est à jour.
Correction : dans le profil ClickOnce, passe la version de publication à 1.2.1.0 (ou réactive l'incrémentation automatique), republie, puis relance l'application sur un poste : la mise à jour est proposée avant le démarrage. Garde <Version>1.2.1</Version> dans le fichier projet pour que la fenêtre « À propos » affiche le même numéro.
Tu avais trouvé ?
Exercice 5 sur 5 Le workflow qui échoue Réussi À revoir
Pour aller plus vite, un camarade a remplacé runs-on: windows-latest par runs-on: ubuntu-latest dans le workflow de publication. L'étape dotnet publish échoue avec l'erreur NETSDK1100, qui parle de EnableWindowsTargeting. Explique, et dis ce qu'il faut faire.
Voir la réponseMasquer la réponse de l'exercice 5
Le projet Gsb.Ui cible net10.0-windows : par défaut, le SDK refuse de le compiler sur un autre système que Windows, d'où l'erreur NETSDK1100. La propriété EnableWindowsTargeting permet de forcer la compilation sous Linux, mais l'application ne pourrait pas y être lancée, et les éventuels tests qui ouvrent des fenêtres non plus.
La solution simple est de revenir à runs-on: windows-latest pour le job de publication. Les tests de la couche métier (Gsb.Metier, Gsb.Tests, en net10.0) peuvent, eux, tourner sur ubuntu-latest dans un job séparé, plus rapide.
Tu avais trouvé ?