Difference between revisions of "Executing External Programs/ja"
(→TProcess: Translation added) |
m (→TProcess: Transltion added) |
||
Line 144: | Line 144: | ||
1.0.x では '''Unix.Shell''' は非推奨になりっています。fpsystem をお使いください。 | 1.0.x では '''Unix.Shell''' は非推奨になりっています。fpsystem をお使いください。 | ||
− | ==TProcess== | + | == TProcess == |
TProcessを使用して外部プログラムを実行することができます。TProcessを使うメリットは次の通りです。 | TProcessを使用して外部プログラムを実行することができます。TProcessを使うメリットは次の通りです。 | ||
− | *プラットホームに依存しない | + | * プラットホームに依存しない |
− | * | + | * 標準入力からの読み込み、標準出力への書き込みが可能 |
+ | * コマンドの終了を待つことも、待たずに実行を続けることもできる | ||
− | + | 重要な注意点: | |
− | + | * TProcess はターミナルでもシェルでもありません。直接スクリプトを実行することや、 "|"、">"、"<"、"&" などを使って出力結果をリダイレクトすることはできません。 しかし、 TProcess を用いて同様な結果を得ることができます。いくつかのサンプルを下に示します。 | |
− | + | * おそらく Linux/Unix では、実行ファイルのフルパスを指定する必要があります。例えば 'cp' ではなく '/bin/cp' を使います。実行ファイルの存在するディレクトリが標準的な PATH 環境変数に含まれるならば、LCL の [[doc:lcl/fileutil/index.html|FileUtil]] にある [[doc:lcl/fileutil/finddefaultexecutablepath.html|FindDefaultExecutablePath]] 関数が使えます。 | |
− | + | * Windows では、コマンドに PATH が通っていれば、フルパスを指定する必要はありません。 | |
+ | * [[doc:fcl/process/tprocess.html|TProcess reference]] | ||
===単純な見本=== | ===単純な見本=== | ||
Line 183: | Line 185: | ||
− | + | // プログラムを走らせるときの、オプションを定義しましょう | |
− | + | // このオプションは、実行した外部プログラムが停止するまで、 | |
− | + | // このプログラムが動かないようにします vvvvvvvvvvvvvv | |
AProcess.Options := AProcess.Options + [poWaitOnExit]; | AProcess.Options := AProcess.Options + [poWaitOnExit]; | ||
− | + | // AProcessにコマンドを実行させましょう | |
AProcess.Execute; | AProcess.Execute; | ||
− | + | // ppc386が停止するまで、これは実行されません。 | |
AProcess.Free; | AProcess.Free; | ||
end. | end. | ||
Line 199: | Line 201: | ||
=== より向上した(でもまだ正しくない)見本 === | === より向上した(でもまだ正しくない)見本 === | ||
− | + | それでは、どのようにして実行した外部プログラムの出力を読めばよいでしょう? | |
先ほどの例を拡張してみましょう。 | 先ほどの例を拡張してみましょう。 | ||
Revision as of 15:15, 22 March 2014
│
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) │
概要
外部のプログラムを動かすにはいくつかの方法がありますが、このチュートリアルでは TProcessを中心に紹介しています。
Delphiで ShellExecute や WinExec を使っていたなら、 FPC/Lazarusにおける代替としてTProcessを使用するようにしましょう。(TProcess は cross-platformであり、Linuxでも有効です。)
Note: FPC/Lazarus は ShellExecute 及び WinExec をサポートしていますが、win32のみで有効です。 あなたのプログラムが cross-platformを目標にしているなら、TProcessの使用が最善です! 次に、さまざまな手法の比較を表にします
方法 | プラットフォーム | 一行で書けるか | 機能の程度 |
SysUtils.ExecuteProcess | クロスプラットフォーム | Yes | 極めて限定的、同期的 |
(ShellApi) ShellExecute | MS Windows のみ | Yes | 多数。より高度な/管理者権限で実行可能 |
Unix fpsystem, fpexecve | Unix のみ | ||
TProcess | クロスプラットフォーム | No | 全機能が可能 |
RunCommand(InDir) | クロスプラットフォーム FPC 2.6.2以降が必要 | Yes | よくあるTProcess利用法をカバー |
(LCLIntf) OpenDocument | クロスプラットフォーム | Yes | 文書を開くだけ |
SysUtils.ExecuteProcess
(クロスプラットフォーム)
最も簡単な方法は、SysUtils.ExecuteProcessを使用することです。ただし、モーダル(訳注: 外部プログラムに制御が移ること。下記)ですし、パイプもいかなるコントロールも使えません:
SysUtils.ExecuteProcess(UTF8ToSys('実行ファイルへのフルパス'), '', [])
呼出しは同期的です: 呼び出した側は、外部プログラムの動作が終了するまで「ハング」します - しかしアプリケーションの実行を進める前に、ユーザになにかをさせるには便利かもしれません。次の節の TProcess はもっと広い用途に用いることができ、おすすめです。Windows だけでいいなら、ShellExecute も使えます。
MS Windows : CreateProcess, ShellExecute と WinExec
![Note icon Light bulb](https://upload.wikimedia.org/wikipedia/commons/d/d8/Nuvola_apps_ktip.png)
![Note icon Light bulb](https://upload.wikimedia.org/wikipedia/commons/d/d8/Nuvola_apps_ktip.png)
ShellExecute は MS Windows で標準的な関数です (ShellApi.h)。マイクロソフトによる解説は documentation on MSDN をどうぞ。この関数が不安定だと思う方は、この解説の中の COM の初期化についての注釈をお読み下さい。
uses ..., ShellApi;
// 簡単な一行野郎(エラーを無視) :
if ShellExecute(0,nil, PChar('"C:\my dir\prog.exe"'),PChar('"C:\somepath\some_doc.ext"'),nil,1) =0 then;
// バッチファイルを実行 :
if ShellExecute(0,nil, PChar('cmd'),PChar('/c mybatch.bat'),nil,1) =0 then;
// 任意のフォルダでコマンドプロンプトを開く :
if ShellExecute(0,nil, PChar('cmd'),PChar('/k cd \path'),nil,1) =0 then;
// (隠れたコマンドウィンドウを通して)start コマンドを使ってデフォルトブラウザでウェブページの URL を開く:
if ShellExecute(0,nil, PChar('cmd'),PChar('/c start www.lazarus.freepascal.org/'),nil,0) =0 then;
// あるいは便利な手続:
procedure RunShellExecute(const prog,params:string);
begin
// ( ハンドル, nil/'open'/'edit'/'find'/'explore'/'print', // 'open' は必ずしも必要ではない
// パス付きプログラム名, 引数, 作業用フォルダ,
// 0=隠し / 1=SW_SHOWNORMAL / 3=最大化 / 7=最小化) //定数 SW_ は Windows ユニットにある
if ShellExecute(0,'open',PChar(prog),PChar(params),PChar(extractfilepath(prog)),1) >32 then; //成功
// 0..32 はエラー
end;
WideChar 版の ShellExecuteExW や AnsiChar 版の ShellExecuteExA もあります。
The fMask option can also use SEE_MASK_DOENVSUBST or SEE_MASK_FLAG_NO_UI or SEE_MASK_NOCLOSEPROCESS, etc.
If in Delphi you used ShellExecute for documents like Word documents or URLs, have a look at the open* (openurl etc) functions in lclintf (see the Alternatives section lower down this page).
Using ShellExecuteEx for elevation/administrator permissions
If you need to execute external program with administrator/elevated privileges, you can use the runas method with the alternative ShellExecuteEx function:
uses ShellApi, ...;
function RunAsAdmin(const Handle: Hwnd; const Path, Params: string): Boolean;
var
sei: TShellExecuteInfoA;
begin
FillChar(sei, SizeOf(sei), 0);
sei.cbSize := SizeOf(sei);
sei.Wnd := Handle;
sei.fMask := SEE_MASK_FLAG_DDEWAIT or SEE_MASK_FLAG_NO_UI;
sei.lpVerb := 'runas';
sei.lpFile := PAnsiChar(Path);
sei.lpParameters := PAnsiChar(Params);
sei.nShow := SW_SHOWNORMAL;
Result := ShellExecuteExA(@sei);
end;
procedure TFormMain.RunAddOrRemoveApplication;
begin
// Example that uses elevated rundll to open the Control Panel to Programs and features
RunAsAdmin(FormMain.Handle, 'rundll32.exe shell32.dll,Control_RunDLL appwiz.cpl', '');
end;
Unix fpsystem, fpexecve と shell
これらの関数はプラットフォーム依存です。
1.0.x では Unix.Shell は非推奨になりっています。fpsystem をお使いください。
TProcess
TProcessを使用して外部プログラムを実行することができます。TProcessを使うメリットは次の通りです。
- プラットホームに依存しない
- 標準入力からの読み込み、標準出力への書き込みが可能
- コマンドの終了を待つことも、待たずに実行を続けることもできる
重要な注意点:
- TProcess はターミナルでもシェルでもありません。直接スクリプトを実行することや、 "|"、">"、"<"、"&" などを使って出力結果をリダイレクトすることはできません。 しかし、 TProcess を用いて同様な結果を得ることができます。いくつかのサンプルを下に示します。
- おそらく Linux/Unix では、実行ファイルのフルパスを指定する必要があります。例えば 'cp' ではなく '/bin/cp' を使います。実行ファイルの存在するディレクトリが標準的な PATH 環境変数に含まれるならば、LCL の FileUtil にある FindDefaultExecutablePath 関数が使えます。
- Windows では、コマンドに PATH が通っていれば、フルパスを指定する必要はありません。
- TProcess reference
単純な見本
// これはどのようにして外部プログラムを実行するかを示すデモプログラムです
program launchprogram;
// 関数や手続きを使用するためにファイルをインクルードします
uses
Classes, SysUtils, Process;
// "TProcess"型の変数"AProcess"を定義します
var
AProcess: TProcess;
// ここからプログラムが開始します
begin
// TProcessオブジェクトを生成し、変数AProcessにアサインします
AProcess := TProcess.Create(nil);
// AProcessに実行するコマンドを伝えます
// FreePascalコンパイラを実行してみましょう
AProcess.Executable:= 'ppc386';
// ppc386に渡すコマンドライン引数を与えます (というわけで ppc386 -h を実行することになります):
AProcess.Parameters.Add('-h');
// .Executable と .Parameters は以前の版の FPC にはなく、
// .CommandLine プロパティを用いていました。これはまだ残っていますが、旧式です。
// 使わないでください。
// プログラムを走らせるときの、オプションを定義しましょう
// このオプションは、実行した外部プログラムが停止するまで、
// このプログラムが動かないようにします 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-2011 by Marc Weustink and contributors
This example is created 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.
}
// これはどのようにして外部プログラムを実行させ、その出力を読むかを
// 示す(実用的な)デモプログラムです。
// Memorystream が一時的な出力バッファとして使われます
uses
Classes, Process, SysUtils;
const
READ_BYTES = 2048;
var
OurCommand: String;
OutputLines: TStringList;
MemStream: TMemoryStream;
OurProcess: TProcess;
NumBytes: LongInt;
BytesRead: LongInt;
begin
// 一時的な Memorystream が出力バッファとして使われます
MemStream := TMemoryStream.Create;
BytesRead := 0;
OurProcess := TProcess.Create(nil);
// ディレクトリの再帰的読み出しは好例です。
OurCommand:='invalid command, please fix the IFDEFS.';
{$IFDEF Windows}
//dir は CMD.EXE の内部コマンドなので直接使えません。
//そこでシェルを使います:
OurCommand:='cmd.exe /c "dir /s c:\windows\"';
{$ENDIF Windows}
{$IFDEF Unix}
//LinuxやUnixてテストしてください
OurCommand := 'ls --recursive --all -l /';
{$ENDIF Unix}
writeln('-- Going to run: ' + OurCommand);
OurProcess.CommandLine := OurCommand;
// 出力サイズが不明であり、poWaintOnExit は使用できません。
// Linux では出力パイプの大きさは2KBであり、
// データの大きさがこれ以上である場合、デッドロックに乗り上げる
// ことになります。
OurProcess.Options := [poUsePipes];
WriteLn('-- External program run started');
OurProcess.Execute;
while True do
begin
// メモリ空間を確保します
MemStream.SetSize(BytesRead + READ_BYTES);
// 読んでいきます
NumBytes := OurProcess.Output.Read((MemStream.Memory + BytesRead)^, READ_BYTES);
if NumBytes > 0 // All read() calls will block, except the final one.
then begin
Inc(BytesRead, NumBytes);
Write('.') //スクリーンに進行状態を表示します
end else
BREAK // プログラムの実行が終了します
end;
if BytesRead > 0 then WriteLn;
MemStream.SetSize(BytesRead);
WriteLn('-- External program run complete');
OutputLines := TStringList.Create;
OutputLines.LoadFromStream(MemStream);
WriteLn('-- External program output line count = ', OutputLines.Count, ' --');
for NumBytes := 0 to OutputLines.Count - 1 do
begin
WriteLn(OutputLines[NumBytes]);
end;
WriteLn('-- Program end');
OutputLines.Free;
OurProcess.Free;
MemStream.Free;
end.
TProcessの出力と入力を使用する
Lazarus-CCR SVNのprocessdemoを参照してください。
TProcess利用のヒント
クロスプラットフォームのアプリケーションを作成する場合、"{$IFDEF}s" と "{$ENDIF}s" 命令を利用することで、OSに応じてコマンドラインを変更できます。
例:
{...}
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
{...}
OS X のアプリケーションバンドルを前面に出す
アプリケーションバンドル は、バンドル内の実行ファイルを指定することで TProcess から起動することができます。たとえば:
AProcess.Executable:='/Applications/iCal.app/Contents/MacOS/iCal';
これで、「カレンダー」が起動します。しかし、ウインドゥは現在使用中のアプリケーションの後ろ側にいってしまいます。アプリケーションを前面に出すには、open ユーティリティを引数 -n 付きで用います。
AProcess.Executable:='/usr/bin/open'; AProcess.Parameters.Add('-n'); AProcess.Parameters.Add('/Application/iCal.app');
アプリケーションが引数を要求する場合、open に、--arg 引数を渡すことができます。アプリケーションに渡す引数はその後に書きます:
AProcess.Parameters.Add('--args'); AProcess.Parameters.Add('argument1'); AProcess.Parameters.Add('argument2');
aspell processによるプロセス間通信(talking)のサンプル
pasdocのソースコードを見ると、パイプを介して実行中のaspellプロセスと通信することでスペルチェックを行う二つのユニットがあります:
- PasDoc_ProcessLineTalk.pas unit は、TProcessを継承するTProcessLineTalkクラスを実装しています。 このクラスによって、どんなプロセスとの間でも行単位の通信が容易にできます。
- PasDoc_Aspell.pas units は、TAspellProcessクラスを実装しています。 このクラスは、基礎となるTProcessLineTalkのインスタンスをaspellプロセスの実行と実行中のaspellプロセスとの通信に用いながら、スペルチェックを行います。
両ユニットは pasdoc の残りのソースから比較的独立しています。 パイプを介して他のプログラムを実行し、プロセスと通信するためにTPorcess を用いる実例として役に立つでしょう。