Connexion Inscription
Avancé .NET Framework 4.7.2

WordApplication - Automatisation de Microsoft Word en C#

Classe C# permettant d'automatiser Microsoft Word : gestion des documents, fusion de fichiers, insertion de textes, images et tableaux, export PDF, gestion des imprimantes et impression.

WordApplication – Automatisation de Microsoft Word en C# :

WordApplication est une classe utilitaire développée en C# permettant de piloter Microsoft Word depuis une application .NET grâce à Microsoft Office Interop.
Elle regroupe différentes fonctionnalités destinées à automatiser la gestion des documents Word : ouverture, fermeture, insertion de contenu, fusion de fichiers, export PDF et impression.

Fonctionnalités principales :

  • Ouverture et fermeture d'une instance de Microsoft Word.
  • Ouverture et fermeture de documents existants.
  • Enregistrement des documents au format PDF.
  • Gestion du changement d'imprimante et restauration de l'imprimante initiale.
  • Impression des documents avec suivi de la fin du traitement.
  • Fusion de plusieurs fichiers Word à partir d'un signet.
  • Navigation entre les pages d'un document.
  • Recherche et positionnement sur les signets.
  • Insertion de texte à un emplacement défini.
  • Mise en forme du texte : gras, italique, soulignement et alignement.
  • Insertion et redimensionnement d'images.
  • Création de tableaux.
  • Gestion de l'impression

Une attention particulière est portée à la gestion de l'impression : l'impression est lancée au premier plan et le module attend que Word ait transmis le travail au spouleur Windows avant de poursuivre certaines opérations.

La classe prévoit également un délai d'attente configurable et la restauration de l'imprimante initialement utilisée.

Intégration

La classe expose des méthodes statiques permettant d'intégrer directement les fonctionnalités de Word dans un projet C#.
Elle s'appuie sur Microsoft Office Interop et nécessite donc un environnement disposant de Microsoft Word.

Le code source

🔒 Copier / Télécharger le codeRéservé aux membres. Se connecter · Créer un compte
WordApplication.csC# · 465 lignes
using Microsoft.Office.Interop.Word;
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using Application = Microsoft.Office.Interop.Word.Application;

public static class WordApplication
{
    private static Application appWord;
    private static Document myDocument;
    private static Range myRange;
    private static Bookmark mySignet;
    private static InlineShape myImage;
    private static string previousPrinter;
    private static object M = System.Reflection.Missing.Value;

    /// <summary>
    /// Ouverture d'une instance Word.
    /// </summary>
    public static void OpenWord(bool visible = true)
    {
        appWord = new Application();
        appWord.Visible = visible;
        if (!visible)
            appWord.DisplayAlerts = WdAlertLevel.wdAlertsNone;

        // CORRECTION IMPRESSION : impression au premier plan,
        // PrintOut ne rend la main qu'une fois le travail transmis au spouleur
        appWord.Options.PrintBackground = false;

        myDocument = null;
    }

    /// <summary>
    /// Fermeture de l'instance Word (restaure l'imprimante par défaut si elle a été changée).
    /// </summary>
    public static void CloseWord(bool closeDoc = true)
    {
        if (closeDoc)
            CloseDocument();

        if (appWord == null)
            return;

        // CORRECTION IMPRESSION : on attend la fin de l'impression avant de quitter
        WaitForPrinting();
        RestorePrinter();

        object _FALSE = false;
        try
        {
            appWord.Application.Quit(ref _FALSE, ref M, ref M);
        }
        catch (Exception e)
        {
            Debug.WriteLine(e.Message);
        }
        finally
        {
            Marshal.FinalReleaseComObject(appWord);
            appWord = null;
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }
    }

    /* ***************************************************************************** */

    /// <summary>
    /// Ouvrir un document dans l'instance Word.
    /// </summary>
    /// <returns>false si le fichier n'existe pas ou n'a pas pu être ouvert.</returns>
    public static bool OpenDocument(string fileName)
    {
        if (appWord == null || !File.Exists(fileName))
            return false;

        object _FALSE = false;
        object _TRUE = true;
        object ofileName = fileName;
        try
        {
            myDocument = appWord.Documents.Open(ref ofileName, ref M, ref _FALSE, ref _FALSE, ref M, ref M, ref M, ref M, ref M, ref M, ref M, ref _TRUE);
            return true;
        }
        catch (Exception e)
        {
            Debug.WriteLine(e.Message);
            myDocument = null;
            return false;
        }
    }

    /// <summary>
    /// Fermeture du document de l'instance Word (sans enregistrer).
    /// Attend la fin de l'impression éventuelle avant de fermer.
    /// </summary>
    public static void CloseDocument()
    {
        if (myDocument == null)
            return;

        // CORRECTION IMPRESSION : on attend la fin de l'impression avant de fermer
        WaitForPrinting();

        object _FALSE = false;
        try
        {
            myDocument.Close(ref _FALSE, ref M, ref M);
        }
        catch (Exception e)
        {
            Debug.WriteLine(e.Message);
        }
        finally
        {
            ReleaseDocumentObjects();
        }
    }

    /// <summary>
    /// Sauvegarde du document au format PDF.
    /// </summary>
    public static bool SaveDocument(string fileName, out string errorMessage)
    {
        errorMessage = null;
        if (string.IsNullOrEmpty(fileName))
        {
            errorMessage = "Nom de fichier vide.";
            return false;
        }
        if (myDocument == null)
        {
            errorMessage = "Aucun document ouvert.";
            return false;
        }

        try
        {
            string dir = Path.GetDirectoryName(fileName);
            if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
                Directory.CreateDirectory(dir);
        }
        catch (Exception e)
        {
            errorMessage = $"Impossible de créer le dossier : {e.Message}";
            return false;
        }

        object oFileName = fileName;
        object oFileFormat = WdSaveFormat.wdFormatPDF;
        try
        {
            myDocument.SaveAs(ref oFileName, ref oFileFormat, ref M, ref M, ref M, ref M, ref M, ref M, ref M, ref M, ref M, ref M, ref M, ref M, ref M, ref M);
            return true;
        }
        catch (Exception e)
        {
            errorMessage = e.Message;
            return false;
        }
    }

    /* ***************************************************************************** */

    /// <summary>
    /// Changement de l'imprimante (attention : modifie aussi l'imprimante par défaut de Windows).
    /// </summary>
    /// <returns>false si Word n'a pas accepté l'imprimante demandée.</returns>
    public static bool PrinterChange(string printer)
    {
        if (appWord == null || string.IsNullOrEmpty(printer))
            return false;

        try
        {
            if (previousPrinter == null)
                previousPrinter = appWord.ActivePrinter;

            appWord.ActivePrinter = printer;
            appWord.Options.PrintBackground = false;

            Thread.Sleep(1000);

            // Word renvoie "Nom sur Ne01:" : on vérifie juste le début
            string actuelle = appWord.ActivePrinter;
            if (!actuelle.StartsWith(printer, StringComparison.OrdinalIgnoreCase))
            {
                Debug.WriteLine($"Imprimante non changée. Demandée : {printer} / Active : {actuelle}");
                return false;
            }
            return true;
        }
        catch (Exception e)
        {
            Debug.WriteLine($"Erreur changement imprimante ({printer}) : {e.Message}");
            return false;
        }
    }

    /// <summary>
    /// Restaure l'imprimante active avant le premier PrinterChange().
    /// </summary>
    public static void RestorePrinter()
    {
        if (previousPrinter == null || appWord == null)
            return;
        try
        {
            appWord.ActivePrinter = previousPrinter;
        }
        catch (Exception e)
        {
            Debug.WriteLine(e.Message);
        }
        previousPrinter = null;
    }

    /// <summary>
    /// Impression du document avec l'instance Word.
    /// Ne rend la main qu'une fois le travail transmis au spouleur Windows.
    /// </summary>
    /// <returns>false en cas d'erreur ou si l'impression n'est pas terminée après le délai.</returns>
    public static bool PrintDocument(int timeoutSecondes = 60)
    {
        if (myDocument == null)
            return false;

        try
        {
            // CORRECTION IMPRESSION : impression explicitement au premier plan
            myDocument.PrintOut(Background: false);
        }
        catch (Exception e)
        {
            Debug.WriteLine($"Erreur impression : {e.Message}");
            return false;
        }

        return WaitForPrinting(timeoutSecondes);
    }

    /// <summary>
    /// CORRECTION IMPRESSION : attend que Word ait vidé sa file d'impression.
    /// </summary>
    private static bool WaitForPrinting(int timeoutSecondes = 60)
    {
        if (appWord == null)
            return true;

        int maxBoucles = timeoutSecondes * 10;
        try
        {
            for (int i = 0; i < maxBoucles; i++)
            {
                if (appWord.BackgroundPrintingStatus == 0)
                    return true;
                Thread.Sleep(100);
            }
        }
        catch (Exception e)
        {
            Debug.WriteLine(e.Message);
        }
        Debug.WriteLine("Délai d'attente d'impression dépassé.");
        return false;
    }

    /* ***************************************************************************** */

    /// <summary>
    /// Insère plusieurs fichiers à l'emplacement du signet "FinPage" du fichier principal.
    /// </summary>
    public static bool FusionnerPlusieurFichiers(string FichierPrincipal, string[] FichiersAInserer)
    {
        if (!OpenDocument(FichierPrincipal))
            return false;
        if (!LocationSignet("FinPage"))
            return false;

        object Type = WdBreakType.wdPageBreak;
        myRange.InsertBreak(ref Type);
        for (int i = FichiersAInserer.Length - 1; i > -1; i--)
        {
            string FileName = FichiersAInserer[i];
            if (File.Exists(FileName))
                myRange.InsertFile(FileName, ref M, ref M, ref M, ref M);
        }
        DeletePage(FichiersAInserer.Length + 2);
        return true;
    }

    /// <summary>
    /// Se place au début de la page et efface le caractère précédent (saut de page).
    /// </summary>
    public static void DeletePage(int NumeroPage)
    {
        GoToPage(NumeroPage);
        Selection MaSelection = appWord.Selection;
        MaSelection.TypeBackspace();
    }

    /// <summary>
    /// Positionne le range et la sélection au début de la page indiquée.
    /// </summary>
    public static void GoToPage(int NumeroPage)
    {
        object what = WdGoToItem.wdGoToPage;
        object which = WdGoToDirection.wdGoToFirst;
        object Numero = NumeroPage;
        myRange = myDocument.GoTo(ref what, ref which, ref Numero, ref M);
        myRange.Select();
    }

    /// <summary>
    /// Insère du texte après le signet.
    /// </summary>
    public static bool TextToSignet(string momSignet, string texte)
    {
        if (LocationSignet(momSignet))
        {
            myRange.InsertAfter(texte);
            return true;
        }
        return false;
    }

    /// <summary>
    /// Se positionne sur le signet (range + sélection).
    /// </summary>
    public static bool LocationSignet(string momSignet)
    {
        if (myDocument == null)
            return false;

        object nomSignet = momSignet;
        Bookmarks allSignets = myDocument.Bookmarks;
        if (allSignets.Exists(momSignet))
        {
            mySignet = allSignets.get_Item(ref nomSignet);
            mySignet.Select();
            myRange = mySignet.Range;
            return true;
        }
        return false;
    }

    /// <summary>
    /// Retourne la collection des signets du document.
    /// </summary>
    public static Bookmarks GetAllSignet()
    {
        try
        {
            return myDocument?.Bookmarks;
        }
        catch (Exception e)
        {
            Debug.WriteLine(e.Message);
        }
        return null;
    }

    public static bool BoldRange(string momSignet, int value)
    {
        if (LocationSignet(momSignet))
        {
            myRange.Font.Bold = value;
            return true;
        }
        return false;
    }

    public static bool ItalicRange(string momSignet, int value)
    {
        if (LocationSignet(momSignet))
        {
            myRange.Font.Italic = value;
            return true;
        }
        return false;
    }

    public static bool UnderLineRange(string momSignet, WdUnderline value)
    {
        if (LocationSignet(momSignet))
        {
            myRange.Font.Underline = value;
            return true;
        }
        return false;
    }

    /// <summary>
    /// Aligne le paragraphe du range courant.
    /// </summary>
    public static void AlignmentRange(WdParagraphAlignment Alignement)
    {
        if (myRange != null)
            myRange.ParagraphFormat.Alignment = Alignement;
    }

    /// <summary>
    /// Insère une image dans le range courant.
    /// </summary>
    public static bool InsertImage(string FichierImage)
    {
        if (myRange == null || !File.Exists(FichierImage))
            return false;

        object False = false;
        object True = true;
        myImage = myRange.InlineShapes.AddPicture(FichierImage, ref False, ref True, ref M);
        return true;
    }

    /// <summary>
    /// Redimensionne la dernière image insérée (en %).
    /// </summary>
    public static void ImageSize(int Pourcent)
    {
        if (myImage == null)
            return;

        float Height = myImage.Height;
        float Width = myImage.Width;
        myImage.Height = Height * Pourcent / 100f;
        myImage.Width = Width * Pourcent / 100f;
    }

    /// <summary>
    /// Insère un tableau dans le range courant.
    /// </summary>
    public static void InsertTable(int NombreLignes, int NombreColonnes)
    {
        if (myDocument == null || myRange == null)
            return;

        object defaultTableBehavior = WdDefaultTableBehavior.wdWord9TableBehavior;
        object autoFitBehavior = WdAutoFitBehavior.wdAutoFitFixed;
        myDocument.Tables.Add(myRange, NombreLignes, NombreColonnes, ref defaultTableBehavior, ref autoFitBehavior);
        myRange = myDocument.Tables[myDocument.Tables.Count].Range;
    }

    /* ***************************************************************************** */

    private static void ReleaseDocumentObjects()
    {
        Release(myImage); myImage = null;
        Release(mySignet); mySignet = null;
        Release(myRange); myRange = null;
        Release(myDocument); myDocument = null;
    }

    private static void Release(object o)
    {
        if (o != null && Marshal.IsComObject(o))
        {
            try { Marshal.ReleaseComObject(o); }
            catch (Exception e) { Debug.WriteLine(e.Message); }
        }
    }
}