CreateProcess et processus infini

Signaler
Messages postés
52
Date d'inscription
lundi 29 mars 2004
Statut
Membre
Dernière intervention
27 avril 2010
-
Messages postés
52
Date d'inscription
lundi 29 mars 2004
Statut
Membre
Dernière intervention
27 avril 2010
-
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

Messages postés
6535
Date d'inscription
lundi 16 décembre 2002
Statut
Modérateur
Dernière intervention
22 août 2010
8
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.

_____________________________________
Messages postés
52
Date d'inscription
lundi 29 mars 2004
Statut
Membre
Dernière intervention
27 avril 2010

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é.
Messages postés
6535
Date d'inscription
lundi 16 décembre 2002
Statut
Modérateur
Dernière intervention
22 août 2010
8
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?

_____________________________________
Messages postés
52
Date d'inscription
lundi 29 mars 2004
Statut
Membre
Dernière intervention
27 avril 2010

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...
Messages postés
6535
Date d'inscription
lundi 16 décembre 2002
Statut
Modérateur
Dernière intervention
22 août 2010
8
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.

_____________________________________
Messages postés
52
Date d'inscription
lundi 29 mars 2004
Statut
Membre
Dernière intervention
27 avril 2010

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 ?
Messages postés
52
Date d'inscription
lundi 29 mars 2004
Statut
Membre
Dernière intervention
27 avril 2010

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 !
Messages postés
52
Date d'inscription
lundi 29 mars 2004
Statut
Membre
Dernière intervention
27 avril 2010

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 :)