.NETでは、プロセスに関する情報の取得や操作はProcessクラスによって行うことができます。 Processクラスでは、自プロセスや他プロセスを参照し、プロセスの状態・情報を取得することができます。 (§.プロセス情報)
子プロセスを生成する(他のアプリケーションを起動する)場合にもProcessクラスを使います。 (§.子プロセスの起動) 子プロセスを起動する際に指定するコマンドライン引数などのオプションは、ProcessStartInfoで指定することができます。 (§.子プロセスの起動オプション)
ここではProcessクラス、およびプロセスの起動と終了に関する事項としてエントリーポイント、コマンドライン引数、終了ステータス等についてを解説します。
プロセスの標準入出力については自プロセスの標準入出力、子プロセスを起動して標準入出力への読み書きを行う場合については子プロセスの標準入出力、プロセスの環境変数については環境変数にて個別に解説しています。
自プロセス
ここでは主にコンソールアプリケーションやウィンドウを持つアプリケーションなど、単一のプロセスとして動作するアプリケーションに関して述べます。 スタンドアロンではないサーバーサイドアプリケーション等にはあてはまらない事柄もあるので注意してください。
プロセスのエントリポイント(Mainメソッド)
.NETでは、プロセスのエントリポイントはMain
メソッドになります。 このメソッドは任意の名前のクラスに持たせることができます。 プロセス名とクラス名を異なるものにすることもできます。
エントリポイント(Mainメソッド)はstatic
(VBではShared
)なメソッドにする必要があります。 一方、エントリポイントを持つ型(クラスまたは構造体)自体はstatic
である必要はありません。
また、誤って呼び出されてしまう可能性を排除するため、エントリポイントはpublic
にするべきではないとされています。 ただし、エントリポイントをpublic
にしてもエラー等にはならず、エントリポイントとしての動作に変わりはありません。
Main
という名前のメソッド以外をエントリポイントにすることはできません。 コンパイラはMain
という名前のメソッドを探し、それをエントリポイントとして使用します。
プロセスの引数・コマンドライン引数を受け取りたい場合は、Main
メソッドの引数として受け取ります。 コマンドライン引数については§.コマンドライン引数で解説します。
また、Main
メソッドの戻り値をint
/Integer
にすることで、プロセスの終了ステータスを返すようにすることもできます。 終了ステータスについては§.終了ステータス(終了コード)で解説します。
Mainメソッドを非同期(async)メソッドとする場合、戻り値の型をTaskにします。 終了ステータスを返す場合は、Task<int>にします。 非同期のMainメソッドはC#7.1以降で使用できます。
複数のMainメソッド (エントリポイントの指定)
Main
という名前のメソッドを持つ型が複数ある場合はコンパイルエラーとなります。 この場合、コンパイラオプション/main、またはプロジェクトファイルのプロパティ<StartupObject>
によって、どの型のMainメソッドをエントリポイントとするか指定することができます。 /main
, <StartupObject>
では、Mainメソッドを持つ型を名前空間.型名
の形式で指定します。
IDE上でもエントリポイントをコンパイルオプションとして変更することができます。 またIDE上では、MainメソッドではなくフォームをWindowsアプリケーションのエントリポイントとして指定する場合もあります。
トップレベルステートメント
C# 9.0以降では、型宣言・Mainメソッド宣言を省略してステートメントを直接記述することができます。 トップレベルステートメントを使ったコードでは、ファイル全体がMainメソッドであるかのように扱われます。
トップレベルステートメントでは、コマンドライン引数をメソッドの引数として取得することができないので、代わりにEnvironment.GetCommandlineArgsメソッドを使って取得します。
構造体でのMainメソッド
通常エントリポイントはクラス(VBではモジュールの場合もある)に持たせることが多く、このようにすることは稀ですが、構造体にエントリポイントをもたせることもできます。
コマンドライン引数
プロセスに与えられたコマンドライン引数、起動時に渡される引数は、Mainメソッドの引数として受け取ることができます。 引数の名前は任意に指定できますが、型はString型の配列である必要があります。 Mainメソッドに渡される引数には、プロセスを起動した時のコマンドラインは含まれません。
プロセスを起動した時のコマンドラインを含めて引数を取得したい場合は、Environment.GetCommandLineArgsメソッドを使います。 詳しくはプロセス・アセンブリの情報 §.コマンドライン引数を参照してください。
コマンドライン引数の解析
コマンドライン引数の解析処理は実装者に任されます。 .NETのクラスライブラリでは、コマンドライン引数の解析を目的としたクラスやメソッド等は用意されていません。 そのため、独自に解析処理を記述するか、サードパーティーのライブラリ等を使用する必要があります。
こういったライブラリの一例として、Mono.Optionsがあります。 Mono.Optionsでは、UnixおよびWindowsでよく用いられる形式のコマンドライン引数の解析をサポートしていて、シンプルなコードで引数の解析を実装することができます。
終了ステータス(終了コード)
.NETでは例外のスローによって異常の発生とその原因を通知することが多いため、終了ステータスを用いることはあまりありませんが、終了ステータスを返すことはできます。
プロセスの終了ステータスを親プロセスに通知するには、Main
メソッドの戻り値をint
/Integer
、非同期の場合はTask<int>
にし、Main
メソッドの戻り値によって終了ステータスを返します。
Environment.Exitメソッドを使うことでも終了ステータスの設定を行うことができます。 このメソッドはexit
システムコールに似たもので、終了ステータスを設定すると同時にプロセスを即座に終了します。
終了ステータスの設定のみを行いたい場合は、Environment.ExitCodeプロパティを設定します。 Environment.Exitメソッドとは異なり、このプロパティに終了ステータスを設定してもプロセスの実行は継続します。
例外発生時の終了ステータス
スローされた例外がハンドリングされないことによりプロセスが終了する場合、終了ステータスとして0
以外の値が自動的に設定されます。 例外による終了ステータスはMainメソッドの型がvoid
の場合でも設定されます。
標準ストリーム (標準入出力)
標準出力への文字列の書き込みにはConsole.WriteLineメソッド、標準入力からの文字列の書き込みにはConsole.ReadLineメソッドなどを使います。 Consoleクラスとプロセスの標準ストリームの扱い方に関しては、自プロセスの標準入出力を参照してください。
プロセス情報
自プロセスに関する情報を取得するには、System.Diagnostics.Processクラスのメンバを参照します。 このクラスでは他プロセスを扱うこともできますが、現在のプロセス(自プロセス)を扱うにはProcess.GetCurrentProcessメソッドによってプロセスのインスタンスを取得します。
Processクラスからは、プロセス名・プロセスIDをはじめとして、以下のような情報を取得することができます。
プロパティ | 取得できる情報 | 備考 | |
---|---|---|---|
基本情報 | ProcessName | プロセス名 | |
Id | プロセスID | .NET 5以降では、Environment.ProcessIdプロパティからも自プロセスIDを取得できる | |
SafeHandle
Handle |
プロセスのネイティブハンドル | SafeHandleは、.NET Framework 4.6以降で使用可能 | |
BasePriority
PriorityClass |
プロセスの優先順位 | ||
StartTime | プロセスが起動した時刻 | ||
プロセッサ使用時間 | UserProcessorTime | ユーザーモードでのプロセッサ使用時間 | |
PrivilegedProcessorTime | 特権モードでのプロセッサ使用時間 | ||
TotalProcessorTime | ユーザーモード・特権モード合計のプロセッサ使用時間 | ||
メモリ | PagedMemorySize64 | プロセスに割り当てられたメモリ量 | |
WorkingSet64 | |||
VirtualMemorySize64ほか | |||
PeakPagedMemorySize64 | 同ピーク値 | ||
PeakWorkingSet64 | |||
PeakVirtualMemorySize64ほか | |||
プロパティ | 取得できる情報 | 備考 |
この他、Processクラスから取得できる情報についてはProcessクラスのドキュメントを参照してください。
メモリ使用量など、プロセスの詳細な状態を計測するにはパフォーマンスカウンタを用いることもできます。 詳しくはパフォーマンス情報の計測を参照してください。
マシンなどプロセスの実行環境・プラットフォームに関する情報を取得する方法についてはランタイム・システム・プラットフォームの情報、環境変数・自プロセスのパスなどに関する情報についてはプロセス・アセンブリの情報を参照してください。
子プロセス
子プロセスの起動
Process.Startメソッドに実行可能ファイルのパスを指定すると、それを子プロセスとして実行することができます。 .NET Frameworkでは、パスが通っていればコマンドを起動したり、拡張子に関連付けられた実行可能ファイルでファイルを開くこともできます。
ProcessStartInfoを使用することで、子プロセスを起動する際のオプションを詳細に指定することができます。
子プロセスの終了の待機・終了ステータスの取得
Process.Startメソッドでプロセスを起動した場合、起動した時点で処理が戻ります。 起動したプロセスの終了を待機する場合は、Process.WaitForExitメソッドまたはProcess.WaitForExitAsyncメソッドを使います。
Process.Startメソッドは起動したプロセスをProcessインスタンスとして返すので、このインスタンスに対してWaitForExitメソッドを呼び出すことにより、起動したプロセスの終了を待機することができます。 また、ExitCodeプロパティを参照すれば終了ステータスを取得することもできます。
WaitForExitメソッドではタイムアウト時間をミリ秒単位で指定することもできます。 タイムアウトまでにプロセスが終了したかどうかは戻り値によって知ることができます。
起動したプロセスを即座に終了させたい場合は、Process.Killメソッドを使うことができます。
子プロセスの起動オプション
ProcessStartInfoクラスを用いると、引数以外にも子プロセス起動時のオプションや動作を細かく指定することができます。 環境変数やカレントディレクトリを変更した状態で起動したい場合には、ProcessStartInfoクラスを使って子プロセスを起動する必要があります。
例として、コマンドなどを子プロセスとして起動したい場合・シェルを使用して起動したい場合には、UseShellExecuteをtrue
にします。 逆に、実行可能ファイルを直接指定して子プロセスとして起動したい場合や、標準入出力を扱いたい場合、環境変数を設定して起動する場合は、false
にする必要があります。 UseShellExecuteはランタイムによってデフォルト値が異なり、.NET Frameworkではtrue
、.NET Core/.NET 5ではfalse
となっています。
また、Windows上でコマンドラインプロセス(コンソールアプリケーション)を起動する場合にDOSプロンプトウィンドウを表示したくない場合には、CreateNoWindowにtrue
を指定して起動します。 CreateNoWindowはデフォルトではfalse
となっています。
子プロセスの起動と環境変数の設定については環境変数 §.子プロセスの環境変数でも解説しています。
子プロセスのコマンドライン引数
子プロセスを起動する際のコマンドライン引数は、ProcessStartInfoクラスのArgumentsプロパティで指定することができます。 ただし、このプロパティは引数すべてを単一の文字列として指定する必要があり、また空白
を含む文字列とダブルクオーテーション"
のエスケープが必要な場合はエスケープ済みの文字列を指定する必要があります。
.NET Standard 2.1/.NET Core 2.1以降では、ArgumentListプロパティを使用することもできます。 このプロパティは引数を文字列のコレクションとして指定することができ、また空白
を含む文字列とダブルクオーテーション"
のエスケープも自動的に行われます。
コマンドライン引数はArgumentsまたはArgumentListのどちらか一方のみが設定でき、両方設定した状態でProcess.Startメソッドを呼び出すとInvalidOperationExceptionをスローします。 また、Argumentsに設定した値はArgumentListには反映されず、逆もまた同様です。
上記のコードでは、コマンドライン引数を与えた上で再帰的にプロセスを起動しています。 上記のコードで環境変数CHILD
を指定せずに実行した場合Fork爆弾と同様の動作となるため、コードを改変する場合は注意してください。
子プロセスの標準ストリーム(標準入出力)
子プロセスの標準ストリームをリダイレクトしたい(子プロセスの標準出力を読み取る・標準入力に書き込む)場合は、ProcessStartInfo.RedirectStandardOutputをtrue
にし、同時にProcessStartInfo.UseShellExecuteをfalse
にして子プロセスを起動します。
標準出力に書き込まれた内容を読み込むには、Process.StandardOutput.ReadToEndメソッドなどによって取得します。
子プロセスの標準ストリームはProcess.StandardInput・Process.StandardOutput・Process.StandardErrorプロパティよりStreamReaderクラス/StreamWriterクラスとして取得することができます。 StreamReader/StreamWriterを使った読み書きについてはStreamReaderクラス・StreamWriterクラスを参照してください。
このほか、子プロセスの標準ストリームの扱い方や、非同期での標準ストリームへの読み書きなど、より具体的な方法は子プロセスの標準入出力で解説しています。
他プロセスの取得
Process.GetProcessesメソッドを使うと、現在起動中の取得可能なプロセスを取得することができます。
また、Process.GetProcessByIdメソッド・Process.GetProcessesByNameメソッドを使うと、プロセスIDまたはプロセス名から該当するプロセスの情報を取得することができます。
プロセス間の協調
排他制御
プロセス間で排他制御を行うには、ミューテックス(System.Threading.Mutexクラス)やセマフォ(System.Threading.Semaphoreクラス)を用いることができます。
これらのクラスの詳細や、これらを使ったプロセス間排他制御の具体例については、以下のページを参照してください。
プロセス間通信
ここで紹介する手法は.NET Frameworkのみで使用できます。 .NET Core/.NET 5以降では使用できません。
System.Runtime.Remoting名前空間にあるクラスを使うことでプロセス間通信を行うことができます。 ここではその一例として、IPCチャンネルを使ったプロセス間通信を例示します。
まず、サーバー・クライアントの両方で使用される、通信オブジェクトとなるクラスを用意します。 このクラスは、MarshalByRefObjectを継承する必要があります。 MarshalByRefObjectは、リモート処理によってプロセス境界(厳密にはアプリケーションドメイン境界)を超えるオブジェクトに必要な機能を備えた基本クラスです。
次にサーバー側です。 サーバー側では、IpcServerChannelクラスでチャネルを登録し、RemotingServices.Marshalメソッドで上記のリモートオブジェクトを公開します。
IpcServerChannelクラスを使用する場合、System.Runtime.Remoting.dll
をアセンブリ参照に追加する必要があります。
最後にクライアント側です。 クライアント側では、サーバー同様にIpcClientChannelクラスでチャネルを登録し、Activator.GetObjectメソッドで公開されているリモートオブジェクトを取得します。
IpcClientChannelクラスを使用する場合、System.Runtime.Remoting.dll
をアセンブリ参照に追加する必要があります。
サーバー側の起動後にクライアント側を起動すると、サーバー側で設定された文字列を取得することができます。 ICPのほか、System.Runtime.Remoting名前空間のクラスを使うことでHTTP・TCPを用いたプロセス間通信を行うこともできます。