CreateProcess et processus infini

cs_Rankin Messages postés 52 Date d'inscription lundi 29 mars 2004 Statut Membre Dernière intervention 27 avril 2010 - 4 juin 2009 à 13:07
cs_Rankin Messages postés 52 Date d'inscription lundi 29 mars 2004 Statut Membre Dernière intervention 27 avril 2010 - 12 juin 2009 à 15:59
Salut à tous,

Je suis en train de développer une appli qui lance pas mal de commandes système pour récupérer notamment la configuration du réseau. Pour me simplifier la vie, j'ai écrit une fonction qui exécute une commande et retourne (ou non) le résultat de celle-ci en passant par un pipe. Cette fonction semblait marcher à merveille, mais j'ai un problème à l'exécution de la commande ipconfig /all : d'ordinaire j'attends que le programme que je lance soit terminé avant d'en récupérer le résultat, mais cette commande ne se termine jamais et je la tue au bout de 5 secondes (je remplace le INFINITE par 5000). C'est très laid et j'aimerais donc savoir si il y a quelque chose dans cette fonction qui n'est pas propre comme il faut.
D'autre part, le résultat n'est pas complet : avec 9 interfaces réseau, les deux dernières ne sont pas affichées.

 En voici le code, avec les include et un main, y'a plus qu'à compiler pour tester :

#include <windows.h>
#include
#include <string.h>
#include <fstream>

using namespace std;

string commandLine(string command, bool hide, bool encode)
{
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    SECURITY_ATTRIBUTES sa;
    HANDLE hPipeOutput, hPipeInput;
    DWORD dwRead;
    unsigned short size = 4096;
    char buffer[size+1];
    memset(buffer,0,sizeof(buffer));
    string res = "";
    sa.bInheritHandle = TRUE;
    sa.lpSecurityDescriptor = 0;
    sa.nLength = sizeof(SECURITY_ATTRIBUTES);

    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
    si.dwFlags = STARTF_USESTDHANDLES;
    //si.wShowWindow = SW_HIDE;
    ZeroMemory(&pi, sizeof(pi));
   
    if(!hide && CreatePipe(&hPipeOutput, &hPipeInput, &sa, 0))   
        si.hStdOutput = hPipeInput;
    else
        //Au cas où la création du pipe ait échoué, on ne lira pas dedans
        hide = true;
    if(CreateProcess(0, (char*)command.c_str(), NULL, NULL, TRUE, CREATE_NO_WINDOW/*|DETACHED_PROCESS*/, NULL, NULL, &si, &pi))
    {
        WaitForSingleObject(pi.hProcess, INFINITE);
        DWORD dwExitCode = 0;
        GetExitCodeProcess(pi.hProcess, &dwExitCode);
        //Si processus toujours présent, on le tue
        if(dwExitCode == STILL_ACTIVE)
        {
            TerminateProcess(pi.hProcess, 0);
        }
        if (!hide)
        {
            bool end = false;
            while (!end)
            {
                if (GetFileSize (hPipeOutput, NULL) >0 && ReadFile(hPipeOutput, buffer, size, &dwRead, NULL))
                {
                    if (encode)
                        CharToOem(buffer,buffer);
                    res += buffer;
                    memset(buffer,0,sizeof(buffer));       
                }
                else
                    end = true;
            }
        }
    }
   
    if(hPipeInput)
        CloseHandle(hPipeInput);

    if(hPipeOutput)
        CloseHandle(hPipeOutput);
    return res;
}

int main()
{
    cout<<commandLine("ipconfig /all",false,true)<<endl;
   return 0;
}

N'hésitez pas à me faire part de vos critiques, même si ce n'est pas de là que vient l'erreur ! C'est toujours bon d'avoir l'avis d'autres personnes pour progresser...
Merci à vous !

8 réponses

vecchio56 Messages postés 6535 Date d'inscription lundi 16 décembre 2002 Statut Membre Dernière intervention 22 août 2010 14
4 juin 2009 à 20:04
Si tu n'obtiens pas toutes tes interfaces réseau, je suppose que c'est parce que tu tues le thread avant qu'il ne se termine. A ta place je me demanderais plutot pourquoi la commande ipconfig /all ne se termine pas.

_____________________________________
0
cs_Rankin Messages postés 52 Date d'inscription lundi 29 mars 2004 Statut Membre Dernière intervention 27 avril 2010
4 juin 2009 à 20:18
C'est effectivement la principale question que je me pose, étant donné que toutes les autres commandes que j'appelle fonctionnent :)
Et a priori non, le résultat tronqué n'est pas dû à la fin d'exécution que je provoque, en allongeant le temps j'ai exactement la même chose. Et si j'exécute la commande à la main dans la console, le résultat est instantané.
0
vecchio56 Messages postés 6535 Date d'inscription lundi 16 décembre 2002 Statut Membre Dernière intervention 22 août 2010 14
4 juin 2009 à 20:28
D'accord j'avais compris que la commande bloquait aussi quand tu la lances à la main.
Je ne reproduis pas le problème chez moi...
Tu as identifié à quel endroit ton thread se bloque?

_____________________________________
0
cs_Rankin Messages postés 52 Date d'inscription lundi 29 mars 2004 Statut Membre Dernière intervention 27 avril 2010
4 juin 2009 à 21:30
Tu ne reproduis pas le problème, c'est-à-dire que chez toi ce code ne se bloque pas ?
Non je ne sais pas où ça bloque, et à vrai dire je ne sais pas comment faire pour le savoir...
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
vecchio56 Messages postés 6535 Date d'inscription lundi 16 décembre 2002 Statut Membre Dernière intervention 22 août 2010 14
4 juin 2009 à 21:39
Oui ca ne se bloque pas chez moi.
Pour savoir ou ca bloque, tu utilises un debugger ou tu utilise un système de trace (comme l'écriture dans un fichier par exemple), ce qui te permettra de voir ou ca bloque. A priori c'est sans doute le ReadFile.

_____________________________________
0
cs_Rankin Messages postés 52 Date d'inscription lundi 29 mars 2004 Statut Membre Dernière intervention 27 avril 2010
4 juin 2009 à 21:50
Bon c'est déjà un point positif si ça ne bloque pas chez toi ; par contre c'est la commande ipconfig qui ne se termine pas, c'est le wait qui est bloquant ; je passe outre en mettant 5000ms d'attente au lieu d'INFINITE. Sinon, quand je fais Ctrl+C pour stopper le programme, j'ai toujours un ipconfig.exe qui tourne dans le gestionnaire des tâches.
C'est vraiment étrange, quelle est la différence entre l'appel de la commande par CreateProcess et son appel à la main ?
0
cs_Rankin Messages postés 52 Date d'inscription lundi 29 mars 2004 Statut Membre Dernière intervention 27 avril 2010
12 juin 2009 à 14:47
Désolé de remonter le topic mais finalement j'ai encore eu le même problème sur une autre machine et avec Vista (la première est sous XP).
En commentant certaines lignes, j'ai pu voir que si je ne créais pas de tube, la commande était instantanée : j'ai même testé de rediriger la sortie vers un fichier pour m'assurer qu'elle avait fonctionné correctement.
Etant donné que je ne bloque pas sur le ReadFile (ce dont je suis sûr aussi : le processus ne se termine pas même si le Read est commenté à partir du moment où j'ai défini le flux de sortie de la commande vers l'entrée du tube).
Aucune idée sur ce qui pourrait aller de travers ? (le code n'a pas changé par rapport au premier post).

Merci !
0
cs_Rankin Messages postés 52 Date d'inscription lundi 29 mars 2004 Statut Membre Dernière intervention 27 avril 2010
12 juin 2009 à 15:59
Bon, j'ai finalement trouvé l'erreur : je créais le processus et attendais sa fin ou le détruisais au bout de 5 secondes. Mais en redirigeant la sortie standard du processus vers l'entrée de mon tube, je créais dans certains cas une situation d'interblocage.
Pour la plupart des commandes, pas de problème. En revanche, pour un ipconfig /all qui représente pas mal de lignes sur les deux machines où j'ai testé mon programme (et c'est pour ça que chez toi ça a marché sans souci vecchio56), le processus écrit dans le tube jusqu'au moment où il est plein : il attend donc que quelqu'un lise à l'autre bout.
Le problème, c'est que mon programme ne lisait qu'une fois le processus fini : situation inextricable.

J'ai donc changé un peu tout ça en faisant confiance au processus (si il ne se finit pas, ma boucle non plus, mais bon, pour un ipconfig... devrait pas y avoir trop de risques). Je poste ma fonction modifiée, des fois que ça intéresse quelqu'un (je déteste les topics pas finis ou pire, quand le mec dit "j'ai trouvé" sans donner plus d'explications).

string commandLine(string command, bool hide, bool encode)
{
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    SECURITY_ATTRIBUTES sa;
    HANDLE hPipeOutput, hPipeInput;
    DWORD dwRead;
    unsigned short size = 1024;
    char buffer[size+1];
    memset(buffer,0,sizeof(buffer));
    string res = "";
    sa.bInheritHandle = TRUE;
    sa.lpSecurityDescriptor = 0;
    sa.nLength = sizeof(SECURITY_ATTRIBUTES);

    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
    si.dwFlags = STARTF_USESTDHANDLES;
    //si.wShowWindow = SW_HIDE;
    ZeroMemory(&pi, sizeof(pi));
  
   if(!hide && CreatePipe(&hPipeOutput, &hPipeInput, &sa, 0))  
        si.hStdOutput = hPipeInput;
    else
        //Au cas où la création du pipe ait échoué, on ne lira pas dedans
        hide = true;
    if(CreateProcess(0, (char*)command.c_str(), NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
    {
        DWORD dwExitCode = 0;
        if(hide)
        {
            WaitForSingleObject(pi.hProcess, 5000);
            GetExitCodeProcess(pi.hProcess, &dwExitCode);
            //Si processus toujours présent, on le tue
            if(dwExitCode == STILL_ACTIVE)
            {
                TerminateProcess(pi.hProcess, 0);
            }
        }
        else
        {
            int left = GetFileSize (hPipeOutput, NULL);
            GetExitCodeProcess(pi.hProcess, &dwExitCode);
            while(dwExitCode == STILL_ACTIVE || left > 0)
            {
                ReadFile(hPipeOutput, buffer, size, &dwRead, NULL);
                cout<<dwRead<<endl;
                if (encode)
                    CharToOem(buffer,buffer);
                res += buffer;
                memset(buffer,0,sizeof(buffer));
                left = GetFileSize (hPipeOutput, NULL);
                GetExitCodeProcess(pi.hProcess, &dwExitCode);
            }
        }
    }
    if(hPipeInput)
        CloseHandle(hPipeInput);

    if(hPipeOutput)
        CloseHandle(hPipeOutput);
    return res;
}

Je vérifie dans ma boucle du ReadFile à la fois qu'il reste des choses à lire grâce à GetFileSize, mais aussi si le processus est toujours actif : tant qu'il n'est pas terminé il peut toujours avoir quelque chose à dire (et surtout, le premier read intervient avant la fin de l'exécution de l'ipconfig chez moi).

Dernière interrogation : après le ReadFile, il y a un cout. Si je ne le mets pas, le programme ne se finit pas. La boucle n'est pas infinie, il se bloque dans la boucle. Je ne sais pas où, parce que si je fais un affichage ça se termine. Si j'affiche par exemple "toto", ça ne marche pas. Vraisemblablement, il faut que j'affiche le contenu d'une de mes variables. Et si je place le cout ailleurs, soit ça ne marche pas sous XP, soit ça ne marche pas sous Vista. Cet emplacement semble être le seul possible pour que le programme se finisse sur les deux systèmes. J'avoue que là, je comprends pas trop d'où ça vient... (ça marche aussi avec cerr)

Je suis preneur pour toute idée :)
0
Rejoignez-nous