Récupération "au fil de l'eau" du résulat d'une commande DOS

10MilleSabords Messages postés 5 Date d'inscription jeudi 10 janvier 2008 Statut Membre Dernière intervention 14 janvier 2008 - 10 janv. 2008 à 15:11
cs_rt15 Messages postés 3874 Date d'inscription mardi 8 mars 2005 Statut Modérateur Dernière intervention 7 novembre 2014 - 15 mars 2009 à 17:52
Bonjour,

Tout est dans le sujet. Est-ce possible?
Voici ou j'en suis:
Je lance ma ligne de commande par un CreateProcess puis je récupère le résulat dans un buffer par un CreatePipe puis ReadFile.
Cela fonctionne très bien mais le buffer est rempli une fois que l'exécution dans la console est terminée or j'aimerai pouvoir récupérer les informations au fil de l'exécution de la commande.
Quelqu'un a-t-il déjà fait ca?
Merci d'avance pour votre aide.
Voici mon code:

STARTUPINFO si;
 PROCESS_INFORMATION pi;
 unsigned long dwExitCode;





 HANDLE PipeInputRead;
 HANDLE PipeInputWrite;
 HANDLE PipeOutputRead;
 HANDLE PipeOutputWrite;



 SECURITY_ATTRIBUTES securityattribs = {sizeof(SECURITY_ATTRIBUTES),NULL,TRUE};
 
 DWORD dwRead;
 DWORD NumByte;
 char buffer[BUFSIZE]; 



 memset(&si, 0, sizeof(si));
 memset(&pi, 0, sizeof(pi));
 si.cb = sizeof(si);



 // Create pipe for standard output redirection
 CreatePipe(&PipeOutputRead, &PipeOutputWrite, &securityattribs, 0);
 // Create pipe for standard input redirection.
 CreatePipe(&PipeInputRead, &PipeInputWrite, &securityattribs, 0);



 si.dwFlags = STARTF_USESHOWWINDOW+STARTF_USESTDHANDLES;
 si.hStdInput = PipeInputRead;
 si.hStdOutput = PipeOutputWrite;
 si.hStdError = PipeOutputWrite;
 si.wShowWindow = SW_SHOW;



 bool pSuccess = CreateProcess( NULL,"C:\\_prov\\avr prog\\AVROSP.exe -dATmega128 -e" , NULL, NULL, true, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);

 if (pSuccess)
 {
  CloseHandle(pi.hThread); // fermer le handle de thread dès qu'il devient inutile
  WaitForSingleObject(pi.hProcess, INFINITE); // attente jusqu'à la signalisation de la fermeture
  GetExitCodeProcess(pi.hProcess, &dwExitCode); // le processus notepad est terminé
  if (dwExitCode != STILL_ACTIVE)
   CloseHandle(pi.hProcess); // fermer le handle de process
 }
 else
 {
  CloseHandle(PipeOutputWrite);
  CloseHandle(PipeInputRead);
 }
 
 CloseHandle(PipeOutputWrite);
 CloseHandle(PipeInputRead);
 CloseHandle(PipeInputWrite);



 while (ReadFile(PipeOutputRead, buffer, BUFSIZE-1, &dwRead, NULL) && (dwRead != 0))
 {
  buffer[dwRead] = '\0';  
 }
    SetDlgItemText(Handle,IDC_EDIT1,buffer); // Affichage dans la ComboBox
 



 CloseHandle(PipeOutputRead);

13 réponses

cs_rt15 Messages postés 3874 Date d'inscription mardi 8 mars 2005 Statut Modérateur Dernière intervention 7 novembre 2014 13
11 janv. 2008 à 12:48
Salut,


Bah là j'ai pas le temps de coder, mais voilà ce que tu peux essayer :


Tu lis le pipe après que le processus soit terminé tout à fait volontairement avec ton WaitForSingleObject.


D'après l'aide du pipe, cela ne devrait pas poser de problème de faire des Read avant la fin du processus.


Je verrais donc une boucle dans ce genre là :


while (WaitForSingleObject(pi.hProcess, 1) == WAIT_TIMEOUT)

{

  Read...

}

Read...


Comme ça, toute les millisecondes, tu lis la sortie et tu vérifie que le programme ne s'est pas terminé.
0
cs_rt15 Messages postés 3874 Date d'inscription mardi 8 mars 2005 Statut Modérateur Dernière intervention 7 novembre 2014 13
11 janv. 2008 à 14:42
Ca à l'air presque bon, mais ça bloque sur le dernier Read. Le pipe est
encore là (Donc pas d'erreur), la fin n'est pas atteinte... Read attend
des nouvelles entrées...


Voilou une appli qui fait un ping dans une nouvelle fenêtre mais qui fait sa sortie sur la première :
#include "windows.h"

#define BUFSIZE 256

void ReadPipe(HANDLE hPipeRead)
{
CHAR lpBuffer[256];
DWORD nBytesRead;
DWORD nCharsWritten;

while (ReadFile(hPipeRead, lpBuffer, sizeof(lpBuffer), &nBytesRead, NULL) && nBytesRead)
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), lpBuffer, nBytesRead, &nCharsWritten, NULL);
}

int main()
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
BOOL pSuccess;

HANDLE PipeInputRead;
HANDLE PipeInputWrite;
HANDLE PipeOutputRead;
HANDLE PipeOutputWrite;

SECURITY_ATTRIBUTES securityattribs = {sizeof(SECURITY_ATTRIBUTES),NULL,TRUE};

CreatePipe(&PipeOutputRead, &PipeOutputWrite, &securityattribs, 0);
CreatePipe(&PipeInputRead, &PipeInputWrite, &securityattribs, 0);

memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.hStdInput = PipeInputRead;
si.hStdOutput = PipeOutputWrite;
si.hStdError = PipeOutputWrite;
si.wShowWindow = SW_SHOW;

pSuccess = CreateProcess(NULL, "ping 127.0.0.1" , NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);

if (pSuccess)
{
while (WaitForSingleObject(pi.hProcess, 1) == WAIT_TIMEOUT)
{
ReadPipe(PipeOutputRead);
}
ReadPipe(PipeOutputRead);
}

CloseHandle(PipeInputRead);
CloseHandle(PipeInputWrite);
CloseHandle(PipeOutputRead);
CloseHandle(PipeOutputWrite);

return 0;
}
0
cs_rt15 Messages postés 3874 Date d'inscription mardi 8 mars 2005 Statut Modérateur Dernière intervention 7 novembre 2014 13
11 janv. 2008 à 14:44
J'oubliais : c'est une application console bien sûr.


Pour passer le dernier Read, il faudrait peut être faire un thread qui
attend la fin du processus avec WaitForSingleObject (Avec INFINITE), et
qui ferme le handle du pipe.
0
10MilleSabords Messages postés 5 Date d'inscription jeudi 10 janvier 2008 Statut Membre Dernière intervention 14 janvier 2008
11 janv. 2008 à 15:07
Merci rt15 pour ta réponse!

J'avais effectivement pensé à la lecture dans une boucle d'attente mais je n'avais pas approfondit.
J'ai testé et ça s'exécute bien mais je pense en fait que j'ai 2 problèmes à régler car le but final est d'afficher ces résultats dans une EditBox (Appli windows)
1) Récupérer les résultats au fil de l'eau: je pense que ta solution résoud ce problème
2) Afficher au fil de l'eau dans l'EditBox: Et là ça se complique car la fenêtre se raffraichit une fois l'exécution de la console terminée
J'utilise  SetDlgItemText dans la boucle pour raffraichir.

Merci encore, ça m'a décoincer pas mal!
0

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

Posez votre question
cs_rt15 Messages postés 3874 Date d'inscription mardi 8 mars 2005 Statut Modérateur Dernière intervention 7 novembre 2014 13
11 janv. 2008 à 15:38
Tu peux passer par des threads.

J'en ai fait un pour corriger le bug cité plus haut.


Il faudrat que tu en fasse un deuxième qui attende la fin du processus pour tuer le premier thread.


#include "windows.h"

#include "stdlib.h"


#define BUFSIZE 256


typedef struct _ReadPipeThreadParam

{

 HANDLE hPipe;

} ReadPipeThreadParam;


DWORD __stdcall ReadPipe(ReadPipeThreadParam* param)

{

 CHAR lpBuffer[256];

 DWORD nBytesRead;

 DWORD nCharsWritten;


 while (ReadFile(param->hPipe, lpBuffer, sizeof(lpBuffer), &nBytesRead, NULL) && nBytesRead)

  WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), lpBuffer, nBytesRead, &nCharsWritten, NULL);

 return 0;

}


int main()

{

 STARTUPINFO si;

 PROCESS_INFORMATION pi;

 BOOL pSuccess;

 ReadPipeThreadParam param;

 DWORD nThreadId;

 HANDLE hThread;


 HANDLE PipeInputRead;

 HANDLE PipeInputWrite;

 HANDLE PipeOutputRead;

 HANDLE PipeOutputWrite;


 SECURITY_ATTRIBUTES securityattribs = {sizeof(SECURITY_ATTRIBUTES),NULL,TRUE};


 CreatePipe(&PipeOutputRead, &PipeOutputWrite, &securityattribs, 0);

 CreatePipe(&PipeInputRead, &PipeInputWrite, &securityattribs, 0);


 memset(&si, 0, sizeof(si));

 si.cb = sizeof(si);

 si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;

 si.hStdInput = PipeInputRead;

 si.hStdOutput = PipeOutputWrite;

 si.hStdError = PipeOutputWrite;

 si.wShowWindow = SW_SHOW;


 pSuccess = CreateProcess(NULL, "ping 127.0.0.1" , NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);


 if (pSuccess)

 {

  param.hPipe = PipeOutputRead;

  hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&ReadPipe, &param, 0, &nThreadId);

 

  WaitForSingleObject(pi.hProcess, INFINITE);

  TerminateThread(hThread, 0);

 }

 

 CloseHandle(hThread);

 CloseHandle(PipeInputRead);

 CloseHandle(PipeInputWrite);

 CloseHandle(PipeOutputWrite);

 CloseHandle(PipeOutputRead);


 system("pause");


 return 0;

}
0
10MilleSabords Messages postés 5 Date d'inscription jeudi 10 janvier 2008 Statut Membre Dernière intervention 14 janvier 2008
11 janv. 2008 à 16:12
Effectivement je n'avais pas pensé aux threads, je vais testé ça...
Merci.
0
10MilleSabords Messages postés 5 Date d'inscription jeudi 10 janvier 2008 Statut Membre Dernière intervention 14 janvier 2008
11 janv. 2008 à 17:14
J'ai pu testé les threads grâce à ton code et à l'exécution le résultat est le même: l'affichage parvient une fois l'exécution de la console terminée.
En fait, j'ai pu vérifier que la thread récupère bien les résultats en parallèle de l'exécution de la console mais l'affichage n'arrive qu'à la fin.
D'autre part, j'ai compté le nombre de fois que je vais dans la boucle du ReadFile (= nbre de fois qu'il vient récupérer des caractères dans la console): 2 fois! alors qu'il faudrait plusieurs 10n de fois pour être réellement au fil de l'eau...
Ca veut donc dire que le temps d'exécution du ReadFile est bien trop longue pour récupérer véritablement au fil de l'eau (en même temps, je suis sous windows et pas un OS temps réel!)
Je crains que je vais me contenter de l'affichage en fin d'exécution....
0
cs_rt15 Messages postés 3874 Date d'inscription mardi 8 mars 2005 Statut Modérateur Dernière intervention 7 novembre 2014 13
12 janv. 2008 à 22:31
Bah pour un ping, on les voit bien arriver les uns après les autres.
Voilà une appli avec un champ de texte où on peut entrer des commandes, et un champ de texte où les résultats sont affichés.

#include "windows.h"

#define BUFFER_SIZE 65536

HINSTANCE _hThisInstance; // Handle du module
HWND _hWnd; // Handle de la fenêtre
HWND _hInput; // Handle de l'EDIT où l'utilisateur tape les commandes
HWND _hOutput; // Handle de l'EDIT où la sortie de la console est affichée
LPSTR _lpAppName = "ConsoleInWindow"; // Nom de l'appli
HANDLE _hConsole; // Handle de l'appli lancée
CHAR lpPipeContent[BUFFER_SIZE]; // Contenu du pipe

HANDLE hReadPipe; // Thread de lecture du pipe
HANDLE hClosePipe; // Thread qui détruit le thread de lecture

// Les Pipe de lecture et d'écriture fournis à la console
HANDLE hPipeInputRead;
HANDLE hPipeInputWrite;
HANDLE hPipeOutputRead;
HANDLE hPipeOutputWrite;

//
// Affiche le message d'erreur associé à la dernière
// erreur Win32 et ferme l'appli.
//
void ShowLastError()
{
DWORD nLastError;
LPSTR lpMessageBuffer;

// Récupération du numéro de l'erreur
nLastError = GetLastError();

// Formatage du message
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, nLastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMessageBuffer, 0, NULL);

// Affichage du message et fin de l'appli
MessageBox(NULL, lpMessageBuffer, "ERROR", MB_OK | MB_ICONERROR);
ExitProcess(nLastError);
}

//
// Détruit le thread de lecture dès que la console est fermée.
//
DWORD __stdcall ClosePipe(LPVOID null)
{
WaitForSingleObject(_hConsole, INFINITE);
TerminateThread(hReadPipe, 0);

CloseHandle(hPipeInputRead);
CloseHandle(hPipeInputWrite);
CloseHandle(hPipeOutputWrite);
CloseHandle(hPipeOutputRead);

CloseHandle(hReadPipe);
CloseHandle(hClosePipe);
return 0;
}

//
// Lit le contenu du pipe et le copie dans la fenêtre.
//
DWORD __stdcall ReadPipe(HANDLE hPipe)
{
CHAR* lpCurrentPos; // Position courante de l'écriture dans le buffer
DWORD nBytesRead; // Nombre d'octets lus dans le pipe

// On va concaténer
lpCurrentPos = lpPipeContent;

// Lecture du pipe
while (ReadFile(hPipe, lpCurrentPos, BUFFER_SIZE - 1, &nBytesRead, NULL) && nBytesRead)
{
// Mise en place du zéro terminal
lpCurrentPos[nBytesRead - 1] = '\0';
SetWindowText(_hOutput, lpPipeContent);

// On va concaténer la suite au niveau du zéro terminal
lpCurrentPos = lpCurrentPos + nBytesRead - 1;
}

return 0;
}

//
// Lance la commande demandée.
//
void Execute(CHAR* lpCommandLine)
{
STARTUPINFO si; // Info de lancement du processus
PROCESS_INFORMATION pi; // Récupération d'handles sur le processus lancé
DWORD nThreadId; // Récupération du thread ID lors des créations de thread

SECURITY_ATTRIBUTES securityattribs = {sizeof(SECURITY_ATTRIBUTES),NULL,TRUE};

if (! CreatePipe(&hPipeOutputRead, &hPipeOutputWrite, &securityattribs, 0)) ShowLastError();
if (! CreatePipe(&hPipeInputRead, &hPipeInputWrite, &securityattribs, 0)) ShowLastError();

ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.hStdInput = hPipeInputRead;
si.hStdOutput = hPipeOutputWrite;
si.hStdError = hPipeOutputWrite;
si.wShowWindow = SW_SHOW;

if (CreateProcess(NULL, lpCommandLine, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi))
{
// Lancement du thread de lecture du pipe
hReadPipe = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&ReadPipe, hPipeOutputRead, 0, &nThreadId);

// Lancement du thread qui va tuer le thread de lecture
_hConsole = pi.hProcess;
hClosePipe = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&ClosePipe, NULL, 0, &nThreadId);
}
}

//
// Traitement des messages.
//
LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT nMessage, WPARAM wParam, LPARAM lParam)
{
RECT rect; // Récupération de la taille de la cliente pour redimenssionner les contrôles

switch (nMessage)
{
case WM_MOVE:
case WM_ACTIVATE:
case WM_SIZING:
GetClientRect(_hWnd, &rect);
SetWindowPos(_hInput, 0, 0, rect.bottom - 20, rect.right, 20, SWP_NOZORDER);
SetWindowPos(_hOutput, 0, 0, 0, rect.right, rect.bottom - 20, SWP_NOZORDER);
return DefWindowProc(hWnd, nMessage, wParam, lParam);
case WM_DESTROY:
// On signale que le thread va s'arrêter
PostQuitMessage(0);
break;
default:
// Application du traitement par défaut
return DefWindowProc(hWnd, nMessage, wParam, lParam);
}
return 0;
}

//
// Initialise la fenêtre principale de l'appli.
//
void InitWindow()
{
WNDCLASSEX wincl; // Classe de la fenêtre utilisée

// Création de la classe de fenêtre
wincl.cbSize = sizeof(WNDCLASSEX);
wincl.style = CS_OWNDC;
wincl.lpfnWndProc = WindowProcedure;
wincl.cbClsExtra = 0;
wincl.cbWndExtra = 0;
wincl.hInstance = _hThisInstance;
wincl.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wincl.hCursor = LoadCursor(NULL, IDC_ARROW);
wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
wincl.lpszMenuName = 0;
wincl.lpszClassName = _lpAppName;
wincl.hIconSm = NULL;

// Enregistrement de la classe
if (!RegisterClassEx(&wincl)) ShowLastError();

// Création de la fenêtre
_hWnd = CreateWindowEx(0, _lpAppName, _lpAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 600, 400, HWND_DESKTOP, NULL, _hThisInstance, NULL);
_hInput = CreateWindowEx(0, "EDIT", "ping 127.0.0.1", WS_VISIBLE | WS_CHILD | WS_BORDER, 0, 0, 0, 0, _hWnd, NULL, _hThisInstance, NULL);
_hOutput = CreateWindowEx(0, "EDIT", "", WS_VISIBLE | WS_CHILD | WS_BORDER | WS_VSCROLL | ES_MULTILINE | ES_READONLY | ES_OEMCONVERT, 0, 0, 0, 0, _hWnd, NULL, _hThisInstance, NULL);

ShowWindow(_hWnd, SW_SHOW);
}

#pragma comment(linker, "/entry:main")
INT32 _cdecl main()
{
MSG message; // Réception des messages envoyés à l'application
CHAR lpCommandLine[1024]; // Rédaction de la commande à executé
CHAR lpInputContent[1024]; // Récupération de la commande tapée par l'utilisateur

// Récupération du handle du module
_hThisInstance = GetModuleHandle(NULL);

InitWindow();

// Boucle de traitement des messages
while (GetMessage(&message, NULL, 0, 0))
{
// On execute la commande lors d'un appui sur entrée if (((message.hwnd _hInput) || (message.hwnd _hOutput)) && (message.wParam == VK_RETURN) && (message.message == WM_KEYUP))
{
SendMessage(_hInput, WM_GETTEXT, 1024, (LPARAM)lpInputContent);
wsprintf(lpCommandLine, "cmd /C "%s"", lpInputContent);
Execute(lpCommandLine);
}

// Traduit certains messages
TranslateMessage(&message);

// Distribution des messages aux fenêtres
DispatchMessage(&message);
}

// Code d'erreur en sortie
return message.wParam;
}
0
10MilleSabords Messages postés 5 Date d'inscription jeudi 10 janvier 2008 Statut Membre Dernière intervention 14 janvier 2008
14 janv. 2008 à 10:20
Ouah! Encore merci de consacrer de ton temps à mon problème!

Effectivement ton appli fonctionne très bien, le ping remonte bien au fur et à mesure.
J'ai testé ma ligne de commande avec mon programme DOS et là, bizarrement le résultats ne remontent qu'une fois le programme terminé.
En fait, ce programme DOS programme un microcontroleur (je suis électronicien à la base) et renvoie un état d'avancement au fur et mesure.
En bref, je n'observe pas le même comportement entre le ping et mon programme dos.
Peut-être un problème de priorité car le programme dos prend peut-être plus de ressources pendant la programmation et ne permet pas à l'affichage de s'exécuter.
0
cs_Shai Messages postés 50 Date d'inscription mardi 8 octobre 2002 Statut Membre Dernière intervention 21 décembre 2011
27 mai 2008 à 16:21
Etrange, pour récupérer le contenu dans la fenêtre DOS en "temps-réel", il n'y a pas besoin de thread !
c'est juste qu'il faut appeler ReadFile sans dépasser la taille du texte déjà écrit de la console grace à GetFileSize ...

Voir ma dernière version de fonction CallCmd:
http://www.developpez.net/forums/showthread.php?p=3311871#post3311871

<hr />
Shai Le Troll
!! Aide via F1 !! Pensez-y !! 
Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le conf
0
cs_rt15 Messages postés 3874 Date d'inscription mardi 8 mars 2005 Statut Modérateur Dernière intervention 7 novembre 2014 13
23 déc. 2008 à 15:11
Manque un LocalFree dans ma ShowLastError.
0
ZMJUVENTINO Messages postés 41 Date d'inscription vendredi 31 mars 2006 Statut Membre Dernière intervention 11 mai 2009
15 mars 2009 à 14:16
Votre code est parfait, mais moi je cherche comment faire le même traitement avec un seul processus cmd, sur le même instance on exécute les différentes commandes non pas lancer un processus à chaque nouvelle commande .
Avez vous une solution pour ça ?

foza juve
0
cs_rt15 Messages postés 3874 Date d'inscription mardi 8 mars 2005 Statut Modérateur Dernière intervention 7 novembre 2014 13
15 mars 2009 à 17:52
Tu es sûr de ne vraiment pas parvenir à mélanger ce source et l'autre tout seul ?

J'ai vraiment l'impression qu'il n'y a plus la moindre difficulté technique.
0
Rejoignez-nous