Ausgabe aus externen Programm in HA umleiten, aber zeile für zeile



  • Das ist mein Code, (ok, ich geb' ja zu, ich bekenne mich schuldig, ich hab ihn geklaut 🙂 ):

    CString ExecuteExternalFile(CString csExeName, CString csArguments)
    {
      CString csExecute;
      csExecute=csExeName + " " + csArguments;
    
      SECURITY_ATTRIBUTES secattr; 
      ZeroMemory(&secattr,sizeof(secattr));
      secattr.nLength = sizeof(secattr);
      secattr.bInheritHandle = TRUE;
    
      HANDLE rPipe, wPipe;
    
      //Create pipes to write and read data
      CreatePipe(&rPipe,&wPipe,&secattr,0);
      //
      STARTUPINFO sInfo; 
      ZeroMemory(&sInfo,sizeof(sInfo));
      PROCESS_INFORMATION pInfo; 
      ZeroMemory(&pInfo,sizeof(pInfo));
      sInfo.cb=sizeof(sInfo);
      sInfo.dwFlags=STARTF_USESTDHANDLES;
      sInfo.hStdInput=NULL; 
      sInfo.hStdOutput=wPipe; 
      sInfo.hStdError=wPipe;
      char command[1024]; strcpy(command,  
              csExecute.GetBuffer(csExecute.GetLength()));
    
      //Create the process here.
      CreateProcess(0 command,0,0,TRUE,
              NORMAL_PRIORITY_CLASS|CREATE_NO_WINDOW,0,0,&sInfo,&pInfo);
      CloseHandle(wPipe);
    
      //now read the output pipe here.
      char buf[100];
      DWORD reDword; 
      CString m_csOutput,csTemp;
      BOOL res;
      do 
      { 
                      res=::ReadFile(rPipe,buf,100,&reDword,0); 
                      csTemp=buf; 
                      m_csOutput+=csTemp.Left(reDword); 
                      while( ::PeekMessage( &msg, NULL, NULL, NULL, PM_NOREMOVE ) ) 
                          ::AfxGetThread()->PumpMessage(); 
      }while(res);
      return m_csOutput;
    }
    

    Das Problem bei diesem Code ist einfach, dass die Pipe am Ende der Verarbeitung ausgelesen wird. Und ich kann mir das nur so vorstellen, dass nachdem der Prozess erzeugt worden, kehrt die Verarbeitung erst dann zurück, wenn das externe Programm abgeschlossen ist.

    Gruss,
    chullain


  • Mod

    Das kann ich nicht sehen.
    Nach dem CreateProcess laufen beide Prozesse.
    Allerdings liest Du ja so lange wie eben auch die Pipe existiert. Und erst wenn das lesen aus der Pipe einen Fehler liefert beendest Du.

    Die Daten in Deiner Schleife müssten aber Stück für Stück einlaufen.



  • Hm...auf jeden Fall komme ich so nicht weiter, da das externe Pgm ziemlich lange läuft und meine Hauptanwendung dadurch blockiert wird.

    Jetzt habe ich versucht einen weiteren Arbeits-Thread aufzubauen, jedoch funktioniert das nicht mit der Haupt- und der Arbeits-Thread Kommunikation. Ich versuche mit meinem Arbeits-Thread eine Nachricht rauszuschicken, die dann vom Hauptthread aufgefangen werden sollte und verarbeitet wird, aber das funktioniert irgendwie nicht.

    Hier noch einbisschen Code:

    Aufruf der Methode zur Thread-Erstellung

    void C6RepositoryOperation_PEV::ExecuteExternalFile2(CString csExeName, CString csArguments) 
    { 
    
      csExecute=csExeName + " " + csArguments; 
    
      CWinThread* pThread = AfxBeginThread(ThreadExecuteExternalFile, GetSafeHwnd(),
    	  THREAD_PRIORITY_NORMAL);
    
    }
    

    Der Arbeits-Thread:

    UINT C6RepositoryOperation_PEV::ThreadExecuteExternalFile(LPVOID pParam) {
    
      SECURITY_ATTRIBUTES secattr; 
    ...
    
      do 
      { 
                      res=::ReadFile(rPipe,buf,100,&reDword,0); 
                      csTemp=buf; 
                      m_Htext +=csTemp.Left(reDword); 
    				  ::PostMessage((HWND) pParam, EN_UPDATE,0,0);	
      }while(res); 
    
    	return 0;
    }
    

    Und zu guter Letzt die Verarbeitung der EN_UPDATE Nachricht, aber ich bekomme hier nie den Aufruf der MessageBox, d.h. er geht auch nie da rein:

    void C6RepositoryOperation_PEV::OnUpdateEdit1() 
    {
       AfxMessageBox("test");
       m_text += m_Htext;
       UpdateData(FALSE);
    }
    

    Ist meine Nachrichtenverarbeitung vielleicht falsch? Muss eine bestimmte Nachricht geschickt werden, oder kann ich eine x-beliebige Nachricht schicken?

    Gruss,
    chullain



  • Bzw. wo werden die Nachrichten deklariert, ich hab jetzt mal versucht, eine WM_TIMER Nachricht zu verschicken, und es hat auch funktioniert, jedoch wird die WM_TIMER Nachricht viel zu oft aufgerufen, d.h. die Funktion OnTimer wird viel zu oft aufgerufen, als notwenidg...


  • Mod

    Ich verstehe Dein rumgebastel nicht.

    Wenn Du einen Thread hast und die Schleife so läuft wie Du sie eben eingebaut hast. Dann wird diese eben den Hauptthread blockieren.

    Wenn Du nicht willst das der Haupthread blockiert, dann musst Du eben einen zweiten Thread starten und dem das Handle der Pipe übergeben. Diese auslesen und wenn fertig eine Nachricht an den Haupthread senden.
    Wenn Du wieder auf den Thread wartest mit WaitForSingleObject dann blockiert Dein Hauptthread wieder.

    In der Zwischenzeit musst Du dann dafür sorgen, dass der Dialog nicht terminiert wird, kein zweites mal der Prozess gestartet wird etc...

    Was willst Du eigentlich machen während das andere Programm läuft. Was soll den in der Zwischenzeit passieren?



  • Also pass auf, ich möchte ganz einfach die Pipe des zweiten Threades abfangen, und im ersten Thread ausgeben. D.h. während der Ausführung der Konsolen-Anwendung möchte ich die Ausgabe ind derKonsole auf meinen ersten Thread umleiten und im Dialog ausgeben. Das habe ich auch mittlerweile geschafft, ich hab ganz einfach einen zweiten Thread erstellt und kommuniziere über Windows-Nachrichten, jedoch passiert folgendes. Während der zweite Thread, läuft auch ind er Zwischenzeit der erste Thread, und überholt den zweiten, dass habe ich versucht dutrch den Aufruf der Methoden MsgWaitForMultipleObjects und WaitForSingleObject abzufangen, jedoch klappt das nicht so gut, so sieht meine Methode aus, die ich nach dem ich den zweiten Thread erstellt habe, aufrufe:

    BOOL C6RepositoryOperation_PEV::waitWithMessageLoop(HANDLE hEvent, DWORD dwTimeout)
    {
      DWORD dwRet;
      MSG msg;
      hEvent = hEvent ? hEvent : CreateEvent(NULL, FALSE, FALSE, NULL); 
    
      while(true)
      {
        dwRet = MsgWaitForMultipleObjects(1, &hEvent, FALSE, dwTimeout, QS_ALLINPUT);
        if (dwRet == WAIT_OBJECT_0)
           return TRUE;
        if (dwRet != WAIT_OBJECT_0 + 1)
           break;
        while(PeekMessage(&msg,NULL,NULL,NULL,PM_REMOVE))
        {
          TranslateMessage(&msg);
          DispatchMessage(&msg);
          if (WaitForSingleObject(hEvent, 0) == WAIT_OBJECT_0)
             return TRUE;
        }
      }
      return FALSE;
    }
    

    Jeodch kriege ich auch hier das Problem, dass der erste Thread auf den zweiten nicht warten will, und seine Verarbeitung fortfährt und so den zweiten "überholt". So sieht die Methode der Threaderstellung von dem zweiten aus:

    void C6RepositoryOperation_PEV::ExecuteExternalFile2(CString csExeName, CString csArguments) 
    { 
      csExecute=csExeName + " " + csArguments; 
    
      CWinThread* pThread = AfxBeginThread(ThreadExecuteExternalFile, GetSafeHwnd(),
    	  THREAD_PRIORITY_NORMAL);
    
         /*if (WaitForSingleObject(pThread->m_hThread, INFINITE) == WAIT_OBJECT_0)
           CloseHandle(pThread);*/
       waitWithMessageLoop(pThread); 
    }
    

    Kann mir da jemand weiterhelfen?

    Gruss,
    chullain



  • Hát keiner für mich einen Ratschlag?

    Gruss,
    chullain



  • Kann mir jemand sagen, wo ich die Methode waitWithMessageLoop positionieren soll. Gehört diese in den zweiten Thread hinein, oder wie ich in dem Code weiter oben aufgelistet habe, nach der Prozesserstellung?

    Gruss,
    chullain


  • Mod

    Es gehört in den Thread in dem Du Nachrichten empfangen möchtest.
    In dem Workerthread hat das nichts zu suchen.

    Ich verstehe Deine Aussage: "Überholt den x-Thread" nicht.
    Du misachtest einige Richtlinien was MT betrifft:
    1. In einer MFC Applikation sollte man nie TranslateMessage/DispatchMessage verwenden. Immer nur AfxPumpMessage.
    2. Deine Applikation wird reentrant wen Dunicht aufpasst. Durch die Nachrichtenschleife kann der Befhel zum auslösen dieses Vorganges erneut ausgelöst werden! Ich hoffedas ist gewährleistet!
    3. AfxBeginThread erzeugt ein Object CWinThread. Dieses wird bei Thread-Ende natürlich zerstört. Das Handle auf das Du wartest wird dadurch ungültig.



  • Jetzt habe ich das so gelöst, indem ich nur dann das waitWithMessageLoop ausführe, wenn pThread gültig...

    void C6RepositoryOperation_PEV::ExecuteExternalFile2(CString csExeName, CString csArguments) 
    { 
    
      csExecute=csExeName + " " + csArguments; 
    
      CWinThread* pThread = AfxBeginThread(ThreadExecuteExternalFile, GetSafeHwnd(),
    	  THREAD_PRIORITY_NORMAL);
    
      if (pThread) {
    	waitWithMessageLoop(pThread->m_hThread);
      }
    }
    

    Jetzt überholt zumindest nicht der Arbeitsthread den Hauptthread, zuvor war es so gewesen,dass die Verarbeitung vom Haupt-Thread weitergelaufen ist, obwohl der Arbeits-Thread noch nicht fertig war...

    Jetzt habe ich aber das Problem, dass zwar die Kommunikation zwischen den Threads stattfindet, ich aber meinen Dialog nicht sehe und erst nach der Verarbeitung dieser auftaucht...

    Ich erklär das mal ganz kurz:

    Ich hab drei AbarbeitungsPunkte:

    Beginnarbeiten
    Verarbeitung
    Abschlussarbeiten

    wobei Verarbeitung der Arbeitsthread ist, jetzt hatte ich den Ablauf davor so:
    Beginn-Arbeiten
    Abschluss-Arbeiten
    Verarbeitung

    ...das Problem hat sich inzwischen gelöst, jedoch sehe ich nicht, wie gesagt, meinen Dialog, dieser taucht nämlich erst am Ende der Verarbeitung auf mit der richtigen Reihenfolge der Verarbeitung...

    Konnte ich das Problem einbisschen aufzeigen?

    Gruss,
    chullain


  • Mod

    Und wie hast Du das gelöst? Ich hoffe über ein Event oder über m_bAutoDelete = FALSE;?

    Wie soll man Dir helfen, wenn Du nur immer so ein paar Brocken hinschmeist?
    Ich habe noch kein Stück Code gesehen, wann Du eigentlich den Dialog erzeugst?
    Machst Du das alles in OnInitDialog? Dann wundert mich nichts, denn ein Dialog wird erst nach Ablauf der Funktion sichtbar, außer Du definierst in mit WS_VISIBLE!

    Und ich habe immer noch nicht verstanden, was überholen heißt?
    Du synchronisierst einfach nicht richtig!



  • ok, wo fange ich an, die komplette Klasse sprich CDialog, wird ganz normal erzeugt und mit der Methode DoModal() ganz normal aufgerufen. Ja, in der OnInitDialog()-Methode rufe ich meine Haupt-Methode auf, wo die Verarbeitung stattfindet und das ist auch gleichzeitig mein Hauptthread. Die Haupt-Methode ruft drei weitere Methoden auf, einmal

    Vorbereitungs - Methode
    eig. Verarbeitungs-Methode
    Abschluss-Methode

    In der Verarbeitungs-Methode erstelle ich auch mein Arbeitsthread, denn ich dann wie zuvor beschrieben, abarbeite.

    Nur was meinst Du mit WS_VISIBLE definieren? Wo kann ich das machen?
    Die Kommunikation zwischen den Thread findet über Nachrichtenbehandlungsroutinen statt.

    Gruss,
    chullain



  • Martin Richter schrieb:

    1. In einer MFC Applikation sollte man nie TranslateMessage/DispatchMessage verwenden. Immer nur AfxPumpMessage.

    Warum eigentlich? Ich hab mir nen Wolf gegooglet, aber nichts Erleuchtendes gefunden.

    /e: Tja, man sollte doch öfter auf mpdvc.de vorbeischauen, da steht es


  • Mod

    Wenn direkt TranslateMessage DispatchMessage verwendet wird werden alle PreTranslateMessage Handler umgangen. Weiterhin finden einige interne Cleanupsnicht statt und und und und...


Anmelden zum Antworten