Difference between revisions of "Executing External Programs/ja"

From Free Pascal wiki
Jump to navigationJump to search
Line 97: Line 97:
 
   
 
   
 
   // プログラムを走らせるときの、オプションを定義しましょう
 
   // プログラムを走らせるときの、オプションを定義しましょう
   // このオプションは、実行した外部プログラムが停止するまで、
+
   // [poWaitOnExit]は、実行した外部プログラムが停止するまで、
 
   // このプログラムが動かないようにします
 
   // このプログラムが動かないようにします
   // また、ファイルの出力を読みたいことを伝えます
+
   // また、[poUsePipes]でファイルの出力を読みたいことを伝えます
 
   AProcess.Options := AProcess.Options + [poWaitOnExit, poUsePipes];
 
   AProcess.Options := AProcess.Options + [poWaitOnExit, poUsePipes];
 
   
 
   

Revision as of 00:24, 11 July 2008

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('/full/path/to/binary',['arg1','arg2'])を使用することです。

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利用のヒント

cross-platform programを作成する場合、"{$IFDEF}s" and "{$ENDIF}s" 命令を利用することで、OSに応じて commandline を変更できる。

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 processを実行することで、"talking"によってスペルチェックを行う - が見つかる。 Inside pasdoc source code you can find two units that perform spell-checking by "talking" with running aspell process through pipes:

  • PasDoc_ProcessLineTalk.pas unit は、TProcessを継承するTProcessLineTalkクラスを実装している。これによって、line-by-line basisの上の他のプロセスとの通信が容易となる。
  • PasDoc_ProcessLineTalk.pas unit implements TProcessLineTalk class, descendant of TProcess, that can be easily used to talk with any process on a line-by-line basis.
  • PasDoc_Aspell.pas units は、TAspellProcessクラスを実装している。TProcessLineTalkのインスタンスの下にあり、aspell processを実行することで、aspellを実行し、通信これは、スペルチェックを行う。
  • PasDoc_Aspell.pas units implements TAspellProcess class, that performs spell-checking by using underlying TProcessLineTalk instance to execute aspell and communicate with running aspell process.

いずれもユニットも残りのpasdoc sourcesから独立しているため、TProcessを使用した実例 - パイプを介して他のプログラムと実行、通信する実例 - として役に立つだろう。 Both units are rather independent from the rest of pasdoc sources, so they may serve as real-world examples of using TProcess to run and communicate through pipes with other program.