Impression et sortie PDF dans les applications métier Windows — choisir entre System.Drawing.Printing, WPF et bibliothèques de rapports
· Go Komura · CSharp, .NET, WinForms, WPF, Impression, PDF, Rapports, Développement Windows, Conseil technique
Imprimer depuis une application métier Windows ne consiste jamais seulement à « dessiner avec PrintDocument ». Sauts de page absents, marges décalées, aperçu différent du papier ou boîte de dialogue alors qu’un PDF était attendu proviennent presque toujours d’une implémentation commencée sans comprendre le mécanisme d’impression.
Cet article organise les quatre options — System.Drawing.Printing WinForms, FlowDocument/FixedDocument WPF, bibliothèques PDF et rapports générés par Excel/Word — selon les besoins.
1. L’essentiel d’abord
- Pour une liste simple ou une extension de WinForms existant,
PrintDocumentsuffit. Les sauts de page reposent sur la répétition dePrintPagejusqu’à ce queHasMorePagessoitfalse; sans cela, seule la première page est imprimée. 12 - Le DPI d’impression diffère entièrement du DPI écran.
PageSettings.PrinterResolutionest la résolution de l’imprimante ;HardMarginX/HardMarginYsont la zone physique non imprimable etMarginsla marge logique. Réutiliser directement les coordonnées écran fausse la mise en page. 345 - WPF distingue le chemin GDI et le chemin XPS. WPF utilise nativement XPS et convertit automatiquement vers GDI si l’imprimante ne prend pas XPSDrv en charge. 67
FlowDocumentest fluide,FixedDocumentfixe. Le premier privilégie la lisibilité et se relaye selon la fenêtre/résolution ; le second la fidélité à l’affichage ou à l’imprimante. 89- Format et bac passent par
PrintDialog,PrintTicketetPrintQueue. Validez et fusionnez la demande avec les capacités réelles de l’imprimante viaPrintQueue.MergeAndValidatePrintTicket. 1011 - Microsoft Print to PDF est interactif. Il demande par défaut l’emplacement d’enregistrement ; il ne convient donc pas aux lots PDF sans surveillance. 12
- Microsoft ne prend pas en charge l’automatisation Office dans un serveur ou service. Office suppose un bureau interactif et un profil utilisateur ; une exécution non interactive peut être instable ou se bloquer. 13
- Les services Windows s’exécutent en Session 0. Ils ne peuvent pas afficher de boîtes de dialogue et ne doivent pas dépendre de l’imprimante par défaut de la session utilisateur. 14
- Une fuite d’objets GDI finit par faire tomber le processus. Les handles GDI ont une limite finie par processus, configurable par
GDIProcessHandleQuota. 15
Table de décision — besoin × approche
| Besoin | Approche appropriée | Pourquoi |
|---|---|---|
| Quelques pages d’une liste ou d’un bordereau simple, WinForms | PrintDocument |
Classes standard suffisantes et réutilisation du dessin GDI+ existant |
| Rapports à nombreux traits, plusieurs formats ou mise en page complexe | Bibliothèque de rapports ou mise en page fixe avec FixedDocument |
Calcul de coordonnées, sauts de page et typographie japonaise sont coûteux à développer |
| Impression par lot importante sans surveillance | PrintDocument.Print() programmatique / soumission directe à XpsDocumentWriter |
Zéro boîte de dialogue et pas de dépendance à l’imprimante par défaut |
| PDF seulement, sans impression | Génération directe avec une bibliothèque PDF | Sans boîte de dialogue, imprimante par défaut ni état du spooler |
| Aperçu obligatoire et circuit de validation interne | PrintPreviewDialog WinForms, DocumentViewer WPF |
Interface standard de revue avant impression |
| Réutilisation d’un modèle Excel existant | Génération directe Open XML SDK ou automatisation COM limitée | Réutilise les actifs, mais évitez COM dans un service résident |
2. Le minimum sur le fonctionnement de l’impression Windows
L’architecture Windows combine spooler et pilote par imprimante. L’application appelle des fonctions indépendantes du périphérique et le travail peut aller vers laser, traceur, imprimante matricielle, fax, etc. 16
- Chemin GDI : une application Win32 GDI envoie les commandes comme EMF ou laisse le pilote rendre l’image avant le spooler. Les applications WinForms traditionnelles utilisent ce chemin. 166
- Chemin XPS : les pilotes XPS et WPF envoient un document XPS au spooler ; si l’imprimante n’est pas XPSDrv, Windows convertit en GDI. 67
Un écran et une impression ne se ressemblent pas lorsque les DPI sont confondus : l’écran est autour de 96 DPI, l’imprimante souvent autour de 600 DPI. Construisez plutôt les coordonnées en centièmes de pouce ou en unité réelle. Tenez aussi compte de la zone physique non imprimable : HardMarginX/HardMarginY limitent l’impression même si Margins est réglé à zéro. 345
3. WinForms : System.Drawing.Printing en pratique
L’impression WinForms repose sur PrintDocument : configurez PrinterSettings/DefaultPageSettings, dessinez dans PrintPage, puis appelez Print(). 1
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Printing;
using System.Windows.Forms;
public sealed class ReportPrinter
{
// Données à imprimer. Une ligne est supposée par élément du bordereau de commande.
private readonly IReadOnlyList<string> _lines;
private int _lineIndex;
public ReportPrinter(IReadOnlyList<string> lines) => _lines = lines;
public void Print(string printerName)
{
using var document = new PrintDocument();
document.DocumentName = "Détails de la commande";
if (!string.IsNullOrEmpty(printerName))
{
document.PrinterSettings.PrinterName = printerName;
}
// Devient false ici si le nom de l'imprimante est inconnu (ne permet pas de détecter une imprimante hors ligne).
if (!document.PrinterSettings.IsValid)
{
throw new InvalidOperationException(
$"L'imprimante spécifiée est introuvable : {printerName}");
}
_lineIndex = 0;
document.PrintPage += Document_PrintPage;
document.Print();
}
private void Document_PrintPage(object? sender, PrintPageEventArgs e)
{
// MarginBounds est la zone de dessin qui reflète la marge logique (Margins).
// PageBounds couvre toute la feuille ; tout dessin dans la marge matérielle est rogné.
using var font = new Font("Meiryo", 10);
float lineHeight = font.GetHeight(e.Graphics);
float y = e.MarginBounds.Top;
while (_lineIndex < _lines.Count)
{
if (y + lineHeight > e.MarginBounds.Bottom)
{
// Cette page se termine ici. Il reste du contenu à dessiner ; passez à la page suivante.
e.HasMorePages = true;
return;
}
e.Graphics.DrawString(_lines[_lineIndex], font, Brushes.Black,
e.MarginBounds.Left, y);
y += lineHeight;
_lineIndex++;
}
// Toutes les lignes sont dessinées ; c'est donc la dernière page.
e.HasMorePages = false;
}
}
Le cas classique où « la deuxième page ne sort jamais » vient d’un mauvais HasMorePages. Vérifiez qu’il reste des lignes avant de le mettre à true, et laissez-le à false après la dernière page. QueryPageSettings permet de modifier taille/orientation par page. 12 Pour les marges, utilisez MarginBounds et tenez compte de HardMarginX/HardMarginY, puis testez plusieurs modèles d’imprimantes. PrintPreviewDialog réutilise le même PrintDocument pour l’aperçu ; les résolutions disponibles sont dans PrinterSettings.PrinterResolutions. 43
4. WPF : FlowDocument / FixedDocument et PrintDialog
FlowDocumentest orienté lisibilité, se remet en page selon fenêtre et polices, et inclut recherche/pagination/modes d’affichage. 8FixedDocumentest orienté fidélité WYSIWYG et laisse à l’application le contrôle complet de la mise en page. 9
Employez FlowDocument pour un rapport interne fluide et FixedDocument pour un bordereau ou une étiquette où les traits et champs doivent rester à une position précise. L’impression de base utilise System.Windows.Controls.PrintDialog, différent de System.Windows.Forms.PrintDialog. 10
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
public static class FlowDocumentPrinter
{
public static void Print(FlowDocument document, string jobName)
{
var printDialog = new PrintDialog();
// N'imprimez que si l'utilisateur a cliqué sur OK.
if (printDialog.ShowDialog() != true)
{
return;
}
// FlowDocument suppose des tailles de page différentes pour l'affichage et l'impression ;
// ajustez donc la taille et les marges de la page cible à la zone imprimable de l'imprimante.
var paginator = ((IDocumentPaginatorSource)document).DocumentPaginator;
paginator.PageSize = new Size(
printDialog.PrintableAreaWidth, printDialog.PrintableAreaHeight);
printDialog.PrintDocument(paginator, jobName);
}
}
Pour contrôler format, bac ou recto-verso, employez PrintQueue, PrintTicket et PrintCapabilities, puis validez la demande par PrintQueue.MergeAndValidatePrintTicket. 711
using System.Linq;
using System.Printing;
public static class DuplexPrintTicketBuilder
{
public static PrintTicket BuildDuplexTicket(PrintQueue printQueue)
{
PrintCapabilities capabilities = printQueue.GetPrintCapabilities();
// Vérifiez au préalable si l'imprimante prend en charge l'impression recto-verso (reliure sur le bord long).
bool supportsDuplex = capabilities.DuplexingCapability
.Contains(Duplexing.TwoSidedLongEdge);
var requestedTicket = new PrintTicket();
if (supportsDuplex)
{
requestedTicket.Duplexing = Duplexing.TwoSidedLongEdge;
}
// Ne fusionnez que la demande d'impression recto-verso avec le PrintTicket par défaut de l'utilisateur.
// Les paramètres non pris en charge sont automatiquement remplacés par des valeurs valides.
ValidationResult result = printQueue.MergeAndValidatePrintTicket(
printQueue.UserPrintTicket, requestedTicket);
return result.ValidatedPrintTicket;
}
}
Pour un rapport FixedDocument, XpsDocumentWriter.Write/WriteAsync envoie directement dans la file XPS ; la conversion GDI est automatique pour une imprimante non XPSDrv. 7
5. Options de sortie PDF
« Nous voulons du PDF » peut désigner : choisir manuellement l’enregistrement PDF lors d’une impression, produire un PDF depuis l’application en passant par la boîte de dialogue, ou produire de gros volumes sans impression ni intervention.
Microsoft Print to PDF convient aux deux premiers cas, pas au dernier. C’est une file et un pilote intégrés à Windows 10 et plus récents, visibles comme imprimante ordinaire, mais conçus pour demander un nom/emplacement de fichier à chaque impression. 12 Pour un lot non surveillé vers un chemin fixe, générez directement le PDF avec une bibliothèque dédiée.
| Axe | À vérifier |
|---|---|
| Licence | OSS MIT/Apache ou GPL/AGPL, ou licence commerciale ; vérifiez toujours texte de licence et site vendeur pour obligations de publication, redistribution et usage commercial |
| Polices japonaises | Incorporation partielle des polices, incorporation de la donnée de police et support de l’écriture verticale ou IVS si nécessaire |
| Fonctions de rapport | Modèles, codes-barres/2D, signatures numériques, PDF/A et facilité de reprise d’une mise en page papier existante |
| Dépendances / runtime | Fonctionnement serveur sans UI ou conteneur, dépendances natives, historique .NET Core/.NET |
| Performances | Mémoire lors de gros volumes/pages et comportement en génération parallèle |
Les licences dépendent du produit et de la version : vérifiez la documentation officielle avant adoption. Cet article ne recommande pas une bibliothèque nommée ; il donne les axes de décision.
6. Rapports via Excel / Word comme option
Quand un modèle Excel existe déjà, le réutiliser est parfois plus réaliste qu’une reconstruction. Deux approches existent :
- Générer/modifier directement xlsx/docx avec Open XML SDK ou équivalent. Le format ZIP+XML est lu/écrit sans lancer Excel/Word, ce qui fonctionne en serveur. Voir How to Build Excel Report Output — COM / Open XML / Templates.
- Piloter
Excel.Applicationavec COM Interop. Cela réutilise le classeur, macros et calculs, mais les références COM non libérées peuvent laisserEXCEL.EXEactif ; voir The EXCEL.EXE-Stays-Running Problem in C# Excel Automation.
Microsoft documente officiellement l’automatisation Office depuis serveur ou service Windows comme « non recommandée et non prise en charge ». Office est pensé pour un bureau interactif et un profil utilisateur ; en environnement non interactif, instabilité et deadlock sont possibles. 13 Dans un service résident ou lot, préférez la génération de fichier Open XML ; si COM est vraiment incontournable, exécutez-le dans une application résidente d’une session utilisateur interactive, pas dans un service.
7. Pièges pratiques
Impression depuis un service Windows — Session 0
Les services s’exécutent en Session 0, isolée des utilisateurs interactifs depuis Vista. Une boîte de dialogue peut être créée mais l’utilisateur ne la voit pas ; attendre sa réponse donne l’impression d’un blocage. 14 Les imprimantes réseau ou mappées par utilisateur sont également visibles différemment depuis le contexte du service. Décidez dès la conception quelles imprimantes sont visibles depuis quel compte et quelle session ; voir Understanding Windows Session Isolation et How to Build and Operate a Windows Service.
Lorsque l’imprimante est absente ou hors ligne
Un PrinterSettings.PrinterName inconnu ou l’absence d’imprimante par défaut échoue à l’impression. Validez avec PrinterSettings.IsValid, concevez la relance et la notification d’erreur, et ne considérez pas la soumission au spooler comme la fin : une imprimante réseau hors ligne peut garder le travail en attente.
Danger de dépendre de l’imprimante par défaut
Ne pas indiquer explicitement PrinterSettings.PrinterName rend le comportement risqué : changement par l’utilisateur ou par déplacement du portable et le rapport part vers une destination inattendue. Les rapports critiques doivent conserver l’imprimante cible dans un fichier de configuration ou des réglages applicatifs.
Fuites de handles GDI en fonctionnement long
Sans using ou Dispose() fiable pour Graphics, Font, Brush ou Pen, les handles GDI finissent par manquer et les appels de dessin échouent. Leur limite par processus est finie et configurable de 256 à 65 536 via GDIProcessHandleQuota. 15 Le problème apparaît souvent seulement en production dans une application résidentielle qui imprime fréquemment ; voir Investigating a Long-Running Crash in an Industrial Camera — The Handle-Leak Edition.
Articles connexes
- How to Build Excel Report Output — COM / Open XML / Templates
- The EXCEL.EXE-Stays-Running Problem in C# Excel Automation — COM Reference Release Patterns and the Replacement Decision
- How to Build and Operate a Windows Service — From Choosing Between It and Task Scheduler to Turning a BackgroundService into a Service
- Understanding Windows Session Isolation — Session 0, RDP, and Concurrent Multi-User Sessions
- Investigating a Long-Running Crash in an Industrial Camera — The Handle-Leak Edition
Domaines de conseil associés
KomuraSoft LLC assure le conseil technique sur l’impression et la sortie de rapports des applications métier Windows, la reconstruction de rapports tirant parti d’actifs Excel existants et la conception d’impression/sortie pour services résidents.
Références
-
Microsoft Learn, PrintDocument Class. Rôle de
PrintDocumentet impression WPF viaSystem.Printing. ↩ ↩2 ↩3 -
Microsoft Learn, PrintPageEventArgs.HasMorePages Property. Valeur par défaut
falseet répétition dePrintPagejusqu’àfalse. ↩ ↩2 -
Microsoft Learn, PageSettings.PrinterResolution Property. Résolution de l’imprimante et choix dans
PrinterSettings.PrinterResolutions. ↩ ↩2 ↩3 -
Microsoft Learn, PageSettings.HardMarginX Property. Hard margin, marge physique de l’imprimante. ↩ ↩2 ↩3
-
Microsoft Learn, PageSettings.Margins Property. Marge par défaut et zone imprimable. ↩ ↩2
-
Microsoft Learn, Windows Print Path Overview. Chemins GDI et XPS. ↩ ↩2 ↩3
-
Microsoft Learn, Printing documents overview. Impression XPS WPF, PrintDialog, PrintTicket/Capabilities/Queue/XpsDocumentWriter et conversion GDI. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Flow Document Overview. Document fluide adapté à la taille de fenêtre et résolution. ↩ ↩2
-
Microsoft Learn, FixedDocument Class. WYSIWYG fixe pour fidélité écran/imprimante. ↩ ↩2
-
Microsoft Learn, PrintDialog Class. PrintDialog WPF et réglage PrintTicket/PrintQueue. ↩ ↩2
-
Microsoft Learn, How to: Validate and Merge PrintTickets. Capabilités et validation MergeAndValidatePrintTicket. ↩ ↩2
-
Microsoft Learn, RemoveMPDW. Microsoft Print to PDF, file/pilote et configuration de l’image. ↩ ↩2
-
Microsoft Support, Considerations for server-side Automation of Office. Office non recommandé/non pris en charge en automatisation non interactive côté serveur/service. ↩ ↩2
-
Microsoft Learn, Service Changes for Windows Vista - Session 0 Isolation. Isolation de Session 0 et boîtes de dialogue non visibles des services. ↩ ↩2
-
Microsoft Learn, GDI Objects. Limite de handles GDI et
GDIProcessHandleQuota. ↩ ↩2 -
Microsoft Learn, Introduction to printing. Spooler, pilotes et EMF GDI. ↩ ↩2
Articles associés
Articles récents partageant les mêmes étiquettes, pour approfondir des sujets proches.
Identifier précisément les lenteurs avec PerfView et dotnet-trace — Introduction pratique à l'analyse des performances .NET
Lorsqu'une application métier est « lente », « sature le processeur » ou « se fige parfois », quel outil utiliser et que faut-il observer...
Introduction au journal des événements Windows et à ETW — placer les journaux de votre application métier sur les mécanismes standard de l'OS
Votre application métier Windows repose-t-elle seulement sur les journaux de fichiers ? Le journal des événements et ETW sont visibles à ...
Au-delà de appsettings.json — guide pratique de la gestion de configuration dans les applications métier Windows (environnements, secrets et emplacements d'écriture)
Guide pratique de la configuration des applications métier Windows : superposition de appsettings.json, choix entre IConfiguration et IOp...
Lancer Google Ads avec un petit budget BtoB — concevoir et exploiter une routine hebdomadaire efficace avec quelques dizaines de milliers de yens par mois
Guide pratique de Google Ads pour les entreprises BtoB souhaitant démarrer avec seulement quelques dizaines de milliers de yens par mois....
Faire apparaître son site pour les recherches par nom de zone — guide pratique du SEO local pour les PME (pages de zone et Google Business Profile)
Pour les PME dont le site n'apparaît pas sur une recherche « nom de zone + secteur ». Cet article présente l'ordre de correction du SEO l...
Sujets associés
Ces pages replacent le sujet dans un contexte plus large de services et de décisions.
Thèmes techniques Windows
Portail des sujets sur le développement Windows, l'analyse des incidents et la valorisation des actifs existants.
Thread UI et minuteries
Thread UI WPF / WinForms, flux asynchrones, Dispatcher et temporisation.
Services liés à ce sujet
Cet article est directement lié aux services suivants.
Développement d'applications Windows
La conception et l'implémentation de fonctions d'impression et de sortie PDF pour applications métier relèvent du conseil en développement d'applications Windows.
Questions fréquentes
Questions souvent posées lors d’une consultation sur le sujet de cet article.
- Dois-je créer l'impression avec PrintDocument ou avec la pile WPF ?
- Le choix direct est PrintDocument pour une UI WinForms et FlowDocument/FixedDocument pour une UI WPF. Il n'est pas nécessaire de mélanger les technologies : le critère pratique est de correspondre au framework UI existant. Pour un nouveau projet WPF, décidez d'emblée si le rapport doit être fluide ou à mise en page fixe afin de centrer la conception sur FlowDocument ou FixedDocument.
- Faut-il acheter une bibliothèque de rapports ?
- Si réaliser vous-même, avec GDI+ ou WPF, des rapports complexes à nombreux traits, plusieurs formats de papier, polices japonaises incorporées ou écriture verticale coûte cher, une bibliothèque commerciale revient souvent moins cher. À l'inverse, un simple rapport comme une liste A4 convient très bien à PrintDocument ou FixedDocument. Évaluez d'abord la complexité des exigences.
- Peut-on produire uniquement des PDF sans imprimer ?
- Oui. Le PDF est un format de sortie ; le générer directement par une bibliothèque PDF, sans boîte de dialogue ni imprimante par défaut, convient souvent bien mieux au traitement par lot et à l'automatisation. Microsoft Print to PDF est un pilote conçu pour une interaction et non pour une génération PDF non surveillée.
- Cela peut-il aussi fonctionner avec une imprimante d'étiquettes ou de tickets ?
- C'est possible, mais étendre l'approche généraliste PrintDocument/FixedDocument peut devenir difficile. La plupart des imprimantes d'étiquettes ont leur propre langage de commande ou SDK et sont pilotées hors des chemins d'impression Windows standard. Vérifiez d'abord si le modèle cible fonctionne comme pilote Windows ordinaire ou exige un SDK dédié.
Profil de l’auteur
Page de présentation de l’auteur de l’article.
Go Komura
Représentant de KomuraSoft LLC
Spécialisé dans le développement de logiciels Windows, le conseil technique et l’analyse de pannes, notamment pour les systèmes existants et les incidents difficiles à reproduire.
Liens publics