Difference between revisions of "Executing External Programs/de"

From Lazarus wiki
Jump to navigationJump to search
m
m
Line 22: Line 22:
  
 
=== Ein einfaches Beispiel ===
 
=== Ein einfaches Beispiel ===
 +
<delphi>
 
  // Dies ist ein Demoprogramm das zeigt wie man ein
 
  // Dies ist ein Demoprogramm das zeigt wie man ein
 
  // externes Programm startet.
 
  // externes Programm startet.
Line 60: Line 61:
 
   AProcess.Free;   
 
   AProcess.Free;   
 
  end.
 
  end.
 +
</delphi>
  
 
Das war's. Sie haben gerade gelernt, wie man in Ihrem Programm ein externes Programm aufruft.
 
Das war's. Sie haben gerade gelernt, wie man in Ihrem Programm ein externes Programm aufruft.
Line 68: Line 70:
 
Lassen sie uns unser Beispiel ein wenig erweitern und einfach das tun:
 
Lassen sie uns unser Beispiel ein wenig erweitern und einfach das tun:
  
 +
<delphi>
 
  // Dies ist ein Demoprogramm, daß zeigt wie man ein externes
 
  // Dies ist ein Demoprogramm, daß zeigt wie man ein externes
 
  // Programm startet und dessen Outout einliest.
 
  // Programm startet und dessen Outout einliest.
Line 123: Line 126:
 
   AProcess.Free;   
 
   AProcess.Free;   
 
  end.
 
  end.
 +
</delphi>
  
 
=== Einlesen eines großen Outputs ===
 
=== Einlesen eines großen Outputs ===

Revision as of 11:44, 22 June 2007

Deutsch (de) English (en) español (es) français (fr) italiano (it) 日本語 (ja) Nederlands (nl) polski (pl) português (pt) русский (ru) slovenčina (sk) 中文(中国大陆)‎ (zh_CN)

Einleitung

Es gibt verschiedene Wege, ein externes Programm auszuführen, aber ich will mich hier auf einen konzentrieren: TProcess.

Wenn sie bereits ShellExecute und/oder WinExec in Delphi verwenden, dann können sie damit beginnen, TProcess als Alternative in FPC/Lazarus zu verwenden (dies ist auch gültig wenn sie Lazarus unter Linux starten, weil TProcess cross-platform ist).

Notiz: FPC/Lazarus bietet Unterstützung für ShellExecute und/oder WinExec, aber diese Unterstützung gibt es nur unter Win32. Wenn ihr Programm cross-platform sein soll, dann ist die Verwendung von TProcess der beste Weg!

SysUtils.ExecuteProcess

Wenn Sie keine Pipes oder sonstige Kontrollmechanismen benötigen, kann das Ausführen externer Programme sehr einfach über folgenden Befehl erreicht werden:

SysUtils.ExecuteProcess('/full/path/to/binary',['arg1','arg2']);

TProcess

Man kann TProcess benutzen um externe Programme zu starten. Vorteile von TProcess sind

  • Plattform unabhängig
  • Es besteht die Möglichkeit, von stdout zu lesen und auf stdin zu schreiben.

Ein einfaches Beispiel

<delphi>

// Dies ist ein Demoprogramm das zeigt wie man ein
// externes Programm startet.
program launchprogram;

// Hier binden wir Dateien ein, die hilfreiche Funktionen
// und Prozeduren haben, die wir benötigen.
uses 
  Classes, SysUtils, Process;

// Dies definiert die Variable "AProcess" als eine Variable 
// vom Typ "TProcess"
var 
  AProcess: TProcess;

// Hier beginnt der Befehlstext:
begin
  // Nun erstellen wir das Objekt TProcess und
  // weisen es der Variable AProcess zu.
  AProcess := TProcess.Create(nil);

  // Lassen sie uns den FreePascal Compiler verwenden.
  // Dazu müssen wir AProcess den Komandozeilenbefel
  // übergeben:
  AProcess.CommandLine := 'ppc386 -h';

  // Während das externe Programm läuft, soll unser
  // Programm natürlich nicht weiterlaufen.
  // Dies regeln wir mit folgender Bedingung: 
  AProcess.Options := AProcess.Options + [poWaitOnExit];

  // Nun muss AProcess noch ausgeführt werden...
  AProcess.Execute;

  // Wegen der oben gesetzter Bedingung wird dieser
  // Codeabschnitt erst erreicht, wenn ppc386 beendet
  // ist:
  AProcess.Free;   
end.

</delphi>

Das war's. Sie haben gerade gelernt, wie man in Ihrem Programm ein externes Programm aufruft.

Ein verbessertes Beispiel

Das ist nett, aber wie lese ich den Output von einem Programm, das ich laufen habe?

Lassen sie uns unser Beispiel ein wenig erweitern und einfach das tun:

<delphi>

// Dies ist ein Demoprogramm, daß zeigt wie man ein externes
// Programm startet und dessen Outout einliest.
program launchprogram;

// Hier binden wir Dateien ein, die hilfreiche Funktionen
// und Prozeduren haben, die wir benötigen.
uses 
  Classes, SysUtils, Process;

// Dies definiert die Variable "AProcess" als eine 
// Variable vom Typ "TProcess"
// Jetzt fügen wir auch eine TStringList hinzu to store the 
// data read from the programs output.
var 
  AProcess: TProcess;
  AStringList: TStringList;

// This is where our program starts to run
begin
  // Jetzt erzeugen wir das TProcess Objekt, und
  // ordnen es der Variablen AProcess zu.
  AProcess := TProcess.Create(nil);

  // Erzeugen des TStringList Objekts.
  AStringList := TStringList.Create;

  // Tell the new AProcess what the command to execute is.
  // Lassen sie uns den FreePascal Compiler verwenden
  AProcess.CommandLine := 'ppc386 -h';

  // We will define an option for when the program
  // is run. This option will make sure that our program
  // does not continue until the program we will launch
  // has stopped running. Also now we will tell it that
  // we want to read the output of the file.
  AProcess.Options := AProcess.Options + [poWaitOnExit, poUsePipes];

  // Now that AProcess knows what the commandline is 
  // we will run it.
  AProcess.Execute;
  
  // This is not reached until ppc386 stops running.

  // Now read the output of the program we just ran
  // into the TStringList.
  AStringList.LoadFromStream(AProcess.Output);
  
  // Speichert den output in eine Datei.
  AStringList.SaveToFile('output.txt');

  // Nun da die Datei gespeichert ist können wir 
  // TStringList und TProcess freigeben.
  AStringList.Free;
  AProcess.Free;   
end.

</delphi>

Einlesen eines großen Outputs

Im vorherigen Beispiel haben wir gewartet bis das Programm beendet wurde. Dann haben wir gelesen, was das Programm in seinen output geschrieben hatte. Aber angenommen das Programm schreibt eine Menge Daten in den output, the pipe becomes full and the called progam waits until the pipe has been read from. Aber das aufrufende Programm liest nicht davon, bis das aufgerufene Programm beendet wurde. Ein dead lock tritt auf.

Das folgende Beispiel benutzt daher nicht poWaitOnExit, but reads from the output, while the program is still running. The output is stored in a memory stream, der später verwendet werden kann, um den output in eine TStringList zu lesen.

<delphi>

program procoutlarge;
{
    Copyright (c) 2004 by Marc Weustink

    This example is creeated in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
}

uses
  Classes, Process, SysUtils;

const
  READ_BYTES = 2048;
  
var
  S: TStringList;
  M: TMemoryStream;
  P: TProcess;
  n: LongInt;
  BytesRead: LongInt;

begin
  // Wir können poWaitOnExit hier nicht nutzen weil wir
  // die Größe des Outputs nicht kennen. On Linux the size of the
  // output pipe is 2 kB. If the output data is more, we 
  // need to read the data. This isn't possible since we are 
  // waiting. So we get a deadlock here.
  //
  // Ein temporärer Memorystream wird verwendet, um den output zu puffern.
  
  M := TMemoryStream.Create;
  BytesRead := 0;

  P := TProcess.Create(nil);
  P.CommandLine := 'ppc386 -va bogus.pp';
  P.Options := [poUsePipes];
  WriteLn('-- executing --');
  P.Execute;
  while P.Running do
  begin          
    // stellt sicher daß wir Platz haben
    M.SetSize(BytesRead + READ_BYTES);
    
    // versuche es zu lesen
    n := P.Output.Read((M.Memory + BytesRead)^, READ_BYTES);
    if n > 0 
    then begin
      Inc(BytesRead, n);
      Write('.')
    end
    else begin     
      // keine Daten, warte 100 ms
      Sleep(100); 
    end;
  end;
  // lese den letzten Teil
  repeat
    // stellt sicher daß wir Platz haben
    M.SetSize(BytesRead + READ_BYTES);
    // versuche es zu lesen
    n := P.Output.Read((M.Memory + BytesRead)^, READ_BYTES);
    if n > 0 
    then begin
      Inc(BytesRead, n);
      Write('.');
    end;
  until n <= 0;
  if BytesRead > 0 then WriteLn;
  M.SetSize(BytesRead); 
  WriteLn('-- executed --');
  
  S := TStringList.Create;
  S.LoadFromStream(M);
  WriteLn('-- linecount = ', S.Count, ' --');
  for n := 0 to S.Count - 1 do
  begin
    WriteLn('| ', S[n]);
  end;
  WriteLn('-- end --');
  S.Free;
  P.Free;
  M.Free;
end.

</delphi>

Verwendung von input und output eines TProcess

Siehe processdemo Beispiel im Lazarus-CCR SVN.

Hinweise für die Verwendung von TProcess

Wenn sie ein cross-platform Programm erstellen, können sie commandline entsprechend dem OS ändern, unter Verwendung der Direktiven "{$IFDEF}s" und "{$ENDIF}s".

Beispiel:

{...}
  AProcess:TProcess.Create(nil)
  {$IFDEF WIN32}
  AProcess.CommandLine := 'calc.exe'; //Windows Rechner
  {$ENDIF}
  {$IFDEF LINUX}
  AProcess.CommandLine := 'kcalc'; //KDE Rechner
  {$ENDIF}
  AProcess.Execute; //alternativ können sie AProcess.Active:=True verwenden
{...}

Beispiel von "talking" mit dem aspell Prozess

Innerhalb des pasdoc Quellcodes können sie zwei Units finden that perform spell-checking by "talking" with running aspell process through pipes:

  • PasDoc_ProcessLineTalk.pas unit implementiert die TProcessLineTalk Klasse, einen Nachfahren von TProcess, der einfach benutzt werden kann to talk with any process on a line-by-line basis.
  • PasDoc_Aspell.pas units implementiert die TAspellProcess Klasse, that performs spell-checking by using underlying TProcessLineTalk instance to execute aspell and communicate with running aspell process.

Beide Units sind ziemlich unabhängig vom Rest der pasdoc Quellen, so they may serve as real-world examples of using TProcess to run and communicate through pipes with other program.