Entwickler-Ecke

Windows API - Ausgaben aus Kommandozeile während der Laufzeit anzeigen


daywalker0086 - Mo 14.04.14 13:57
Titel: Ausgaben aus Kommandozeile während der Laufzeit anzeigen
Hallo Jungs und Mädels,
nach längerer Abstinenz melde ich mich mit einem Problem zurück:
Mein Programm startet auf Knopfdruck ein Kommandozeilenprogramm. Dies startet und führt auch aus was es soll.
Allerdings:
Das Programm macht während es läuft mehrere Ausgaben, diese werden jedoch erst in meinem MemoFeld angezeigt wenn das Programm geschlossen wurde.
Ich muss aber wenn das Programm durchgelaufen ist einmal Space drücken damit es sich schließt. Das ist natürlich doof wenn man die Ausgabe Space zu drücken nicht sieht.
Hier mal der jetzige Code:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
function TForm1.RunCaptured(const _dirName, _exeName, _cmdLine: string): Boolean;
var
   start: TStartupInfo;
   procInfo: TProcessInformation;
   tmpName: string;
   tmp: Windows.THandle;
   tmpSec: TSecurityAttributes;
   res: TStringList;
   return: Cardinal;
begin
   Result := False;
   try
     { Setze ein Temporäres File }
     { Set a temporary file }
     tmpName := 'Test.tmp';
     FillChar(tmpSec, SizeOf(tmpSec), #0);
     tmpSec.nLength := SizeOf(tmpSec);
     tmpSec.bInheritHandle := True;
     tmp := Windows.CreateFile(PChar(tmpName),
            Generic_Write, File_Share_Write,
            @tmpSec, Create_Always, File_Attribute_Normal, 0);
     try
       FillChar(start, SizeOf(start), #0);
       start.cb          := SizeOf(start);
       start.hStdOutput  := tmp;
       start.dwFlags     := StartF_UseStdHandles or StartF_UseShowWindow;
       start.wShowWindow := SW_Minimize;
       { Starte das Programm }
       { Start the program }
       if CreateProcess(nil, PChar(_exeName + ' ' + _cmdLine), nilnil, True,
                        0nil, PChar(_dirName), start, procInfo) then
       begin
         SetPriorityClass(procInfo.hProcess, Idle_Priority_Class);
         WaitForSingleObject(procInfo.hProcess, Infinite);
         GetExitCodeProcess(procInfo.hProcess, return);
         Result := (return = 0);
         CloseHandle(procInfo.hThread);
         CloseHandle(procInfo.hProcess);
         Windows.CloseHandle(tmp);
         { Die Ausgaben hinzufügen }
         { Add the output }
         res := TStringList.Create;
         try
           res.LoadFromFile(tmpName);
           Memo1.Lines.AddStrings(res);
         finally
           res.Free;
         end;
         Windows.DeleteFile(PChar(tmpName));
       end
       else
       begin
         Application.MessageBox(PChar(SysErrorMessage(GetLastError())),
           'RunCaptured Error', MB_OK);
       end;
     except
       Windows.CloseHandle(tmp);
       Windows.DeleteFile(PChar(tmpName));
       raise;
     end;
   finally
   end;
end;



Delphi-Quelltext
1:
2:
3:
4:
procedure TForm1.Button1Click(Sender: TObject);
begin
RunCaptured('C:\stvp\','C:\stvp\STVP_CmdLine.exe','-Device=STM32F401xE -ProgMode=SWD -FileProg=nucleo.hex');
end;


Hier noch die gekürzte Ausgabe des Programms:

Quelltext
1:
2:
3:
4:
5:
>>> Verifying PROGRAM MEMORY
(API) WARNING : ==> Reset done, device Running...
<<< Verifying PROGRAM MEMORY succeeds

Hit 'Space' key to stop or any other key to Loop

Vielleicht ist es nur ein Parameter den man umstellen kann?
Wer kann helfen?

Grüße Christian


jaenicke - Mo 14.04.14 14:20

Statt die Ausgabe in eine Datei umzuleiten :shock: solltest du diese lieber in eine Pipe umleiten. Die kannst du auch direkt auslesen während das Programm noch läuft. Mit GetConsoleOutput solltest du z.B. Ergebnisse finden, das ist eine fertige Funktion dafür, die im Netz herumspukt, auch hier im Forum AFAIK.


daywalker0086 - Mo 14.04.14 15:37

Hi und danke für die Antwort,
ich habe inzwischen die Komponente TDosCommand installiert da ich in dem Threat welchen du auch beantwortet hast gesehen habe das dort die Ausgabe direkt kommen soll:
http://www.entwickler-ecke.de/viewtopic.php?t=67254&highlight=tdoscommand+onnewline

Leider kommt bei mir das auch erst am Ende, bzw. was noch problematischer ist: wenn das Programm einen Fehler hat, dann beendet sich der Prozess von selbst und die komplette Ausgabe wird danach in meinem Memofeld angezeigt. Wenn das Programm aber durchläuft fordert es einen zum schluss auf die Space Taste zu drücken um es zu beenden.
Da ich aber nichts sehe kann ich auch kein Space drücken...
Hier der kurze Code:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
procedure TForm1.Button3Click(Sender: TObject);
var
t:string;
begin
DosCommand1.CommandLine:= 'C:\stvp\STVP_CmdLine.exe -Device=STM32F401xE -ProgMode=SWD -FileProg=nucleo.hex';
DosCommand1.WorkingDirectory:='C:\stvp\';

DosCommand1.OutputLines:= Memo1.Lines;

 DosCommand1.Execute;
 while DosCommand1.Active do
 Application.ProcessMessages;
end;

procedure TForm1.DosCommand1NewLine(Sender: TObject; NewLine: string;
  OutputType: TOutputType);
begin
cmd_receivedtext:= NewLine;
if cmd_receivedtext <> NewLine then
Memo1.Lines.Add(NewLine);
end;


Wie kann ich es besser machen auch in Bezug auf das Drücken der Leertaste, dass das auch automatisiert wird?


jaenicke - Mo 14.04.14 16:31

Ich kann gerade nicht schauen, aber ich glaube die Komponente hatte auch Write oder so etwas um etwas an das Programm zu schicken.


daywalker0086 - Mo 14.04.14 16:44

Ja ich habe DosCommand1.SendLine('test',TRUE); gefunden.
Wenn ich das so aufrufe:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
procedure TForm1.Button3Click(Sender: TObject);
var
t:string;
begin
DosCommand1.CommandLine:= 'cmd.exe ';
DosCommand1.WorkingDirectory:='C:\';

DosCommand1.OutputLines:= Memo1.Lines;
//DosCommand1.OnNewLine:= Memo1.Lines;
//DosCommand1.MaxTimeAfterBeginning:= 20;
 DosCommand1.Execute;
 DosCommand1.SendLine('cd stvp',TRUE);
 DosCommand1.SendLine('STVP_CmdLine.exe',TRUE);
end;


Dann öffnet er auch das Programm und die ersten Zeilen (was ich mit Sendline schicke) werden auch gleich in das Memofeld geschrieben.
Aber dann dauert es eine Weile in der nichts kommt und dann kommt auf einmal die gesamte Ausgabe des Programms (da ich es ohne Paramter aufgerufen habe und es sich dadurch selbst beendet. Ich wüsste eben so nicht wo ich das Schicken eines Leerzeichen einfügen sollte denn so wie es jetzt aussieht wird:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
procedure TForm1.DosCommand1NewLine(Sender: TObject; NewLine: string;
  OutputType: TOutputType);
begin
cmd_receivedtext:= NewLine;
if cmd_receivedtext <> NewLine then
Memo1.Lines.Add(NewLine);
//DosCommand1.SendLine(#32,TRUE);

end;


entgegen der eigentlichen Intention erst aufgerufen wenn der Prozess wieder beendet ist.

Mir fällt da nichtsmehr ein, da die Componente ja auch auf CreateProcess, was ich in anderen Codebeispielen gesehen habe beruht.


daywalker0086 - Di 15.04.14 10:39

gib es denn eine Möglichkeit die Ausgaben einer Eingabeaufforderung die mit Shellexecute aufgerufen wurde in men Programm zu kopieren?

Delphi-Quelltext
1:
failure:=ShellExecute(Handle,'open','c:\stvp\STVP_CmdLine.exe','-Device=STM32F401xE -ProgMode=SWD -FileProg=nucleo.hex','c:\stvp',SW_ShowNormal)  ;                    


Hintergrund ist der das wnn ich das Programm so aufrufe dort alles angezeigt wird und ich wenn es soweit ist, dazu aufgefordert werde die Taste zu drücken. So würde mir das schon ausreichen.
Ich müsste aber am Schluss nochmal die Ausgaben in meinem Programm haben zu Dokumentationszwecken. Geht das irgendwie?

Mit createprocess und mit der Komponente TDosCommand öffnet sich zwar die Eingabeaufforderung aber sie bleibt leer und ich muss auf verdacht die Leertaste drücken. Wenn dann das Programm fertig ist schließt sich das Fenster und ich habe meine Eingaben im MemoFeld. So ist das aber Sch**ße da jemand anders nicht weis das er die Leertaste zu drücken hat.