Difference between revisions of "Executing External Programs/ja"

From Free Pascal wiki
Jump to navigationJump to search
Line 67: Line 67:
 
先ほどの例を拡張してみましょう。
 
先ほどの例を拡張してみましょう。
  
<delphi>
+
<delphi> // これはどのようにして外部プログラムを実行させ、その出力を読むかを
// これはどのようにして外部プログラムを実行させ、その出力を読むかを
 
 
  // 示すデモプログラムです。
 
  // 示すデモプログラムです。
 
  program launchprogram;
 
  program launchprogram;
Line 116: Line 115:
 
   AStringList.Free;
 
   AStringList.Free;
 
   AProcess.Free;   
 
   AProcess.Free;   
  end.
+
  end.</delphi>
</delphi>
 
  
 
===巨大な出力を読み取る===
 
===巨大な出力を読み取る===

Revision as of 12:46, 23 May 2009

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)

日本語版メニュー
メインページ - 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 関数を使用できます。

単純な見本

<pascal>

// これはどのようにして外部プログラムを実行するかを示すデモプログラムです
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.

</pascal>

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

より向上した見本

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

<delphi> // これはどのようにして外部プログラムを実行させ、その出力を読むかを

// 示すデモプログラムです。
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.</delphi>

巨大な出力を読み取る

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

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

<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
  // 出力サイズが不明であり、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;</delphi>

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

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

TProcess利用のヒント

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

Example: <delphi> {...}

  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
{...}</delphi>

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

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

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