12720


※環境 Win764Bit, .NetFramework4.0 or 4.5

本来DOSプロンプトで動作する対話型プログラム、例えばMySQL.exeなどをにコマンドを送り、その表示結果を取得するウィンドウズフォームアプリケーションを作成しています。

動作イメージは添付の画像のような感じで、テキストボックスにコマンドを入力し、送信ボタンを押すことを繰り返す事で作業を進めます。

※入力されているテキストはイメージです。  
[![アプリケーションイメージ](https://i.stack.imgur.com/fExQp.png)](https://i.stack.imgur.com/fExQp.png)

これを実現するために以前質問した内容（[ウィンドウズフォームアプリケーションから対話型コンソールアプリケーションを操作するには？](https://ja.stackoverflow.com/questions/12663/%E3%82%A6%E3%82%A3%E3%83%B3%E3%83%89%E3%82%A6%E3%82%BA%E3%83%95%E3%82%A9%E3%83%BC%E3%83%A0%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%8B%E3%82%89%E5%AF%BE%E8%A9%B1%E5%9E%8B%E3%82%B3%E3%83%B3%E3%82%BD%E3%83%BC%E3%83%AB%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E6%93%8D%E4%BD%9C%E3%81%99%E3%82%8B%E3%81%AB%E3%81%AF)）を踏まえて作成してみたのですが上手く行きません。

主な処理概要として、

１．フォームをロードするタイミング（Loadイベント）で`System.Diagnostics.Process`で対象となるコンソールアプリケーション（mysql.exe）のプロセスを立ち上げ、プロセス自体はフォームの変数へ保持しておく。

２．送信ボタンを押すとフォームに保持してあるプロセスに対して`StandardInp‌​ut.WriteLine`と`StandardOutput.ReadLine`で入出力を行いその結果を結果領域に表示します。

この時`StandardInp‌​ut.WriteLine`でコマンドを送出した瞬間にプロセスが終了していまい、結果を受け取ることは可能なのですがその後の操作が継続して行う事が出来ません。

プロセスが終了しなければ良いと思い`WaitForExit`を試してみましたが同期待機なので画面がフリーズして使えませんでした。

非同期で待機が可能な`Process.Exited`も試しましたが`StandardInp‌​ut.WriteLine`処理でプロセスが終了してしまう事には変わりがありませんでした。

なんとかプロセスが終了せず継続してコンソールアプリケーションと対話を続ける方法はないものでしょうか？

なるべく.NETでの実現を望んでいますがインターフェイスだけ.NETであればいいので他の言語などでの実現方式もあればヒントを頂ければ嬉しいです。

前回@pgrho様に丁寧に教えて頂いたのですが当方の理解が足りず再度の質問となります。

皆様のお力添えを頂ければと思います。

なにとぞよろしくお願いします。

以下検証中のプログラムコードです。

[code]

    Imports System.Diagnostics
    Public Class Form1
    
        Private p As New Process
    
        Private Sub p_Exited(ByVal sender As Object, ByVal e As EventArgs)
            'プロセスが終了したときに実行される
            MessageBox.Show("終了しました。")
        End Sub
        Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
            '起動するファイルを指定する
            Dim command As String = "C:\xampp\mysql\bin\mysql.exe"
    
            p.StartInfo.FileName = command
            p.StartInfo.UseShellExecute = False
            p.StartInfo.RedirectStandardInput = True
            p.StartInfo.RedirectStandardOutput = True
            p.StartInfo.RedirectStandardError = True
            p.SynchronizingObject = Me
    
            AddHandler p.Exited, New EventHandler(AddressOf p_Exited)
            p.EnableRaisingEvents = True
            p.Start()
    
    
        End Sub
    
        Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
    
            p.StandardInput.WriteLine("show databases;" + "\r\n")
            Dim output As String = p.StandardOutput.Read()
            Dim errorStrings As String = p.StandardError.Read()
            MsgBox(output + errorStrings)
        End Sub
    End Class
    
[/code]

○プロセス終了  
[![画像の説明をここに入力](https://i.stack.imgur.com/t6l4v.png)](https://i.stack.imgur.com/t6l4v.png)


CC BY-SA 3.0
2015-07-26T02:19:30.070
12711
2015-07-27T16:44:50.610
2017-04-13T12:52:38.920
-1
10606
question
.net vb.net
ウィンドウズフォームアプリケーションから対話型コンソールアプリケーションを操作する為の実装について


`p.StandardOutput.Read`(および`ReadLine`)メソッドは実際に入力(外部プロセスから見れば出力)があるまで現在のスレッドをブロックしてしまうので、フォームアプリ等のメインスレッドで使用してはいけません。

非同期に読み込みを行うには`ReadAsync`メソッドを使ったり、別スレッドを起こして`Read`/`ReadLine`したりといった方法がありますが、手軽なのは`BeginOutputReadLine`を使ってイベントとして入力を受け取ることかと思います。

とりあえず下記コードで入出力はできることは確認しました。

それと、VB.NETにおいて`"\r\n"`は改行コードを意味しません。文字通り`\r\n`という文字列を書き込んでいるだけになってますのでそこも修正しています。

[code]

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        '起動するファイルを指定する
        Dim command As String = "C:\xampp\mysql\bin\mysql.exe"
    
        p.StartInfo.FileName = command
        p.StartInfo.UseShellExecute = False
        p.StartInfo.RedirectStandardInput = True
        p.StartInfo.RedirectStandardOutput = True
        p.StartInfo.RedirectStandardError = True
        p.SynchronizingObject = Me
    
        ' 標準出力、標準エラーを受け取るとテキストボックスに追加する。
        AddHandler Me.p.OutputDataReceived,
            Sub(s, dataReceivedEvent)
                Me.TextBox1.AppendText(dataReceivedEvent.Data & Environment.NewLine)
            End Sub
        AddHandler Me.p.ErrorDataReceived,
            Sub(s, dataReceivedEvent)
                Me.TextBox1.AppendText(dataReceivedEvent.Data & Environment.NewLine)
            End Sub
    
        p.Start()
        p.BeginOutputReadLine()
        p.BeginErrorReadLine()
    End Sub
    
    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        p.StandardInput.WriteLine("show databases;")
    End Sub
    
[/code]

* * *

一般的には上記のコードで他のプロセスと入出力ができます。  
質問では「例えばMySQLなど」となっていたので、一般的な回答としてはここまでです。

**MySQLについて**

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

とりあえずわかった範囲でmysqlの現象と対処法です。

  * 出力がバッファリングされる  
`--unbuffered`オプションをつけて起動すれば即フラッシュされるようになります。

  * テーブルなどの出力時に罫線フォーマットされない  
`--table`オプションをつければ罫線つきになります。

  * `\r`コマンドなどのエラー時にプロセスが終了してしまう？  
これは解決方法が見つかりませんでした。

ひとまず`p.Start()`の前に

[code]

    p.StartInfo.Arguments = "-uuser -ppass --unbuffered --table"
    
[/code]

のようにすればそれっぽく動きます。

参考：  
[Super User: Different output when
redirecting](https://superuser.com/questions/352490/different-output-when-
redirecting)  
[Stack Overflow: A bit curious about `mysql -e` output format with & without
redirect](https://stackoverflow.com/questions/27812190/a-bit-curious-about-
mysql-e-output-format-with-without-redirect)  
[GitHubGist: bashでパイプとかでつないでバッファされちゃう時](https://gist.github.com/riywo/874011)


CC BY-SA 3.0
2015-07-26T12:38:24.463
12720
2015-07-27T16:44:50.610
2017-05-23T12:38:55.307
-1
4765
12711
answer