2009-10-21 6 views
4

Il y a au moins trois parties à ce problème, donc supporter avec moi:Sous Windows, comment créer un processus fils et capturer stdin, stdout et stderr sans dupliquer les poignées héritables?

1) CreateProcess a un paramètre bInheritHandles, qui provoque le processus de l'enfant d'hériter de tous les poignées héritables dans le processus parent. Cette option doit être définie sur TRUE pour permettre au parent de spécifier les poignées stdin, stdout et stderr pour l'enfant dans le paramètre STARTUPINFO.

2) Dans Win32, la suppression et le changement de nom des fichiers peuvent échouer lorsque plus d'un descripteur est ouvert sur le même fichier.

3) La fonction open() de Microsoft CRT créera par défaut des handles héritables. De plus, les handles de fichiers créés par défaut souffrent du problème 2 ci-dessus.

Cette combinaison magique crée le problème opérationnel suivant: La bibliothèque A appelle open() et ne s'attend pas à ce que les renames et suppressions suivantes échouent. Ailleurs dans le processus, une autre bibliothèque B appelle CreateProcess avec bInheritHandles défini sur TRUE (pour capturer stdin/out/err) créant temporairement des handles dupliqués. Désormais, les opérations de fichiers de la bibliothèque A échouent parfois. Naturellement, les bibliothèques A et B sont entretenues par des personnes distinctes. Je connais aussi une autre bibliothèque A 'qui utilise open() et souffre d'un problème similaire.

Ce kb article traite d'un problème et d'une solution liés. Toutefois, il repose toujours sur l'appel de CreateProcess avec bInheritHandles défini sur TRUE dans le processus parent, il ne résout donc pas ce problème.

Je me demande si d'autres ont rencontré ce problème et s'il n'y a pas de solution connue? L'article kb ci-dessus implique essentiellement qu'appeler CreateProcess avec bInheritHandles défini sur TRUE est racé, donc mon penchant est de réparer la bibliothèque B de façon à ce qu'elle ne le fasse jamais. Je ferais ceci par:

  1. Créer un processus intermédiaire suspendu (idéalement en utilisant rundll pour exécuter un point d'entrée personnalisé dans la bibliothèque B) avec bInheritHandles mis à FAUX.
  2. Créez des tubes stdin/out/err et copiez les bonnes extrémités de ceux-ci dans le processus intermédiaire.
  3. Passez les poignées duped au processus intermédiaire en quelque sorte.
  4. Reprendre le processus intermédiaire.
  5. À partir du processus intermédiaire, remplissez le STARTUPINFO avec les canaux du parent et appelez CreateProcess avec bInheritHandles défini sur TRUE.

Est-ce une bonne stratégie ou existe-t-il une meilleure solution? Comment recommanderiez-vous de passer les poignées dup au processus intermédiaire à l'étape 3? Rundll + point d'entrée personnalisé est-il un moyen fiable de configurer le processus intermédiaire à l'étape 1?

Répondre

1

Si vous avez accès aux handles de fichiers réels, vous pouvez utiliser SetHandleInformation() pour supprimer l'indicateur HANDLE_FLAG_INHERIT avant d'appeler CreateProcess().

0

Vous pouvez utiliser la fonction ZwQuerySystemInformation (SystemHandleInformation, ...) ntdll.dll pour rechercher toutes les poignées appartenant à votre processus, puis toutes SetHandleInformation sur chacune, comme suggéré par Remy, pour supprimer l'indicateur HANDLE_FLAG_INHERIT.

+0

Malheureusement, il y a toujours une course où l'ouverture est appelée par un autre thread entre ZwQuerySystemInformation et CreateProcess. De même, quelqu'un d'autre pourrait dépendre d'un handle héritable. –

+0

L'article de la base de connaissances indique que "Windows dupliquera toujours les poignées STD, même si bInheritHandles est défini sur FALSE.". Avez-vous essayé d'utiliser SetStdHandle pour modifier les poignées std de votre processus, puis appelez CreateProcess avec bInheritHandles == FALSE. Il va de soi que vous devrez l'emballer dans une section critique et être sûr qu'aucun autre thread n'essaiera d'utiliser stdout, etc. en même temps. – atomice

+0

C'est un truc mignon! Cependant, je préfère ne pas changer stdin/out/err de sous d'autres threads. Je suis toujours penché vers l'approche décrite dans la question, mais je ne l'ai pas encore mise en œuvre. –

1

Vous pouvez utiliser l'attribut étendu PROC_THREAD_ATTRIBUTE_HANDLE_LIST pour spécifier explicitement exactement quel processus gère un processus particulier.

L'article de blog de Raymond Chen "Programmatically controlling which handles are inherited by new processes in Win32" inclut un exemple de code pour ce faire.

La version courte:

  • InitializeProcThreadAttributeList() pour créer une liste d'attributs

  • UpdateProcThreadAttribute pour spécifier les poignées d'hériter

  • lpAttributeList membre situé dans STARTUPINFOEX

  • Ensemble de drapeaux EXTENDED_STARTUPINFO_PRESENT dans l'appel à CreateProcess

Nécessite Windows Vista, donc peut-être pas résolu le problème de cette question quand OPs a été demandé, mais tout le monde en utilisant Vista ou plus tard maintenant, non? :-)

Questions connexes