14534


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

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

[前回Rippleさんに頂いた回答](https://ja.stackoverflow.com/a/12720/8000)では次のように書かれていました。

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

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

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

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


CC BY-SA 3.0
2015-08-15T05:42:03.943
14438
2015-08-17T13:40:50.670
2017-04-13T12:52:38.920
-1
10606
question
mysql windows .net
cmd.exeとVisualBasic.netでコンソールアプリケーションを実行した場合の挙動の違いについて


一般的に コンソールアプリケーションは 標準入力(STDIN＝キーボード)からデータを読み込み、標準出力(STDOUT＝コンソール行)
に出力しますが、これらの入出力を一時的に変更するのがリダイレクトです。リダイレクトはシェル（cmd.exe）が実施しますので、コンソールアプリケーション
としては標準入力と標準出力を使うようにプログラミングされています。

ご質問の mysql.exe に限っては、標準入力と出力が、ターミナルコンソール（cmd.exe）であるかどうかを内部でチェックして、動作を制御しています。

client/mysql.cc

[code]

      if (!isatty(0) || !isatty(1))
      {
        status.batch=1;
        opt_silent=1;
        ignore_errors=0;
      }
    
[/code]

`status.batch=1` は
[バッチモード](http://mysql.stu.edu.tw/doc/refman/5.1-olh/ja/batch-mode.html)
を意味する変数です。  
`opt_silent=1` は
[サイレントモード](http://mysql.stu.edu.tw/doc/refman/5.1-olh/ja/mysql-command-
options.html) を意味する変数  
`ignore_errors` は エラーを無視する変数を無効にしています。

> VB.netから起動した mysql.exe は、正しくないコマンド列を受け取るとプロセスが終了してしまうようです。

VB.netのことはよく分かりませんが、入力または出力をハンドリングするためにファイルか何かにリダイレクトしてるのだと思いますが、Mysql.exe
はリダイレクトかどうかを独自に判断して、バッチモードで動くため、正しくないSQLはエラー終了するようになっています。


CC BY-SA 3.0
2015-08-17T07:21:22.673
14517
2015-08-17T07:56:13.853
2015-08-17T07:56:13.853
5008
5008
14438
answer


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

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

[code]

    p.StartInfo.RedirectStandardOutput = true;
    
[/code]

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

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

* * *

.NETのProcessクラスで使われているのは [`CreateProcess`](https://msdn.microsoft.com/ja-
jp/library/Cc429066.aspx) というAPIです。これはあるプロセスが別のプロセスを起動する際に使う一般的なAPIです。

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

Processクラスでこれに相当するのが [`StartInfo`](https://msdn.microsoft.com/ja-
jp/library/system.diagnostics.process.startinfo\(v=vs.110\).aspx)
プロパティで、[`RedirectStandardOutput`](https://msdn.microsoft.com/ja-
jp/library/system.diagnostics.processstartinfo.redirectstandardoutput\(v=vs.110\).aspx)
等を指定すると内部的に無名パイプ*1が生成され、`Process.StandardOutput` がこのパイプを指すようになります。

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

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

*2: 元は端末に限った話ではなく、UNIX系では [character device](https://ja.wikipedia.org/wiki/%E3%83%87%E3%83%90%E3%82%A4%E3%82%B9%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB#.E3.82.AD.E3.83.A3.E3.83.A9.E3.82.AF.E3.82.BF.E3.83.87.E3.83.90.E3.82.A4.E3.82.B9) 、Windows(MSDN) では [character file](https://msdn.microsoft.com/ja-jp/library/windows/desktop/aa364960\(v=vs.85\).aspx) などと呼ばれます。

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

ファイルハンドルが指すものが端末であるかどうかを識別する方法としては、Cランタイムライブラリの
[`_isatty()`](https://msdn.microsoft.com/ja-
jp/library/f4s0ddew\(v=vs.120\).aspx) や、ファイルハンドルの種類を調べる
[`GetFileType()`](https://msdn.microsoft.com/ja-jp/library/Cc429323.aspx)
が使えます。

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

[code]

    Sub Main()
        Dim p = New Process
        p.StartInfo.FileName = "C:\xampp\mysql\bin\mysql.exe"
        p.StartInfo.UseShellExecute = False
        p.Start()
        p.WaitForExit()
    End Sub
    
[/code]

## 参考

  * [（新）APIから知るWindowsの仕組み - 第1回 Windowsのプロセスを詳しく探る：ITpro](http://itpro.nikkeibp.co.jp/article/COLUMN/20070126/259762/)
  * [c# - How to detect if Console.In (stdin) has been redirected? - Stack Overflow](https://stackoverflow.com/q/3453220/2818869)
  * [Process.StartWithCreateProcess - Microsoft Reference Source](http://referencesource.microsoft.com/#System/services/monitoring/system/diagnosticts/Process.cs,c50d8ac0eb7bc0d6,references)
  * [コンソール - EthernalWindows](http://eternalwindows.jp/windevelop/console/console00.html)


CC BY-SA 3.0
2015-08-17T13:27:43.247
14534
2015-08-17T13:40:50.670
2017-05-23T12:38:56.467
-1
8000
14438
answer