none
コマンドプロンプト用プログラムのGUIフロントエンドを作る際の標準入出力の取り扱い方について質問です。 RRS feed

  • 質問

  • GPGのフロントエンドを作りたいと思ってます。

    Processのヘルプを参考に下のプログラムのようにしてみましたが、行き詰まりました。
    すみませんが助言をお願いします。

    下のテストプログラムは、コマンドライン用のGPG.exeを使ったものです。
    GPGのヘルプのオプション(-help)では、TextBox2に出力できました。
    しかし、暗号化のための引数(-r name -es TextBox1.Text(暗号化対象のフルパス付きファイル))
    で実行しようとすると、コマンドプロンプトが立ち上がり、パスフレーズ入力待ちになります。
    コマンドプロンプトでパスフレーズを入力すれば、暗号化はできますが、GUIのフロントエンド
    を作ろうとしているので、パスフレーズは入力値を使いたいと思います。
    テストプログラムではプログラム中に埋め込んでます。
    myProcessStartInfo.CreateNoWindow = True
    とすると、コマンドプロンプトが出ない代わりに、バックグラウンドでパスフレーズ入力待ちになっているようで固まります。
    StandardInputが使えてなさそうです。

    このような場合、StandardInputとStandardOutputの使い方をどうすればよいでしょうか?
    パスフレーズを入力後、既に暗号化したファイルが存在していたら、上書きするかどうかを
    聞いてくるので、また入力待ちになったら、yを入力する処理というものも必要そうです。

    <テストプログラム>
        Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
            Dim TMP As String
            TMP = """-r name"" ""-es"" """ & TextBox1.Text & """"
            'TMP = " -help"
            Dim myProcess As New Process()
            Dim myProcessStartInfo As New ProcessStartInfo(GPG_PATH)
            myProcessStartInfo.Arguments = TMP
            myProcessStartInfo.UseShellExecute = False
            myProcessStartInfo.WindowStyle = ProcessWindowStyle.Minimized
            myProcessStartInfo.RedirectStandardOutput = True
            myProcessStartInfo.RedirectStandardInput = True
            myProcessStartInfo.UseShellExecute = False
            'myProcessStartInfo.CreateNoWindow = True '<---これを有効にするとコマンドプロンプトが出ない。
            myProcess.StartInfo = myProcessStartInfo
            myProcess.Start()
            Dim myStreamReader As StreamReader = myProcess.StandardOutput
            ' Read the standard output of the spawned process.
            Dim myString As String = myStreamReader.ReadToEnd
            Dim myInputStrm As StreamWriter = myProcess.StandardInput
            myInputStrm.WriteLine("パスフレーズ")
            myProcess.Close()
            TextBox2.Text = myString
        End Sub

    コマンドプロンプトを表示すると以下のような文字が画面に出ます。
    -------------------------------
    次のユーザーの秘密鍵のロックを解除するには
    パスフレーズがいります:“name <e-mailaddress>”
    1024ビットDSA鍵, ID XXXXXXXX作成日付は200X-XX-XX

    パスフレーズを入力:<---ここで入力待ち
    -------------------------------

    2009年3月23日 21:54

回答

  • StandardInputが使えてないわけではなく、プログラムがそこまで進んでいません。
    「Dim myString As String = myStreamReader.ReadToEnd」はStreamの終端まで読みます。StandardOutputの終端とは一般的に子プロセスの終了まで待つことを指します。
    (例外的に、子プロセスが明示的にstdoutを閉じることでも終端に達しますが、そのようなプログラムはほとんどありません。)
    そして子プロセスGPGはstdinに入力があるまで待つ、典型的なデッドロックです。

    ちなみにProcess.StandardOutputプロパティにはその可能性を指摘する記述があります。
    同期読み取り操作により、StandardOutput ストリームから読み取る呼び出し元と、そのストリームに書き込む子プロセスの間に依存関係が発生します。この依存関係によってデッドロック状態が発生する場合があります。~
    OutputDataReceivedイベントを使うことになるでしょう。サンプルも記載されています。
    ReadToEnd()を非同期呼び出しするのもありなのかなぁ…? ちょっとわかりません。
    2009年3月24日 13:12
  • パスワード系の入力は、入力を隠す必要があることから、標準入力をそのまま読まず、
    コンソールAPIなどを使用しているのではないでしょうか。
    GPGのコマンドライン引数に、「パスフレーズを標準入力から読む」ようなものがないか探してみては如何でしょうか。
    たとえばGnuPGでは、--batchや --passphrase-fdなどという引数があります。


    jzkey
    2009年3月27日 9:04

すべての返信

  • StandardInputが使えてないわけではなく、プログラムがそこまで進んでいません。
    「Dim myString As String = myStreamReader.ReadToEnd」はStreamの終端まで読みます。StandardOutputの終端とは一般的に子プロセスの終了まで待つことを指します。
    (例外的に、子プロセスが明示的にstdoutを閉じることでも終端に達しますが、そのようなプログラムはほとんどありません。)
    そして子プロセスGPGはstdinに入力があるまで待つ、典型的なデッドロックです。

    ちなみにProcess.StandardOutputプロパティにはその可能性を指摘する記述があります。
    同期読み取り操作により、StandardOutput ストリームから読み取る呼び出し元と、そのストリームに書き込む子プロセスの間に依存関係が発生します。この依存関係によってデッドロック状態が発生する場合があります。~
    OutputDataReceivedイベントを使うことになるでしょう。サンプルも記載されています。
    ReadToEnd()を非同期呼び出しするのもありなのかなぁ…? ちょっとわかりません。
    2009年3月24日 13:12
  • 助言ありがとうございます。

    > 「Dim myString As String = myStreamReader.ReadToEnd」はStreamの終端まで読みます。
    > StandardOutputの終端とは一般的に子プロセスの終了まで待つことを指します。

    私もそうかと思ったので、一度「Dim myStreamReader As StreamReader = myProcess.StandardOutput」とそれに関係した部分をコメントかしてみたのですが、それでもパスフレーズの自動入力ができなかったので、困り果てて質問しました。

    OutputDataReceivedイベントのリンク先を見ましたが、どうやらプロセス間通信ということでそちらのプログラミング知識が必要そうですね。
    原理はなんとなくわかるつもりでもプログラミングの仕方はまだわかってないので、サンプルを実行してみようとしましたが、同ページのサンプルはそのままでは実行できなさそうなので、実行できずでまた行き詰ってしまいました。
    スレッド、プロセス関係のプログラミングの勉強してからGUIフロントエンドを検討しなおした方がよさそう、という自分なりの結論になりました。

    2009年3月25日 15:10
  • パスワード系の入力は、入力を隠す必要があることから、標準入力をそのまま読まず、
    コンソールAPIなどを使用しているのではないでしょうか。
    GPGのコマンドライン引数に、「パスフレーズを標準入力から読む」ようなものがないか探してみては如何でしょうか。
    たとえばGnuPGでは、--batchや --passphrase-fdなどという引数があります。


    jzkey
    2009年3月27日 9:04
  • jzkeyさんありがとうございます。

    -helpで出てこなかったオプションですが、--batchや--passphrase-fdも使えました。
    Linuxのgpgとほぼ同等のオプションが使えそうです。
    --passphraseもありましたので、標準入力を使ってパスフレーズを入力する必要なさそうです。

    まだ、標準入出力を使いこなせて無い感は否めませんが、--batchや--passphraseなどで大部分対処できそうです。

    2009年3月30日 14:49