cmd.exeとVisualBasic.netでコンソールアプリケーションを実行した場合の挙動の違いについて

投稿者: Anonymous

VB.net上で対話型コンソールアプリケーションの実行について以前質問させて頂いた者です。
質問:ウィンドウズフォームアプリケーションから対話型コンソールアプリケーションを操作する為の実装について

あれから試行錯誤した結果、VB.netから起動した mysql.exe は、正しくないコマンド列を受け取るとプロセスが終了してしまうようです。しかしコマンドプロンプトから実行した場合は、エラーが発生しても mysql.exe は終了しません。

前回Rippleさんに頂いた回答では次のように書かれていました。

mysqlコマンドは出力がリダイレクトされると動きが変わります。出力を最適化するため、出力先がコンソールかどうか判断してわざわざ動きを変えているようです。

確かに p.St‌​artInfo.RedirectStandardOutput = True を指定したので、私の作ったプログラムがリダイレクトを利用しているのだと理解できます。ということは cmd.exe は別の方法で mysql.exe とやり取りしているはずですが、それはどういったもので、VB.netから実行するときと何が違うのでしょうか?

両者ともアプリケーションが対話型コンソールアプリケーションを実行するという構図は同じように思えるのですが…

よろしくご教示お願い致します。

解決

長くなってしまったので先にまとめます。

通常、標準入出力には端末=コンソールウィンドウを指しているハンドルが使われます。これを

p.StartInfo.RedirectStandardOutput = true;

としたり、コマンドプロンプトで mysql > hoge.txt とすると、標準出力を端末以外のハンドルに差し換える(リダイレクト)ことになります。

mysql.exe は起動時に「標準入出力が端末を指しているかどうか」を調べているため、上記のようなリダイレクトを行った場合に挙動が変わります。


.NETのProcessクラスで使われているのは CreateProcess というAPIです。これはあるプロセスが別のプロセスを起動する際に使う一般的なAPIです。

CreateProcess には lpStartupInfo という引数があり、STARTUPINFO という構造体を指定するのですが、この中に標準入出力・エラー出力のファイルハンドルを指定するフィールドがあります。 cmd.exe でも mysql > out.txt などとしてリダイレクトする場合はこれを使っているはずです。

Processクラスでこれに相当するのが StartInfo プロパティで、RedirectStandardOutput 等を指定すると内部的に無名パイプ*1が生成され、Process.StandardOutput がこのパイプを指すようになります。

*1: このパイプとはプロセス間で共有できるトンネルのようなもので、作成すると入口と出口のファイルハンドルが得られます。これは実際のファイルを読み取り専用・書き込み専用で開いたときと同じように扱え、入口に書き込めば出口で読み取ることができます。

では、 cmd.exe から起動し、リダイレクトをしない場合は、どんなファイルハンドルを mysql.exe が受け取るのでしょうか。これはファイルでもなく、パイプでもなく、端末*2を指しています。

*2: 元は端末に限った話ではなく、UNIX系では character device 、Windows(MSDN) では character file などと呼ばれます。

Windowsでコンソールアプリケーションを起動するとコンソールウィンドウが生成されますが、このウィンドウに入出力するためのファイルハンドルも同時に用意されます。これが端末を指すファイルハンドルです。

ファイルハンドルが指すものが端末であるかどうかを識別する方法としては、Cランタイムライブラリの _isatty() や、ファイルハンドルの種類を調べる GetFileType() が使えます。

例えば VB.net でコンソールアプリケーションを作り、以下のようなプログラムを書いてみると面白いでしょう。この場合、VB.net側の標準入出力がそのまま使われることになるので・・・。

Sub Main()
    Dim p = New Process
    p.StartInfo.FileName = "C:xamppmysqlbinmysql.exe"
    p.StartInfo.UseShellExecute = False
    p.Start()
    p.WaitForExit()
End Sub

参考

回答者: Anonymous

Leave a Reply

Your email address will not be published. Required fields are marked *