Serveur d'impression

Isolation du pilote d'imprimante au niveau de l'application – peteronprogramming – Bien choisir son serveur d impression

Le 9 mars 2020 - 9 minutes de lecture

TL; DR: vous pouvez isoler les pilotes d'imprimante de vos applications en modifiant légèrement les manifestes de vos applications, ce qui améliore la stabilité.

Personne n'aime les invités non invités, surtout s'ils sont impolis ou ne respectent pas les règles. Ils viennent, falsifient des choses qui ne devraient pas être falsifiées, ils brûlent parfois votre maison, ou même vous empêchent de mourir en paix. Sous Windows, il existe de nombreux types d'invités indésirables, tels que les extensions shell, les programmes utilisant l'injection de DLL et les pilotes d'imprimante.

Les applications ont très peu de contrôle sur ces composants, de sorte que leur qualité affecte également la qualité de vos produits logiciels. Les utilisateurs ne se soucient pas si votre application se bloque régulièrement à cause d'une extension du shell bâclée, car à la fin de la journée, c'est votre produit qui a planté. Dans cet article de blog, je voudrais attirer votre attention sur une caractéristique quelque peu obscure de Windows: l'isolement du pilote d'imprimante au niveau de l'application.

L'application sur laquelle je travaille quotidiennement dans le cadre de mon travail de jour est un programme de CAO, de sorte que l'impression est une caractéristique importante et souvent utilisée du produit. Les utilisateurs utilisent toutes sortes d'imprimantes, allant des modèles grand public habituels aux traceurs de qualité industrielle coûtant des milliers de dollars. Malheureusement, ce niveau d'utilisation et de variété signifie que le produit en question souffre plus de pilotes d'imprimante buggy qu'une application moyenne. Si le pilote d'imprimante se bloque, il arrête tout le programme, car il est chargé dans l'espace d'adressage de l'application. Ou pire, s'il corrompt le tas, il peut ne provoquer qu'une erreur plus tard, ce qui rend le problème beaucoup plus difficile à diagnostiquer.

Un exemple d'accident

Avant de passer à l'atténuation, voyons un plantage réel du pilote d'imprimante. Ci-dessous, vous pouvez trouver le code d'un petit programme de test1 qui provoque une version très spécifique du pilote d'imprimante PCL 5 de HP pour corrompre le tas. La reproduction de ce plantage par vous-même nécessite une certaine préparation (vous n'avez cependant pas besoin d'imprimer ou même de connecter une imprimante à votre ordinateur):

  1. Téléchargez la version 5.9.0.18326 du pilote HP x64 PCL 5 depuis le serveur FTP de HP
  2. Installez-le, lorsque l'option vous est présentée, sélectionnez «Mode dynamique»
  3. Ouvrez «Panneau de configuration» / «Périphériques et imprimantes»
  4. Cliquez avec le bouton droit sur «HP Universal Printing PCL 5» et sélectionnez «Définir comme imprimante par défaut»
  5. Compilez (x64) et exécutez l'extrait de code ci-dessous


#comprendre 

#comprendre 
#comprendre 

int main ()

  PRINTDLGW dialogResult = ;
  dialogResult.lStructSize = taille de dialogResult;
  dialogResult.Flags = PD_RETURNDEFAULT;

  PrintDlgW (& dialogResult);

  DEVMODEW * pDefaultDevMode =
    (DEVMODEW *) GlobalLock (dialogResult.hDevMode);
  caractère non signé * pDriverPrivateData =
    (caractère non signé *) pDefaultDevMode + sizeof (DEVMODEW);

  // Offsets magiques ...
  pour (size_t i = 3290; i < 3300; ++i)
    pDriverPrivateData[i] = 0;

  HDC hDC = CreateICW (L"winspool",
                       pDefaultDevMode->dmDeviceName,
                       L "LPT1:",
                       pDefaultDevMode);

  std :: cout << "CreateICW" <<
    (hDC? "réussi!": "échoué!") << std :: endl;

Si j'exécute ce programme sur ma machine, j'obtiens un crash environ 1 fois sur 10 à un stade tardif de l'exécution. Pour une meilleure reproductibilité, activons le tas de pages pour cet exécutable (utilisez gflags ou Application Verifier à partir du SDK Windows), car il s'agit d'une corruption de tas après tout. Cela le rend 100% reproductible: une violation d'accès est atteinte2 à chaque exécution de la ligne en surbrillance. Je voudrais souligner que ce n'est pas la boucle qui provoque la corruption réelle, qui endommage simplement les données privées du conducteur qui sont transmises au conducteur avec le CreateIC une fonction. La corruption de segment de mémoire se produit à la suite de ces données corrompues dans le pilote tout en servant le CreateIC appel.

Cela émule pratiquement très bien un scénario du monde réel: une application rencontre un pilote défectueux qui arrête tout le processus. Notre objectif est de faire de ce programme ne tombe pas en panne malgré le pilote d'imprimante qui se comporte mal.

Atténuons

Bonne nouvelle: il existe un moyen de demander au système de charger les pilotes d'imprimante dans un processus distinct. Tout ce que nous avons à faire est de fournir un soi-disant manifeste d'application pour l'exécutable, avec le printerDriverIsolation attribut défini sur true (voici la documentation).

Créez un fichier avec le ".manifeste"Et le contenu suivant:



  
    
      vrai
    
  

Dans Visual Studio, accédez aux propriétés de votre projet, puis sous "Outil manifeste" / "Entrée et sortie", ajoutez le chemin de ce fichier manifeste à la propriété "Fichiers manifeste supplémentaires". Cela ajoutera le manifeste en tant que ressource dans l'exécutable.

Si vous avez tout fait correctement, après avoir reconstruit et exécuté le projet, vous devriez remarquer les changements de comportement suivants:

  1. L'application ne plante plus (même pas avec le tas de pages activé)
  2. Lors de l'exécution de l'application, un nouveau processus nommé splwow64.exe est généré et le pilote d'imprimante sera chargé dans ce processus
Le programme de test sans et avec isolation du pilote d'imprimante au niveau de l'application

Et en gros c'est tout, il suffisait d'un changement très simple pour atténuer un problème complexe (n'oubliez pas de désinstaller le pilote3, si vous avez suivi).

Pensées de clôture

Si vous recherchez «isolation du pilote d'imprimante» sur Google, vous trouverez des articles de blog et la documentation officielle. Ils décrivent une fonction qui isole les pilotes d'imprimante du processus du spouleur d'impression (spoolsv.exe). Ceci est une fonctionnalité différente de celle que je viens de montrer, qui est niveau application isolation du pilote d'imprimante.

Je me souviens très clairement que je suis tombé sur cette fonctionnalité par accident, quand j'ai dû chercher autre chose sur la page de documentation du manifeste d'application. Je souhaite que Microsoft ait créé une page de documentation dédiée à cette fonctionnalité, car en ce moment il y a très peu d'informations disponibles, et il est facile de confondre les deux différents types d'isolement du pilote d'imprimante.

Par exemple, sur la page décrivant l'isolement au niveau du spouleur, il est indiqué qu'il nécessite la prise en charge des pilotes eux-mêmes, qu'ils doivent déclarer dans leurs fichiers INF. Est-ce également le cas pour l'isolement au niveau de l'application? La réponse est non, mais au lieu d'obtenir ces informations de la documentation, je devais m'en assurer moi-même avec une expérience4.

Par souci de concision, la gestion des erreurs et les désallocations de ressources sont omises.

Remarquez comment j'ai évité de dire «crash». C'est parce que malheureusement, la violation d'accès est avalée par winspool.drv (un gestionnaire d'exceptions dans la fonction CallDrvDocumentEventNative, pour être exact), qui est le composant qui contient l'implémentation des fonctions API d'impression et des interfaces avec le spouleur. C'est dommage (mieux vaut un crash aujourd'hui, qu'une corruption de tas demain), mais si je dois faire une supposition, c'est très probablement pour des raisons de compatibilité. Dans le reste du message, quand je dis crash, je veux dire violation d'accès qui est avalée.

C'est plus difficile et plus fastidieux que vous ne le pensez:

  1. Ouvrez «Panneau de configuration» / «Périphériques et imprimantes»
  2. Faites un clic droit sur «HP Universal Printing PCL 5», sélectionnez «Supprimer le périphérique»
  3. Sélectionnez une autre imprimante et cliquez sur «Propriétés du serveur d'impression»
  4. Sur l'onglet «Pilotes», recherchez et sélectionnez «HP Universal Printing 5»
  5. Cliquez sur "Supprimer", sélectionnez "Supprimer le pilote et le package de pilotes", appuyez sur OK
  6. Cliquez sur «Oui» dans la boîte de dialogue de confirmation
  7. Cliquez sur le bouton "Supprimer" dans la nouvelle boîte de dialogue
  8. Le pilote est supprimé (même si la boîte de dialogue se plaint de l'impossibilité d'accéder au pilote)

Voici ce que j'ai fait:

  1. J'ai téléchargé un pilote d'imprimante (tout le monde le ferait)
  2. Avant de l'installer, j'ai localisé toutes les entrées «DriverIsolation» dans les fichiers * .inf du package et modifié leurs valeurs à zéro
  3. Comme dans cet état, la vérification de signature aurait échoué sur le pilote, j'ai redémarré ma machine en mode test (dans une invite de commande admin, j'ai tapé bcdedit.exe -set TESTSIGNING ON, puis redémarré mon ordinateur)
  4. Installé le pilote, fait de la nouvelle imprimante celle par défaut
  5. A exécuté le programme de test avec le manifeste d'application intégré
  6. J'ai remarqué que l'isolement au niveau de l'application fonctionnait toujours
  7. Désinstallé le pilote
  8. Désactivé le mode de signature de test (dans une invite de commande d'administration, j'ai tapé bcdedit.exe -set TESTSIGNING OFF, puis redémarré mon ordinateur)

Commentaires

Laisser un commentaire

Votre commentaire sera révisé par les administrateurs si besoin.