Difference between revisions of "Executing External Programs/ja"

From Lazarus wiki
m (Text replace - "delphi>" to "syntaxhighlight>")
m (Text replace - "pascal>" to "syntaxhighlight>")
Line 27: Line 27:
  
 
===単純な見本===
 
===単純な見本===
<pascal> // これはどのようにして外部プログラムを実行するかを示すデモプログラムです
+
<syntaxhighlight> // これはどのようにして外部プログラムを実行するかを示すデモプログラムです
 
  program launchprogram;
 
  program launchprogram;
 
   
 
   
Line 57: Line 57:
 
   // ppc386が停止するまで、これは実行されません。
 
   // ppc386が停止するまで、これは実行されません。
 
   AProcess.Free;   
 
   AProcess.Free;   
  end.</pascal>
+
  end.</syntaxhighlight>
  
 
おめでとう!自分のプログラムから外部プログラムを実行する方法を学べました。
 
おめでとう!自分のプログラムから外部プログラムを実行する方法を学べました。

Revision as of 00:50, 25 March 2012

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

日本語版メニュー
メインページ - Lazarus Documentation日本語版 - 翻訳ノート - 日本語障害情報


概要

外部のプログラムを動かすにはいくつかの方法がありますが、このチュートリアルでは TProcessを中心に紹介しています。

Delphiで ShellExecute や WinExec を使っていたなら、 FPC/Lazarusにおける代替としてTProcessを使用するようにしましょう。(TProcess は cross-platformであり、Linuxでも有効です。)

Note: FPC/Lazarus は ShellExecute 及び WinExec をサポートしていますが、win32のみで有効です。 あなたのプログラムが cross-platformを目標にしているなら、TProcessの使用が最善です!

SysUtils.ExecuteProcess

パイプや他のコントロールから成るフォームを使用しない場合、最も簡単な方法は、 SysUtils.ExecuteProcess('実行ファイルへのフルパス',['引数1','引数2'])を使用することです。

TProcess

TProcessを使用して外部プログラムを実行することができます。TProcessを使うメリットは次の通りです。

  • プラットホームに依存しない
  • stdinからの読み込み、stdoutへの書き込みが可能


Note: TProcess は terminal/shell ではないので、直接スクリプトを実行することや、出力結果をリダイレクトすることは出来ません。 しかし、 TProcess を用いて同様な結果を得ることができます。いくつかのサンプルを以下に示します。

Important: 実行ファイルへのフルパスを明示する必要があります。(例えば、'cp' ではなく '/bin/cp' のように。) もしもプログラムにパスが通っていたなら、LCLのFileUtil ユニットから、FindDefaultExecutablePath 関数を使用できます。

単純な見本

 // これはどのようにして外部プログラムを実行するかを示すデモプログラムです
 program launchprogram;
 
 // 関数や手続きを使用するためにファイルをインクルードします
 uses 
   Classes, SysUtils, Process;
 
 // "TProcess"型の変数"AProcess"を定義します
 var 
   AProcess: TProcess;
 
 // ここからプログラムが開始します
 begin
   // TProcessオブジェクトを生成し、変数AProcessにアサインします
   AProcess := TProcess.Create(nil);
 
   // AProcessに実行するコマンドを伝えます
   // FreePascalコンパイラを実行してみましょう
   AProcess.CommandLine := 'ppc386 -h';
 
   // プログラムを走らせるときの、オプションを定義しましょう
   // このオプションは、実行した外部プログラムが停止するまで、
   // このプログラムが動かないようにします           vvvvvvvvvvvvvv
   AProcess.Options := AProcess.Options + [poWaitOnExit];
 
   // AProcessは実行するコマンドラインを知っています
   AProcess.Execute;
 
   // ppc386が停止するまで、これは実行されません。
   AProcess.Free;   
 end.

おめでとう!自分のプログラムから外部プログラムを実行する方法を学べました。

より向上した見本

それでは、どのようにして実行した外部プログラムの出力を読めばよいでしょう? 先ほどの例を拡張してみましょう。

 // これはどのようにして外部プログラムを実行させ、その出力を読むかを
 // 示すデモプログラムです。
 program launchprogram;
 
 // 関数や手続きを使用するためにファイルをインクルードします
 uses 
   Classes, SysUtils, Process;
 
 // "TProcess"型の変数"AProcess"を定義します
 // また、プログラムの出力からデータを読み取り、
 // 保存する TStringList を追加します。
 var 
   AProcess: TProcess;
   AStringList: TStringList;
 
 // ここからプログラムが開始します
 begin
   // TProcessオブジェクトを生成し、変数AProcessにアサインします
   AProcess := TProcess.Create(nil);
 
   // TStringListオブジェクトを生成します
   AStringList := TStringList.Create;
 
   // AProcessに実行するコマンドを伝えます
   // FreePascalコンパイラを実行してみましょう
   AProcess.CommandLine := 'ppc386 -h';
 
   // プログラムを走らせるときの、オプションを定義しましょう
   // [poWaitOnExit]は、実行した外部プログラムが停止するまで、
   // このプログラムが動かないようにします
   // また、[poUsePipes]でファイルの出力を読みたいことを伝えます
   AProcess.Options := AProcess.Options + [poWaitOnExit, poUsePipes];
 
   // AProcessは実行するコマンドラインを知っています
   AProcess.Execute;
   
   // ppc386が停止するまで、次にいきません
 
   // プログラムの出力を、TStringListに読み込みます。
   AStringList.LoadFromStream(AProcess.Output);
   
   // ファイルに保存します
   AStringList.SaveToFile('output.txt');
 
   // ファイルが保存されたので、
   // TStringListとTProcessを開放します
   AStringList.Free;
   AProcess.Free;   
 end.

巨大な出力を読み取る

前述したサンプルでは、外部プログラムの終了まで待ち、その後に外部プログラムの出力内容を読み取りました。 ここで、外部プログラムが大量のデータを出力すると想定した場合、パイプが満杯になるとパイプの内容が読み出されるまで外部プログラムはストップします。 しかし、外部プログラムが終わるまでは、読み出し手のプログラムはパイプの内容を読み出せないため、袋小路に陥ります。

後述するサンプルでは poWaitOnExitを使わず、プログラムの実行中に出力から読むようにしました。出力はメモリストリームに保存され、追って TStringList に読み出されます。

 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
   // 出力サイズが不明であり、poWaintOnExit は使用できません。
   // Linux では出力パイプの大きさは2KBであり、
   // データの大きさがこれ以上である場合、行き詰まります。
   //
   // 一時的にMemorystreamが出力バッファとして使われます。
   
   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          
     // メモリ空間を確保します
     M.SetSize(BytesRead + READ_BYTES);
     
     // 読んでいきます
     n := P.Output.Read((M.Memory + BytesRead)^, READ_BYTES);
     if n > 0 
     then begin
       Inc(BytesRead, n);
       Write('.')
     end
     else begin     
       // データがないときは100 ms待ちます
       Sleep(100); 
     end;
   end;
   // 読み込みの最終段階です
   repeat
     // メモリ空間を確保します
     M.SetSize(BytesRead + READ_BYTES);
     // 読んでいきます
     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.

TProcessの出力と入力を使用する

Lazarus-CCR SVNのprocessdemoを参照してください。

TProcess利用のヒント

クロスプラットフォームのアプリケーションを作成する場合、"{$IFDEF}s" と "{$ENDIF}s" 命令を利用することで、OSに応じてコマンドラインを変更できます。

Example:

 {...}
   AProcess:TProcess.Create(nil)
   {$IFDEF WIN32}
   AProcess.CommandLine := 'calc.exe'; //Windows Calc
   {$ENDIF}
   {$IFDEF LINUX}
   AProcess.CommandLine := 'kcalc'; //KDE Calc
   {$ENDIF}
   AProcess.Execute; //右の書き方もできます。 AProcess.Active:=True
 {...}

aspell processによるプロセス間通信(talking)のサンプル

pasdocのソースコードを見ると、パイプを介して実行中のaspellプロセスと通信することでスペルチェックを行う二つのユニットがあります:

  • PasDoc_ProcessLineTalk.pas unit は、TProcessを継承するTProcessLineTalkクラスを実装しています。 このクラスによって、どんなプロセスとの間でも行単位の通信が容易にできます。
  • PasDoc_Aspell.pas units は、TAspellProcessクラスを実装しています。 このクラスは、基礎となるTProcessLineTalkのインスタンスをaspellプロセスの実行と実行中のaspellプロセスとの通信に用いながら、スペルチェックを行います。
  • 両ユニットは pasdoc の残りのソースから比較的独立しています。 パイプを介して他のプログラムを実行し、プロセスと通信するためにTPorcess を用いる実例として役に立つでしょう。