Partager via


Extensions d’imprimante

Important

La plateforme d’impression moderne est le moyen privilégié de Windows pour communiquer avec les imprimantes. Nous vous recommandons d’utiliser le pilote de classe IPP en boîte de Microsoft, ainsi que les applications de support d’impression (PSA), pour personnaliser l’expérience d’impression dans Windows 10 et 11 pour le développement de périphériques d’impression.

Pour plus d’informations, veuillez consulter la section Plateforme d’impression moderne et le Guide de conception des applications de support d’impression.

Les applications d’extension d’imprimante prennent en charge les préférences d’impression et les notifications d’imprimante lorsque les utilisateurs exécutent des applications existantes sur le bureau Windows.

Les extensions d’imprimante peuvent être développées dans n’importe quel langage compatible COM, mais sont optimisées pour être développées à l’aide de Microsoft .NET Framework 4. Les extensions d’imprimante peuvent être distribuées avec un package de pilote d’imprimante, si elles sont compatibles avec XCopy et n’ont pas de dépendances sur des runtimes externes autres que ceux inclus avec le système d’exploitation, par exemple, .NET. Si l’application d’extension d’imprimante ne répond pas à ces critères, elle peut être distribuée dans un package setup.exe ou MSI, et affichée dans l’expérience Device Stage de l’imprimante en utilisant la directive PrinterExtensionUrl spécifiée dans le manifeste v4. Lorsque l’application d’extension d’imprimante est distribuée via un package MSI, vous avez la possibilité d’ajouter le pilote d’impression au package ou de le laisser de côté et de distribuer le pilote séparément. Le PrinterExtensionUrl est affiché dans l’expérience des préférences d’imprimante.

Les administrateurs informatiques disposent de quelques options pour gérer la distribution des extensions d’imprimante. Si l’application est empaquetée dans un setup.exe ou MSI, alors les administrateurs informatiques peuvent utiliser des outils de distribution de logiciels standard tels que Microsoft Endpoint Configuration Manager, ou ils peuvent inclure les applications dans leur image standard du système d’exploitation. Les administrateurs informatiques peuvent également remplacer le PrinterExtensionUrl spécifié dans le manifeste v4, s’ils modifient HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers\<nom de la file d’impression>\PrinterDriverData\PrinterExtensionUrl.

Et si une entreprise choisit de bloquer complètement les extensions d’imprimante, cela peut être fait via une stratégie de groupe appelée « Configuration ordinateur\Modèles d’administration\Imprimantes\Ne pas autoriser les pilotes d’imprimante v4 à afficher les applications d’extension d’imprimante ».

Développer une extension d’imprimante

Lorsque vous développez une extension d’imprimante, il y a six domaines principaux sur lesquels vous devez vous concentrer. Ces domaines de concentration sont présentés dans la liste suivante.

  • Enregistrement

  • Activation des événements

  • Gestionnaire d’événements OnDriverEvent

  • Préférences d’impression

  • Notifications d’imprimante

  • Gestion des imprimantes

Enregistrement

Les extensions d’imprimante sont enregistrées avec le système d’impression en spécifiant un ensemble de clés de registre ou en spécifiant les informations de l’application dans la section PrinterExtensions du fichier manifeste v4.

Il existe des GUID spécifiés qui prennent en charge chacun des différents points d’entrée pour les extensions d’imprimante. Vous n’êtes pas obligé d’utiliser ces GUID dans le fichier manifeste v4, mais vous devez connaître les valeurs GUID pour utiliser le format de registre pour l’installation du pilote v4. Le tableau suivant montre les valeurs GUID pour les deux points d’entrée.

Point d’entrée GUID
Préférences d’impression {EC8F261F-267C-469F-B5D6-3933023C29CC}
Notifications d’imprimante {23BB1328-63DE-4293-915B-A6A23D929ACB}

Les extensions d’imprimante qui sont installées en dehors du pilote d’imprimante doivent être enregistrées en utilisant le registre. Cela garantit que les extensions d’imprimante peuvent être installées quel que soit l’état du spouleur ou du module de configuration v4 sur la machine cliente.

Une fois le service PrintNotify démarré, il vérifiera les clés de registre sous le chemin [OfflineRoot] et traitera toutes les inscriptions ou désinscriptions en attente. Une fois les inscriptions ou désinscriptions en attente terminées, les clés de registre sont supprimées en temps réel. Si vous utilisez un script ou un processus itératif pour placer des clés de registre, vous devrez peut-être recréer la clé \[PrinterExtensionID] chaque fois que vous spécifiez une clé \[PrinterDriverId]. Les clés incomplètes ou mal formées ne sont pas supprimées.

Cet enregistrement n’est nécessaire qu’à la première installation. L’exemple suivant montre le format correct de clé de registre utilisé pour enregistrer des extensions d’imprimante.

Remarque

[OfflineRoot] est utilisé comme abréviation pour HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\OfflinePrinterExtensions.

[OfflineRoot]
    \[PrinterExtensionId] {GUID}
           AppPath=[PrinterExtensionAppPath] {String}
           \[PrinterDriverId] {GUID}
                  \[PrinterExtensionReasonGuid]
(default) = ["0"|"1"] {REG_SZ 0:Unregister, 1:Register}
                  \…
                  \[PrinterExtensionReasonGuidN]
           \[PrinterDriverId2]
                  \[PrinterExtensionReasonGuid2.1]
                  \…
                  \[PrinterExtensionReasonGuid2.Z]
           …
           \[PrinterDriverIdM]
    \[PrinterExtensionId2]
    …
    \[PrinterExtensionIdT]

Par exemple, l’ensemble de clés suivant enregistrerait une extension d’imprimante avec le PrinterExtensionID {PrinterExtensionIDGuid} et un chemin entièrement qualifié vers l’exécutable "C:\Program Files\Fabrikam\pe.exe" pour les PrinterDriverIDs {PrinterDriverID1Guid} et {PrinterDriverID2Guid}, avec les raisons de préférences d’imprimante et de notifications d’imprimante.

[OfflineRoot]
    \{PrinterExtensionIDGuid}
           AppPath="C:\Program Files\Fabrikam\pe.exe"
           \{PrinterDriverID1Guid}
                 \{EC8F261F-267C-469F-B5D6-3933023C29CC}
            (default) = "1"
                 \{23BB1328-63DE-4293-915B-A6A23D929ACB}
            (default) = "1"
           \{PrinterDriverID1Guid}
                 \{EC8F261F-267C-469F-B5D6-3933023C29CC}
            (default) = "1"
                 \{23BB1328-63DE-4293-915B-A6A23D929ACB}
            (default) = "1"

Pour désinstaller la même extension d’imprimante, l’ensemble de clés suivant devrait être spécifié.

[OfflineRoot]
    \{PrinterExtensionIDGuid}
           AppPath="C:\Program Files\Fabrikam\pe.exe"
           \{PrinterDriverID1Guid}
                 \{EC8F261F-267C-469F-B5D6-3933023C29CC}
            (default) = "0"
                 \{23BB1328-63DE-4293-915B-A6A23D929ACB}
            (default) = "0"
           \{PrinterDriverID1Guid}
                 \{EC8F261F-267C-469F-B5D6-3933023C29CC}
            (default) = "0"
                 \{23BB1328-63DE-4293-915B-A6A23D929ACB}
            (default) = "0"

Étant donné que les extensions d’imprimante peuvent fonctionner à la fois dans un contexte lancé par l’utilisateur et dans un contexte lancé par un événement, il est utile de pouvoir déterminer le contexte dans lequel votre extension d’imprimante fonctionne. Cela peut permettre à une application, par exemple, de ne pas énumérer le statut sur toutes les files si elle a été lancée pour une notification ou des préférences d’impression. Microsoft recommande que les extensions d’imprimante installées séparément du pilote (par exemple, avec un MSI ou setup.exe) utilisent des commutateurs de ligne de commande soit dans les raccourcis du menu Démarrer, soit dans l’entrée AppPath qui a été renseignée dans le registre lors de l’enregistrement. Étant donné que les extensions d’imprimante installées avec le pilote sont installées dans le DriverStore, elles ne seront pas lancées en dehors des événements de préférences d’impression ou de notifications d’imprimante. Par conséquent, la spécification de commutateurs de ligne de commande n’est pas prise en charge dans ce cas.

Lorsque l’extension d’imprimante s’enregistre pour l’actuel PrinterDriverID, elle doit inclure le PrinterDriverID dans l’AppPath. Par exemple, pour une application d’extension d’imprimante nommée printerextension.exe, et une valeur PrinterDriverID de {GUID}, le [PrinterExtensionAppPath] ressemblerait à l’exemple suivant :

"C:\program files\fabrikam\printerextension.exe {GUID}"

Activation des événements

Au moment de l’exécution, les extensions d’imprimante doivent activer le déclenchement d’événements pour l’actuel PrinterDriverID. C’est le PrinterDriverID qui a été passé à l’application via le tableau args[], et il permet au système d’impression de fournir un contexte d’événement approprié pour gérer des raisons telles que les préférences d’impression ou les notifications d’imprimante.

L’application doit donc créer un nouveau PrinterExtensionManager pour l’actuel PrinterDriverID, enregistrer un délégué pour gérer l’événement OnDriverEvent, et appeler la méthode EnableEvents avec le PrinterDriverID. L’extrait de code suivant illustre cette approche.

PrinterExtensionManager mgr = new PrinterExtensionManager();
mgr.OnDriverEvent += OnDriverEvent;
mgr.EnableEvents(new Guid(PrinterDriverID1));

Si une application n’appelle pas EnableEvents dans les 5 secondes, Windows expirera et lancera une interface utilisateur standard. Afin d’atténuer cela, les extensions d’imprimante doivent suivre les dernières bonnes pratiques de performance, notamment les suivantes :

  • Retarder autant que possible l’initialisation de l’application, jusqu’après avoir appelé EnableEvents. Après cela, prioriser la réactivité de l’interface utilisateur en utilisant des méthodes asynchrones et en ne bloquant pas le thread de l’interface utilisateur pendant l’initialisation.

  • Utilisez ngen pour générer une image native lors de l’installation. Pour plus d’informations, veuillez consulter la section Native Image Generator.

  • Utilisez des outils de mesure de performance pour identifier les problèmes de performance lors du chargement. Pour plus d’informations, veuillez consulter la section Windows Performance Analysis Tools.

Gestionnaire d’événements DriverEvent

Après qu’un gestionnaire d’événements OnDriverEvent est enregistré et que les événements sont activés, si l’extension d’imprimante a été lancée pour gérer les préférences d’impression ou les notifications d’imprimante, alors le gestionnaire sera invoqué. Dans l’extrait de code précédent, une méthode appelée OnDriverEvent a été enregistrée en tant que gestionnaire d’événements. Dans l’extrait de code suivant, le paramètre PrinterExtensionEventArgs est l’objet qui permet de construire les scénarios de préférences d’impression et de notifications d’imprimante. PrinterExtensionEventArgs est un wrapper pour IPrinterExtensionEventArgs.

static void OnDriverEvent(object sender, PrinterExtensionEventArgs eventArgs)
{
    //
    // Display the print preferences window.
    //

    if (eventArgs.ReasonId.Equals(PrinterExtensionReason.PrintPreferences))
    {
        PrintPreferenceWindow printPreferenceWindow = new PrintPreferenceWindow();
        printPreferenceWindow.Initialize(eventArgs);

        //
        // Set the caller application's window as parent/owner of the newly created printing preferences window.
        //

        WindowInteropHelper wih = new WindowInteropHelper(printPreferenceWindow);
        wih.Owner = eventArgs.WindowParent;

        //
        // Display a modal/non-modal window based on the 'WindowModal' parameter.
        //

        if (eventArgs.WindowModal)
        {
            printPreferenceWindow.ShowDialog();
        }
        else
        {
            printPreferenceWindow.Show();
        }
    }

    //
    // Handle driver events.
    //

    else if (eventArgs.ReasonId.Equals(PrinterExtensionReason.DriverEvent))
    {
        // Handle driver events here.
    }
}

Afin de prévenir une mauvaise expérience utilisateur associée à des extensions d’imprimante plantant ou lentes, Windows implémente un délai d’expiration si EnableEvents n’est pas appelé dans un court laps de temps après le lancement de l’application. Pour activer le débogage, ce délai d’expiration est désactivé s’il y a un débogueur attaché au service PrintNotify.

Dans la plupart des cas, cependant, tout le code lié à l’application qui nous intéresse s’exécute pendant ou après le rappel OnDriverEvent. Pendant le développement, il peut également être utile d’afficher une MessageBox avant de commencer une expérience de préférences d’impression ou de notifications d’imprimante à partir du rappel OnDriverEvent. Lorsque la MessageBox apparaît, retournez à Visual Studio et sélectionnez Déboguer>Attacher au processus et choisissez le nom de votre processus. Enfin, retournez à votre MessageBox et sélectionnez OK pour reprendre. Cela garantira que vous voyez les exceptions et que vous atteignez tous les points d’arrêt à partir de ce moment-là.

De nouveaux ReasonIds peuvent être pris en charge à l’avenir. En conséquence, les extensions d’imprimante doivent vérifier explicitement le ReasonID et ne doivent pas utiliser une instruction « else » pour détecter le dernier ReasonID connu. Si un ReasonID est reçu et inconnu, l’application doit se fermer correctement.

Les préférences d’impression sont pilotées par l’objet PrintSchemaEventArgs.Ticket. Cet objet encapsule à la fois les documents PrintTicket et PrintCapabilities qui décrivent les fonctionnalités et les options pour un périphérique. Bien que le XML sous-jacent soit également disponible, le modèle objet facilite le travail avec ces formats.

À l’intérieur de chaque objet IPrintSchemaTicket ou IPrintSchemaCapabilities se trouvent des fonctionnalités (IPrintSchemaFeature) et des options (IPrintSchemaOption). Bien que les interfaces utilisées pour les fonctionnalités et les options soient les mêmes, quelle que soit l’origine, le comportement varie légèrement en raison du XML sous-jacent. Par exemple, les documents PrintCapabilities spécifient de nombreuses options par fonctionnalité, tandis que les documents PrintTicket spécifient uniquement l’option sélectionnée (ou par défaut). De même, les documents PrintCapabilities spécifient des chaînes d’affichage localisées, alors que les documents PrintTicket ne le font pas.

Pour plus d’informations sur la liaison de données dans WPF, veuillez consulter la section Aperçu de la liaison de données.

Afin d’optimiser les performances, Microsoft recommande que les appels à GetPrintCapabilities ne soient effectués que lorsqu’il est nécessaire de mettre à jour le document PrintCapabilities.

Lorsque l’utilisateur fait des choix à l’aide des contrôles de zone de liste déroulante liés aux données, l’objet PrintTicket est automatiquement mis à jour. Lorsque l’utilisateur clique finalement sur OK, une chaîne de validation asynchrone et de finalisation commence. Ce modèle asynchrone est largement utilisé pour éviter que des tâches de longue durée ne se produisent sur les threads de l’interface utilisateur et ne provoquent des blocages dans l’interface utilisateur des préférences d’impression ou dans l’application qui imprime. Voici une liste des étapes utilisées pour traiter les modifications du PrintTicket après que l’utilisateur clique sur OK.

  1. Le PrintSchemaTicket est validé de manière asynchrone en utilisant la méthode IPrintSchemaTicket::ValidateAsync.

  2. Lorsque la validation asynchrone est terminée, le Common Language Runtime (CLR) invoque la méthode PrintTicketValidateCompleted.

    1. Si la validation a réussi, elle appelle la méthode CommitPrintTicketAsync, et CommitPrintTicketAsync appelle la méthode IPrintSchemaTicket::CommitAsync. Et lorsque la mise à jour du PrintTicket est terminée avec succès, cela invoque la méthode PrintTicketCommitCompleted, qui appelle une méthode de commodité qui appelle la méthode PrinterExtensionEventArgs.Request.Complete pour indiquer que les préférences d’impression sont terminées, puis elle ferme l’application.

    2. Sinon, elle présente une interface utilisateur à l’utilisateur pour gérer la situation de contrainte.

Si l’utilisateur a cliqué sur annuler ou a fermé directement la fenêtre des préférences d’impression, l’extension d’imprimante appelle IPrinterExtensionEventArgs.Request.Cancel avec une valeur HRESULT appropriée et un message pour le journal des erreurs.

Si le processus pour l’extension d’imprimante a été fermé et n’a pas appelé les méthodes Complete ou Cancel comme décrit dans les paragraphes précédents, alors le système d’impression reviendra automatiquement à l’interface utilisateur fournie par Microsoft.

Pour récupérer les informations d’état du périphérique, les extensions d’imprimante peuvent utiliser Bidi pour interroger le périphérique d’impression. Par exemple, pour afficher l’état de l’encre ou d’autres types d’état du périphérique, les extensions d’imprimante peuvent utiliser la méthode IPrinterExtensionEventArgs.PrinterQueue.SendBidiQuery pour émettre des requêtes Bidi vers le périphérique. Obtenir le dernier statut Bidi est un processus en deux étapes impliquant la configuration d’un gestionnaire d’événements pour l’événement OnBidiResponseReceived et l’appel de la méthode SendBidiQuery avec une requête Bidi valide. L’extrait de code suivant montre ce processus en deux étapes.

PrinterQueue.OnBidiResponseReceived += new
EventHandler<PrinterQueueEventArgs>(OnBidiResponseReceived);
PrinterQueue.SendBidiQuery("\\Printer.consumables");

Lorsque la réponse Bidi est reçue, le gestionnaire d’événements suivant est invoqué. Ce gestionnaire d’événements comprend également une implémentation simulée de l’état de l’encre, ce qui peut être utile pour le développement lorsqu’un périphérique n’est pas disponible. L’objet PrinterQueueEventArgs inclut à la fois un HRESULT et une réponse Bidi XML. Pour plus d’informations sur les réponses Bidi XML, veuillez consulter la section Schémas de requêtes et réponses Bidi.

private void OnBidiResponseReceived(object sender, PrinterQueueEventArgs e)
{
    if (e.StatusHResult != (int)HRESULT.S_OK)
    {
        MockInkStatus();
        return;
    }

    //
    // Display the ink levels from the data.
    //

    BidiHelperSource = new BidiHelper(e.Response);
    if (PropertyChanged != null)
    {
        PropertyChanged(this, new PropertyChangedEventArgs("BidiHelperSource"));
    }
    InkStatusTitle = "Ink status (Live data)";
}

Notifications d’imprimante

Les notifications d’imprimante sont invoquées de la même manière que les préférences d’impression. Dans le gestionnaire d’événements OnDriverEvent, si IPrinterExtensionEventArgs indique qu’un ReasonID correspond au GUID DriverEvents, alors nous pouvons créer une expérience pour gérer cet événement.

Les variables suivantes sont les plus utiles pour gérer une expérience fonctionnelle de notifications d’imprimante.

  • PrinterExtensionEventArgs.BidiNotification – Cela transporte le Bidi XML qui a déclenché l’événement.

  • PrinterExtensionEventArgs.DetailedReasonId – Cela contient le GUID eventID du fichier XML d’événements du pilote.

L’attribut le plus important de l’objet IPrinterExtensionEventArgs pour les notifications est la propriété BidiNotification. Cela transporte le Bidi XML qui a déclenché l’événement. Pour plus d’informations sur les réponses Bidi XML, veuillez consulter la section Schémas de requêtes et réponses Bidi.

Gestion des imprimantes

Afin de soutenir le rôle d’une extension d’imprimante en tant qu’application pouvant être utilisée comme un hub pour la gestion/maintenance des imprimantes, il est possible d’énumérer les files d’impression pour lesquelles l’extension d’imprimante actuelle est enregistrée, et d’obtenir le statut de chaque file. Cela n’est pas démontré dans le projet PrinterExtensionSample, mais l’extrait de code suivant pourrait être ajouté dans la méthode Main de App.xaml.cs pour enregistrer un gestionnaire d’événements.

mgr.OnPrinterQueuesEnumerated += new EventHandler<PrinterQueuesEnumeratedEventArgs>(mgr_OnPrinterQueuesEnumerated);

Une fois les files énumérées, le gestionnaire d’événements est appelé et les opérations de statut peuvent avoir lieu. Cet événement se déclenche périodiquement pendant la durée de vie de l’application afin de garantir que la liste des files d’impression énumérées est à jour, même si l’utilisateur a installé plus de files depuis son ouverture. En conséquence, il est important que le gestionnaire d’événements ne crée pas une nouvelle fenêtre à chaque fois qu’il est exécuté, comme le montre l’extrait de code suivant.

static void mgr_OnPrinterQueuesEnumerated(object sender, PrinterQueuesEnumeratedEventArgs e)
{
    foreach (IPrinterExtensionContext pContext in e)
    {
        // show status
    }
}

Pour effectuer des tâches de maintenance à l’aide d’une extension d’imprimante, Microsoft recommande d’utiliser l’API WritePrinter héritée comme décrit par le pseudo-code suivant.

OpenPrinter
    StartDocPrinter
        StartPagePrinter
          WritePrinter
        EndPagePrinter
    EndDocPrinter
ClosePrinter

Bonnes pratiques de performance pour les extensions d’imprimante

Afin de garantir la meilleure expérience utilisateur, les extensions d’imprimante doivent être conçues pour se charger le plus rapidement possible. Le projet Printer Extension Sample est une application .NET, ce qui signifie qu’elle est construite dans un langage intermédiaire (IL) qui doit être compilé au moment de l’exécution dans le format approprié pour l’architecture du processeur natif. Lors de l’installation, Microsoft recommande que les extensions d’imprimante soient installées selon les bonnes pratiques, pour s’assurer que l’application a été compilée pour l’architecture du système natif. Pour plus d’informations sur la compilation de code et les bonnes pratiques d’installation, veuillez consulter la section Amélioration des performances de lancement pour vos applications de bureau.

Microsoft recommande également que les extensions d’imprimante reportent les tâches d’initialisation telles que le chargement des ressources jusqu’après l’appel de la méthode EnableEvents. Cela minimise la probabilité que l’application appelle EnableEvents avant le délai d’expiration de 5 secondes pour les extensions d’imprimante.

Après l’appel OnDriverEvent, les extensions d’imprimante doivent initialiser leur interface utilisateur et dessiner le plus rapidement possible, en utilisant des méthodes asynchrones lorsque cela est possible pour garantir la réactivité. Les extensions d’imprimante ne doivent pas avoir de dépendance sur les appels réseau ou Bidi pour créer l’état initial de la fenêtre pour les préférences d’impression ou les notifications d’imprimante.

À mesure que l’utilisateur fait des choix en utilisant l’interface utilisateur à l’écran qui affecte le PrintTicket, l’extension d’imprimante doit utiliser la méthode IPrintSchemaTicket::ValidateAsync pour valider les modifications dès que possible. Enfin, l’extension d’imprimante doit utiliser la méthode IPrintSchemaTicket::CommitAsync pour valider les modifications du PrintTicket.

Les extensions d’imprimante sont toujours exécutées hors processus à partir du processus qui les a invoquées. Vous devez donc garder à l’esprit le comportement de la fenêtre lorsque vous développez une extension d’imprimante :

  • La propriété WindowParent de IPrinterExtensionEventArgs spécifie le handle de la fenêtre qui a invoqué l’application.
  • La propriété WindowModal de IPrinterExtensionEventArgs spécifie si une extension d’imprimante (en mode préférences d’impression) doit être exécutée de manière modale.

Le projet Printer Extension Sample montre comment créer une interface utilisateur généralement lancée comme fenêtre au premier plan. Mais dans certains cas, l’interface utilisateur ne sera pas affichée au premier plan, comme lorsque le processus qui a provoqué l’invocation de l’interface utilisateur s’exécute à un niveau d’intégrité différent, ou lorsque le processus est compilé pour une architecture de processeur différente. Dans ce cas, l’extension d’imprimante doit appeler FlashWindowEx pour demander l’autorisation de l’utilisateur de passer au premier plan en faisant clignoter l’icône dans la barre des tâches.

Schémas de requêtes et réponses Bidi

Vue d’ensemble de la liaison de données

Amélioration des performances de lancement pour vos applications de bureau

Native Image Generator

Interfaces de schéma d’impression

Outils d’analyse des performances de Windows