Ausgabe einer Konsolenanwendung in GUI
-
Guten Tag,
wer hätte gedacht dass ich auf meine alten Tage noch anfange zu programmieren. Ich würde gerne die Ausgabe einer Konsolenanwendung in Echtzeit in einem CEdit-Feld anzeigen lassen. Habe ein Beispiel mit einer pipe gefunden, allerdings wird die komplette Ausgabe erst dann im CEdit-Feld angezeigt, wenn das Konsolenprogramm bereits beendet ist.
Hat jemand einen funktionierenden Code diesbezüglich? Braucht ja niemand das Rad neu zu erfinden.
MfG
Karl-Heinz
-
Ich verstehe beim besten Willen nicht, weshalb dieser Code nicht den gewünschten Effekt bringt:
FILE *in; char buff[512]; if(!(in = _popen("\"C:\\Users\\admin\\Documents\\Visual Studio 2010\\Projects\\out\\Debug\\out.exe\"", "rt"))) { // fehler } else { m_out.SetWindowTextW(_T("start...")); m_out.RedrawWindow(); while(fgets(buff, 512, in) != NULL) { m_out.SetSel(MAXLONG,MAXLONG); m_out.ReplaceSel(CString(buff)); } } _pclose(in);
Beim Debuggen sieht man schön, dass die Ausgabe der out.exe zeilenweise eingelesen wird und immer nur die aktuelle Zeile im Puffer steht. Wieso wird die im Editfeld nicht direkt dargestellt?
-
Karl-Heinz-Schmidt schrieb:
Beim Debuggen sieht man schön, dass die Ausgabe der out.exe zeilenweise eingelesen wird und immer nur die aktuelle Zeile im Puffer steht. Wieso wird die im Editfeld nicht direkt dargestellt?
Weil dein Programm in der while-Schleife festhängt und dadurch nicht dazu kommt, irgendwelche Steuerelemente neu zu malen.
-
Ok danke, und wie kann ich dem entgegenwirken?
Also ich hab folgende while-Schleife getestet:
int i = 0; char out[5]; while(i < 50) { sprintf(out, "%d", i); m_out.SetSel(MAXLONG,MAXLONG); m_out.ReplaceSel(CString(out) + _T("\r\n")); if (!AfxGetThread()->PumpMessage()) return; i++; Sleep(50); }
durch
if (!AfxGetThread()->PumpMessage()) return;
funktioniert es in dem Fall. Während meiner popen()-Anwendung allerdings nicht. Also liegt es doch nicht explizit an der while-Schleife oder?
-
Würd mich auch ma brennend interessieren, suche sowas schon ewig, aber nirgendwo im Netz was gefunden... irgendeiner hier kann doch mit Sicherheit helfen oder hat das schon genau so gemacht?!
-
Hallo Karl-Heinz
Versuch mal folgendes:
while(fgets(buff, 512, in) != NULL) { m_out.SetSel(MAXLONG,MAXLONG); m_out.ReplaceSel(CString(buff)); m_out.Invalidate (); m_out.UpdateWindow (); }
-
Hi, weicher, funktioniert auch nicht, weder UpdateWindow() noch UpdateData() noch sonst irgendwas. Bin ehrlich gesagt ein wenig ratlos...
-
Hallo Karl-Heinz
das könntest Du noch versuchen... Hat aber seine Tücken
MSG msg; // edit: brauchts natürlich auch noch while(fgets(buff, 512, in) != NULL) { m_out.SetSel(MAXLONG,MAXLONG); m_out.ReplaceSel(CString(buff)); m_out.Invalidate (); m_out.UpdateWindow (); while (::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { if (!AfxGetThread()->PumpMessage()) break; } }
-
Leider auch nicht, erst wenn die Konsolenanwendung beendet ist, ich hab auch während die Konsolenanwendung läuft, keinen Zugriff auf meine GUI
-
Hallo Karl-Heinz
ich hab auch während die Konsolenanwendung läuft
Dass Du eine Konsolenanwendung hast, habe ich überlesen.
Mach doch eine Dialogbasierte Anwendung, dann wird es gehen.
-
ich glaube du verstehst mein problem nicht. ich will ja aus einer Konsolenanwendung bewusst die Ausgabe auslesen
-
dann leg doch das lesen in einen thread und poste von da über eine usermessage die daten in dein mainthread also da wo dein edit läuft, dann sollte meiner meinung nach auch die darstellung laufen, denk dran im read-thread das du ne pause mit ein baust sonst pollt der auf 100% der CPU leistung unmd wird nur durch windows eigenem threadwechsel raus gerissen
-
also zunächst hab ich die popen-Funktion rausgeschmissen und gehe nun über CreateProcess. Hab mir mal angeschaut wie man mit pipes umgeht und mir daraus den gleichen Effekt gebastelt wie mit popen. Die Frage ist nun an welcher Stelle muss ich welche Nachricht wohin schicken, damit die GUI nicht mit "keine Rückmeldung" da steht?
Pipe.h:
#ifndef PIPEH #define PIPEH #include <windows.h> class CPipe { public: enum pipeEnde {READ, WRITE}; CPipe(); ~CPipe(); HANDLE GetPipeHandle(pipeEnde end); void MakeInheritable(pipeEnde end); void ClosePipeHandle(pipeEnde end); private: HANDLE readEnd, writeEnd; void MakeInheritable(HANDLE *h, bool inheritable); //dont copy || assign CPipe(const CPipe &); CPipe& operator=(const CPipe &); }; #endif
Pipe.cpp
#include "StdAfx.h" #include "Pipe.h" //Konstruktor CPipe::CPipe() { CreatePipe(&readEnd, &writeEnd, NULL, 0); } //Destruktor CPipe::~CPipe() { ClosePipeHandle(READ); ClosePipeHandle(WRITE); } //public void CPipe::ClosePipeHandle(pipeEnde end) { switch(end) { case READ: if(readEnd) { CloseHandle(readEnd); readEnd = 0; } break; case WRITE: if(writeEnd) { CloseHandle(writeEnd); writeEnd = 0; } break; } } HANDLE CPipe::GetPipeHandle(pipeEnde end) { switch(end) { case READ: return readEnd; case WRITE: return writeEnd; } return NULL; } void CPipe::MakeInheritable(pipeEnde end) { switch(end) { case READ: MakeInheritable(&readEnd, true); MakeInheritable(&writeEnd, false); break; case WRITE: MakeInheritable(&readEnd, false); MakeInheritable(&writeEnd, true); break; } } //private void CPipe::MakeInheritable(HANDLE *h, bool inheritable) { if(!h) return; HANDLE proc = GetCurrentProcess(); DuplicateHandle(proc, *h, proc, h, 0, inheritable, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); }
Dlg.h
// createprocessguiDlg.h: Headerdatei // #pragma once #include "afxwin.h" #include <vector> #include <string> #include "Pipe.h" #include <cstringt.h> // CcreateprocessguiDlg-Dialogfeld class CcreateprocessguiDlg : public CDialogEx { // Konstruktion public: CcreateprocessguiDlg(CWnd* pParent = NULL); // Standardkonstruktor // Dialogfelddaten enum { IDD = IDD_CREATEPROCESSGUI_DIALOG }; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV-Unterstützung // Implementierung protected: HICON m_hIcon; // Generierte Funktionen für die Meldungstabellen virtual BOOL OnInitDialog(); afx_msg void OnSysCommand(UINT nID, LPARAM lParam); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); DECLARE_MESSAGE_MAP() public: CEdit m_out; afx_msg void OnBnClickedOk(); int StartTSMuxer(); CString csExe; const std::vector<std::string> & getResultVector() const; private: std::vector<std::string> result; void readToVector(HANDLE rHandle); int startTSMuxerProcess(HANDLE wrHandle); };
Dlg.cpp
void CcreateprocessguiDlg::OnBnClickedOk() { result.clear(); CPipe readPipe; readPipe.MakeInheritable(CPipe::WRITE); if(startTSMuxerProcess(readPipe.GetPipeHandle(CPipe::WRITE)) == 0) { // Fehler } readPipe.ClosePipeHandle(CPipe::WRITE); readToVector(readPipe.GetPipeHandle(CPipe::READ)); } int CcreateprocessguiDlg::startTSMuxerProcess(HANDLE wrHandle) { STARTUPINFO si; PROCESS_INFORMATION pi; CString std_cmd; ZeroMemory(&si,sizeof(STARTUPINFO)); ZeroMemory(&pi,sizeof(PROCESS_INFORMATION)); si.cb = sizeof(STARTUPINFO); si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; si.hStdOutput = wrHandle; si.hStdError = wrHandle; si.wShowWindow = SW_HIDE; std_cmd = _T("\"C:\\Users\\admin\\Documents\\Visual Studio 2010\\Projects\\out\\Debug\\out.exe\""); if (!CreateProcess(NULL, (LPWSTR&)std_cmd, NULL, NULL, true, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) { return 0; } // Close any unnecessary handles. CloseHandle(pi.hThread); CloseHandle(pi.hProcess); return 1; } void CcreateprocessguiDlg::readToVector(HANDLE rHandle) { char inBuf[256]; memset(inBuf, 0, sizeof(inBuf)); DWORD nBytesRead; std::string tmp; BOOL ret; MSG msg; while(TRUE) { if (!ReadFile(rHandle, inBuf, sizeof(inBuf), &nBytesRead, NULL) || !nBytesRead) break; for(int i = 0; i < nBytesRead; ++i) { if(inBuf[i] == 10) { result.push_back(tmp); tmp.clear(); theApp.PumpMessage(); m_out.SetWindowTextW((CString)inBuf); } else if(inBuf[i] == 13) ; else tmp.push_back(inBuf[i]); } } }
-
Das mit dem Thread war ne gute Idee. Jetzt hab ich Zugriff auf das Fenster.
Hab einen Thread erstellt, in dem ich die Lese-Schleife von meiner pipe abarbeite. Dazu hätte ich noch eine Frage:
Wie genau funktioniert die Interprozesskommunikation?
Ich starte ja über einen Button eine Konsolenanwendung und Frage deren Ausgabe ab, um sie in der GUI darzustellen. Das vorzeitige Abbrechen der Konsolenanwendung würde ich gerne über einen Button realisieren. Allerdings hängt der Thread ja in der while-Schleife bis das Programm auf normalen Wege beendet ist. Wie genau muss ich das anstellen, dass mein thread mitbekommt dass der "Abbrechen-Button" gedrückt wurde und er aus der Schleife rausspringen soll?
-
Na ja du könntest der Threadklasse eine Variable spenden die du von außen veränderst also sprich ne BOOL-Variable und solange die TRUE ist soll der Thread laufen und wenn die FALSE ist soll er sich beenden.
Des weitern könnte der Thread auch schaun ob die Konsolenanwendung noch läuft und wenn die net mehr läuft sich beenden.
Aber gibt sicher noch andere elegantere Lösungen, aber das nur mal auf die schnelle als Gedankenansatz.