Récupération "au fil de l'eau" du résulat d'une commande DOS
10MilleSabords
Messages postés5Date d'inscriptionjeudi 10 janvier 2008StatutMembreDernière intervention14 janvier 2008
-
10 janv. 2008 à 15:11
cs_rt15
Messages postés3874Date d'inscriptionmardi 8 mars 2005StatutModérateurDerniè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;
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);
}
cs_rt15
Messages postés3874Date d'inscriptionmardi 8 mars 2005StatutModérateurDernière intervention 7 novembre 201413 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"
cs_rt15
Messages postés3874Date d'inscriptionmardi 8 mars 2005StatutModérateurDernière intervention 7 novembre 201413 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.
10MilleSabords
Messages postés5Date d'inscriptionjeudi 10 janvier 2008StatutMembreDernière intervention14 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!
Vous n’avez pas trouvé la réponse que vous recherchez ?
10MilleSabords
Messages postés5Date d'inscriptionjeudi 10 janvier 2008StatutMembreDernière intervention14 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....
cs_rt15
Messages postés3874Date d'inscriptionmardi 8 mars 2005StatutModérateurDernière intervention 7 novembre 201413 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();
// 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);
//
// 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
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();
#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;
}
10MilleSabords
Messages postés5Date d'inscriptionjeudi 10 janvier 2008StatutMembreDernière intervention14 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.
cs_Shai
Messages postés50Date d'inscriptionmardi 8 octobre 2002StatutMembreDernière intervention21 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 ...
ZMJUVENTINO
Messages postés41Date d'inscriptionvendredi 31 mars 2006StatutMembreDernière intervention11 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 ?