begin process at 2012 02 11 20:40:14
  Trouver un code source :
 
dans
 
Accueil > Forum > 

C++ & C++ .NET

 > 

Windows

 > 

System

 > 

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


Derniers messages déposésPoser une question dans le forum ou lancer une discussion

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

jeudi 10 janvier 2008 à 15:11:14 | Récupération "au fil de l'eau" du résulat d'une commande DOS

10MilleSabords

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

vendredi 11 janvier 2008 à 12:48:42 | Re : Récupération "au fil de l'eau" du résulat d'une commande DOS

rt15

Membre Club Administrateur CodeS-SourceS
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é.

vendredi 11 janvier 2008 à 14:42:16 | Re : Récupération "au fil de l'eau" du résulat d'une commande DOS

rt15

Membre Club Administrateur CodeS-SourceS
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;
}

vendredi 11 janvier 2008 à 14:44:20 | Re : Récupération "au fil de l'eau" du résulat d'une commande DOS

rt15

Membre Club Administrateur CodeS-SourceS
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.

vendredi 11 janvier 2008 à 15:07:35 | Re : Récupération "au fil de l'eau" du résulat d'une commande DOS

10MilleSabords

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!
vendredi 11 janvier 2008 à 15:38:37 | Re : Récupération "au fil de l'eau" du résulat d'une commande DOS

rt15

Membre Club Administrateur CodeS-SourceS
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;
}

vendredi 11 janvier 2008 à 16:12:45 | Re : Récupération "au fil de l'eau" du résulat d'une commande DOS

10MilleSabords

Effectivement je n'avais pas pensé aux threads, je vais testé ça...
Merci.
vendredi 11 janvier 2008 à 17:14:19 | Re : Récupération "au fil de l'eau" du résulat d'une commande DOS

10MilleSabords

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....

samedi 12 janvier 2008 à 22:31:58 | Re : Récupération "au fil de l'eau" du résulat d'une commande DOS

rt15

Membre Club Administrateur CodeS-SourceS
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;
}

lundi 14 janvier 2008 à 10:20:12 | Re : Récupération "au fil de l'eau" du résulat d'une commande DOS

10MilleSabords

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.


1 2

Cette discussion est classée dans : buffer, handle, pi, closehandle, pipeoutputwrite


Répondre à ce message

Sujets en rapport avec ce message

getoenfilename -> ??? [ par stefbuet ] salut,sur une source (http://www.cppfrance.com/code.aspx?ID=28478)j'ai extrait une fonction de la librairy créé et ais essayé de la faire fonctioner, CreateProcess et processus infini [ par Rankin ] 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 Pipes sous windows [ par ssmint ] Bonjour à tous, Voilà je voudrais piloter CMD.EXE gràce à mon prog pour au final pouvoir réaliser une sorte de telnet...J'ai redirigé l'entrée et la s Redirection sortie [ par M5i9k ] Bonjour à tous,je voudrais lire la sortie d'une console lancée à partir de mon programme (j'utilise CreateProcess), je voudrais savoir si je suis sur Info sur les include [ par jose12 ] Bonjour, contexte : Je ne suis pas développeur mais admin réseau. Je connais un peu le VB et divers langage Web, et j'ai une vision très très succint Double buffer win32 [ par Stepharcher ] Bonjour à tous !J'ai récemment tenté le double buffer sous window avec GDI... Mais je ne comprend pas trop mon erreur. Quand je dessine directement su client irc [ par tagboys ] bon j'ai trouver une source sur ce site ces un socket bot j'aimerait y rajouter quelque commands .la seul chose qu'il a ces le pong reply .j'aimerait Récupération de Handle [ par Gendal67 ] Bonjour à tous,J'ai un petit souci et j'espère que les plus habiles sauront me répondre car je suis un peu embêté là.Voila, j'aimerais récupérer le ha UpdateResource() ICON [ par maladedede ] BonjourJ'ai deja utiliser plusoeur fois Update resource pour changer des resources du type RCDATA d'un ".exe"Et la c'est la resourc ICON du ".exe" que comportement recv de Winsock etrange [ par thejojo1 ] Salut jespere quun spécialiste de winsock va pouvoir maider! Voila je programme un serveur ftp et tout marche... en reseau local! Sur internet c'est u


Nos sponsors


Sondage...

CalendriCode

Février 2012
LMMJVSD
  12345
6789101112
13141516171819
20212223242526
272829    

Consulter la suite du CalendriCode

 
Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel (EBArtSoft), Merci à Vincent pour ses précieux conseils.
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés

Google Coop CodeS-SourceS Google Coop CodeS-SourceS
Temps d'éxécution de la page : 1,264 sec (4)

Nous contacter | Annoncer sur CodeS-SourceS | Mentions légales