none
パイプからの入力とコマンドライン引数からの入力とを判別して処理したいです RRS feed

  • 質問

  • C++/CLI のコンソールアプリケーションです。

    パイプからの入力をConsole::Inで受け取れる(どこかの段階で格納される)ようなので、

    ・パイプから起動したとき → Console::In → ReadLine()で文字列がある
       → ReadLine()で取得した文字列から新たに配列を作ってargsを再初期化する

    ・コマンドライン引数を付けて起動したとき → ReadLine()で文字列がない
       → argsはそのまま使用する

    と分けてmainの引数argsを加工してから先に進んでいるのですが、
    以下の感じでうまくいきません。

      // Cosole::Inはパイプからの入力があるときは、その値を格納し、
      // ReadLine()で、入力待ちする事なしにすぐ値をStringに代入できるが、
      // パイプからの入力が無い時は、コンソールからの入力待ちをしている?

    ●textReader->ReadLine() の結果、文字列の有無が解るので、仕分け自体はできるのだが、
    そのReadLine()で、文字列が無い時にConsole::ReadLine()のように入力待ち状態になってしまうので
    そこで停まってしまうということです。

    なにか、パイプ経由とコマンド引数経由(引数無しも含む)をノンストップで判別して
    加工に持ち込む方法はありませんでしょうか?

    よろしく、お願いいたします m(_ _)m

    // time /t | conMode.exe    OK
    // conMode.exe < test.txt  OK
    // conMode.exe                 NG (<●><●>1)
    // conMode.exe arg.txt      NG (<●><●>1)

    using namespace System;
    using namespace System::IO;
    using namespace System::Windows::Forms;
    
    [STAThreadAttribute]
    int main(array<String^>^ args)
    {
      MessageBox::Show("main入ってすぐのargsLength = " + args->Length.ToString());
    
      String^ inputLine = nullptr;
      if (Console::In != nullptr) {
        TextReader^ tIn = Console::In;
    
        inputLine = tIn->ReadLine(); //●ここで一時停止してしまう
    
        // ●適当なキーを押すと、それがコンソールに送られて
        // 先に進むことができる (<●><●>1)
    
        //Console::WriteLine();          // 試しにコンソールに送る(効果なし)
        //SendKeys::SendWait("{ENTER}"); // 試しにコンソールに送る(効果なし)
    
        tIn->Close();
      }
    
    
      // パイプ経由で入力を受け取れたら、argsを再初期化
      if (inputLine != nullptr && 0 < inputLine->Length) {
        delete args; args = nullptr;
        args = inputLine->Split(' ', 5); // パイプから最大5要素
      }
    
      if      (0 < args->Length) {
        MessageBox::Show(
          "行, argsLength, args[0] = " +
          __LINE__.ToString() + "行, " + args->Length.ToString() + ", " + args[0]
        );
      }
      else if (args->Length == 0) {
        MessageBox::Show(
          "行, argsLength, args[0] = " +
          __LINE__.ToString() + "行, " + args->Length.ToString() + ", 無し(゚・゚*"
        );
      }
    
      return 0;
    
    }




    • 編集済み koujichiu 2015年8月3日 10:18
    2015年8月3日 9:15

回答

  • .Ner Frameworkが4.5以降であれば、Console::IsInputRedirectedで判別できます。

    if (Console::IsInputRedirected)
    {
    	Console::WriteLine(L"Pipe");
    }
    else
    {
    	Console::WriteLine(L"Console");
    }

    4.5より前だとないので無理やりとってみる

    System::Type^ t = System::Console::typeid;
    
    System::Reflection::PropertyInfo^ pi = t->GetProperty(L"ConsoleInputHandle", System::Reflection::BindingFlags::Static | System::Reflection::BindingFlags::NonPublic);
    if (pi != nullptr)
    {
    	System::IntPtr^ p = (System::IntPtr^)pi->GetValue(nullptr, nullptr);
    	switch (GetFileType((HANDLE)p->ToPointer())) //#include "windows.h"
    	{
    	case FILE_TYPE_CHAR:
    		Console::WriteLine(L"Console");
    		break;
    	case FILE_TYPE_PIPE:
    		Console::WriteLine(L"Pipe");
    		break;
    	default:
    		Console::WriteLine(p);
    		break;
    	}
    }

    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答としてマーク koujichiu 2015年8月3日 12:09
    2015年8月3日 10:46

すべての返信

  • なにか、パイプ経由とコマンド引数経由(引数無しも含む)をノンストップで判別して
    加工に持ち込む方法はありませんでしょうか?
    ふつうは逆で、引数を確認し、指定がなかった場合にConsole::Inを参照します。
    2015年8月3日 10:44
  • .Ner Frameworkが4.5以降であれば、Console::IsInputRedirectedで判別できます。

    if (Console::IsInputRedirected)
    {
    	Console::WriteLine(L"Pipe");
    }
    else
    {
    	Console::WriteLine(L"Console");
    }

    4.5より前だとないので無理やりとってみる

    System::Type^ t = System::Console::typeid;
    
    System::Reflection::PropertyInfo^ pi = t->GetProperty(L"ConsoleInputHandle", System::Reflection::BindingFlags::Static | System::Reflection::BindingFlags::NonPublic);
    if (pi != nullptr)
    {
    	System::IntPtr^ p = (System::IntPtr^)pi->GetValue(nullptr, nullptr);
    	switch (GetFileType((HANDLE)p->ToPointer())) //#include "windows.h"
    	{
    	case FILE_TYPE_CHAR:
    		Console::WriteLine(L"Console");
    		break;
    	case FILE_TYPE_PIPE:
    		Console::WriteLine(L"Pipe");
    		break;
    	default:
    		Console::WriteLine(p);
    		break;
    	}
    }

    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答としてマーク koujichiu 2015年8月3日 12:09
    2015年8月3日 10:46
  • こんばんは、 ご回答ありがとうございました。

    古いOSでも動かせるように、win32にアクセスするタイプで、

    gekka様のコードを参考に、振り分けのコードを作ってみました所、

    無事動いてくれました。 m(_ _)m

    #include <Windows.h>                                // GetFileType
    
    using namespace System;
    
    array<String^>^ StdinToArgs() {
    	String^ inputLine = Console::In->ReadLine();
    	if (inputLine != nullptr && 0 < inputLine->Length) {
    		return inputLine->Split(' ');
    	}
    	else {
    		return gcnew array<String^>(2) { "err", "stdin2args" };
    	}
    }
    
    int main(array<String^>^ args)
    {
    	int argsLength = args->Length;
    
    	if (argsLength == 0) { // 単独起動かパイプ・リダイレクト経由
    
    		Type^ tID = Console::typeid;
    		Reflection::PropertyInfo^ pInfo = tID->GetProperty(
    			L"ConsoleInputHandle",
    			Reflection::BindingFlags::Static |
    			Reflection::BindingFlags::NonPublic
    			);
    
    		if (pInfo != nullptr)
    		{
    			IntPtr^ p = (IntPtr^)pInfo->GetValue(nullptr, nullptr);
    			switch (GetFileType((HANDLE)p->ToPointer())) //#include "windows.h"
    			{
    			case FILE_TYPE_UNKNOWN: // 0: エラーかも
    				break;
    			case FILE_TYPE_DISK:         // 1: app.exe < file.txt
    				args = StdinToArgs();
    				break;
    			case FILE_TYPE_CHAR:        // 2: app.exe, app.exe arg[0], ...
    				break;
    			case FILE_TYPE_PIPE:         // 3: app1.exe | app2.exe
    				args = StdinToArgs();
    				break;
    			}
    		}
    
    	} // if (argsLength == 0)
    
    	return 0;
    }





    • 編集済み koujichiu 2015年8月3日 13:03
    2015年8月3日 12:03