コンソール プロセスを生成して標準ハンドルをリダイレクトする方法

文書翻訳 文書翻訳
文書番号: 190351 - 対象製品
すべて展開する | すべて折りたたむ

目次

概要

この資料では、標準入力ハンドルからの入力を受信する、または標準出力ハンドルに出力を送信する子プロセスの入出力をリダイレクトする方法について説明します。Win32 API を使用することにより、アプリケーションでコンソールの子プロセスを生成し、標準ハンドルをリダイレクトすることができます。この機能により、親プロセスは子プロセスとの間で入出力の送受信を行うことができます。

: コンソール ベースのアプリケーションには、入出力 (IO) 処理に標準ハンドルを使用しないものもあります。Win32 API では、このようなプロセスのリダイレクトはサポートされません。

詳細

STARTUPINFO 構造体を指定して CreateProcess() API を使用すると、コンソール ベースの子プロセスの標準ハンドルをリダイレクトすることができます。dwFlags メンバに STARTF_USESTDHANDLES を設定した場合は、STARTUPINFO の以下のメンバで、コンソール ベースの子プロセスの標準ハンドルを指定します。
   HANDLE hStdInput - 子プロセスの標準入力ハンドル。
   HANDLE hStdOutput - 子プロセスの標準出力ハンドル。
   HANDLE hStdError - 子プロセスの標準エラー ハンドル。
				
これらのハンドルには、パイプ ハンドル、ファイル ハンドル、または、ReadFile() および WriteFile() API を使用して同期読み取りおよび同期書き込みを実行できる任意のハンドルを設定できます。これらのハンドルは継承可能である必要があり、また、CreateProcess() API で bInheritHandles パラメータに TRUE を指定して、継承可能なハンドルが子プロセスに継承されるようにする必要があります。親プロセスからリダイレクトする標準ハンドルが 1 つないし 2 つのみの場合は、特定のハンドルに対して GetStdHandle() を指定すると、子プロセスで、リダイレクトしない場合と同様に標準ハンドルが生成されます。たとえば、親プロセスで子プロセスの標準出力と標準エラーのみをリダイレクトする必要がある場合は、STARTUPINFO 構造体の hStdInput メンバに次のように設定します。
   hStdInput = GetStdHandle(STD_INPUT_HANDLE);
				
: printf() や fprintf() などの C ランタイム関数を使用する子プロセスでは、リダイレクトを行うと処理速度が低下する場合があります。C ランタイム関数では、独立した入出力バッファが使用されます。リダイレクトを行うと、各 IO 呼び出しの後、これらのバッファがすぐにはフラッシュされない場合があります。その結果、printf() 呼び出しのリダイレクト パイプへの出力、または getch() 呼び出しからの入力がすぐにはフラッシュされず、遅延が発生し、それがときには長時間にわたります。この問題は、子プロセスで C ランタイム IO 関数を呼び出した後に毎回、入出力バッファをフラッシュすることにより、回避できます。C ランタイムの入出力バッファをフラッシュできるのは、そのバッファを使用する子プロセスのみです。プロセスで fflush() 関数を呼び出すことにより、そのプロセスの C ランタイム入出力バッファをフラッシュできます。

: Windows 95 および Windows 98 では、特定の子プロセスの標準ハンドルをリダイレクトするときに、さらに必要な手順があります。

次のサンプル コードでは、CreateProcess 呼び出しで指定された子プロセスの標準入力、標準出力、および標準エラーがリダイレクトされます。このサンプルでは、指定されたコンソール プロセス (Child.c) をリダイレクトします。

サンプル コード

   /*++

      Copyright (c) 1998  Microsoft Corporation

      Module Name:

         Redirect.c

      Description:
          This sample illustrates how to spawn a child console based
          application with redirected standard handles.

          The following import libraries are required:
          user32.lib

      Dave McPherson (davemm)   11-March-98

   --*/ 

   #include<windows.h>
   #pragma comment(lib, "User32.lib")
   void DisplayError(char *pszAPI);
   void ReadAndHandleOutput(HANDLE hPipeRead);
   void PrepAndLaunchRedirectedChild(HANDLE hChildStdOut,
                                     HANDLE hChildStdIn,
                                     HANDLE hChildStdErr);
   DWORD WINAPI GetAndSendInputThread(LPVOID lpvThreadParam);

   HANDLE hChildProcess = NULL;
   HANDLE hStdIn = NULL; // Handle to parents std input.
   BOOL bRunThread = TRUE;


   void main ()
   {
      HANDLE hOutputReadTmp,hOutputRead,hOutputWrite;
      HANDLE hInputWriteTmp,hInputRead,hInputWrite;
      HANDLE hErrorWrite;
      HANDLE hThread;
      DWORD ThreadId;
      SECURITY_ATTRIBUTES sa;


      // Set up the security attributes struct.
      sa.nLength= sizeof(SECURITY_ATTRIBUTES);
      sa.lpSecurityDescriptor = NULL;
      sa.bInheritHandle = TRUE;


      // Create the child output pipe.
      if (!CreatePipe(&hOutputReadTmp,&hOutputWrite,&sa,0))
         DisplayError("CreatePipe");


      // Create a duplicate of the output write handle for the std error
      // write handle. This is necessary in case the child application
      // closes one of its std output handles.
      if (!DuplicateHandle(GetCurrentProcess(),hOutputWrite,
                           GetCurrentProcess(),&hErrorWrite,0,
                           TRUE,DUPLICATE_SAME_ACCESS))
         DisplayError("DuplicateHandle");


      // Create the child input pipe.
      if (!CreatePipe(&hInputRead,&hInputWriteTmp,&sa,0))
         DisplayError("CreatePipe");


      // Create new output read handle and the input write handles. Set
      // the Properties to FALSE. Otherwise, the child inherits the
      // properties and, as a result, non-closeable handles to the pipes
      // are created.
      if (!DuplicateHandle(GetCurrentProcess(),hOutputReadTmp,
                           GetCurrentProcess(),
                           &hOutputRead, // Address of new handle.
                           0,FALSE, // Make it uninheritable.
                           DUPLICATE_SAME_ACCESS))
         DisplayError("DupliateHandle");

      if (!DuplicateHandle(GetCurrentProcess(),hInputWriteTmp,
                           GetCurrentProcess(),
                           &hInputWrite, // Address of new handle.
                           0,FALSE, // Make it uninheritable.
                           DUPLICATE_SAME_ACCESS))
      DisplayError("DupliateHandle");


      // Close inheritable copies of the handles you do not want to be
      // inherited.
      if (!CloseHandle(hOutputReadTmp)) DisplayError("CloseHandle");
      if (!CloseHandle(hInputWriteTmp)) DisplayError("CloseHandle");


      // Get std input handle so you can close it and force the ReadFile to
      // fail when you want the input thread to exit.
      if ( (hStdIn = GetStdHandle(STD_INPUT_HANDLE)) ==
                                                INVALID_HANDLE_VALUE )
         DisplayError("GetStdHandle");

      PrepAndLaunchRedirectedChild(hOutputWrite,hInputRead,hErrorWrite);


      // Close pipe handles (do not continue to modify the parent).
      // You need to make sure that no handles to the write end of the
      // output pipe are maintained in this process or else the pipe will
      // not close when the child process exits and the ReadFile will hang.
      if (!CloseHandle(hOutputWrite)) DisplayError("CloseHandle");
      if (!CloseHandle(hInputRead )) DisplayError("CloseHandle");
      if (!CloseHandle(hErrorWrite)) DisplayError("CloseHandle");


      // Launch the thread that gets the input and sends it to the child.
      hThread = CreateThread(NULL,0,GetAndSendInputThread,
                              (LPVOID)hInputWrite,0,&ThreadId);
      if (hThread == NULL) DisplayError("CreateThread");


      // Read the child's output.
      ReadAndHandleOutput(hOutputRead);
      // Redirection is complete


      // Force the read on the input to return by closing the stdin handle.
      if (!CloseHandle(hStdIn)) DisplayError("CloseHandle");


      // Tell the thread to exit and wait for thread to die.
      bRunThread = FALSE;

      if (WaitForSingleObject(hThread,INFINITE) == WAIT_FAILED)
         DisplayError("WaitForSingleObject");

      if (!CloseHandle(hOutputRead)) DisplayError("CloseHandle");
      if (!CloseHandle(hInputWrite)) DisplayError("CloseHandle");
   }


   /////////////////////////////////////////////////////////////////////// 
   // PrepAndLaunchRedirectedChild
   // Sets up STARTUPINFO structure, and launches redirected child.
   /////////////////////////////////////////////////////////////////////// 
   void PrepAndLaunchRedirectedChild(HANDLE hChildStdOut,
                                     HANDLE hChildStdIn,
                                     HANDLE hChildStdErr)
   {
      PROCESS_INFORMATION pi;
      STARTUPINFO si;

      // Set up the start up info struct.
      ZeroMemory(&si,sizeof(STARTUPINFO));
      si.cb = sizeof(STARTUPINFO);
      si.dwFlags = STARTF_USESTDHANDLES;
      si.hStdOutput = hChildStdOut;
      si.hStdInput  = hChildStdIn;
      si.hStdError  = hChildStdErr;
      // Use this if you want to hide the child:
      //     si.wShowWindow = SW_HIDE;
      // Note that dwFlags must include STARTF_USESHOWWINDOW if you want to
      // use the wShowWindow flags.


      // Launch the process that you want to redirect (in this case,
      // Child.exe). Make sure Child.exe is in the same directory as
      // redirect.c launch redirect from a command line to prevent location
      // confusion.
      if (!CreateProcess(NULL,"Child.EXE",NULL,NULL,TRUE,
                         CREATE_NEW_CONSOLE,NULL,NULL,&si,&pi))
         DisplayError("CreateProcess");


      // Set global child process handle to cause threads to exit.
      hChildProcess = pi.hProcess;


      // Close any unnecessary handles.
      if (!CloseHandle(pi.hThread)) DisplayError("CloseHandle");
   }


   /////////////////////////////////////////////////////////////////////// 
   // ReadAndHandleOutput
   // Monitors handle for input. Exits when child exits or pipe breaks.
   /////////////////////////////////////////////////////////////////////// 
   void ReadAndHandleOutput(HANDLE hPipeRead)
   {
      CHAR lpBuffer[256];
      DWORD nBytesRead;
      DWORD nCharsWritten;

      while(TRUE)
      {
         if (!ReadFile(hPipeRead,lpBuffer,sizeof(lpBuffer),
                                          &nBytesRead,NULL) || !nBytesRead)
         {
            if (GetLastError() == ERROR_BROKEN_PIPE)
               break; // pipe done - normal exit path.
            else
               DisplayError("ReadFile"); // Something bad happened.
         }

         // Display the character read on the screen.
         if (!WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE),lpBuffer,
                           nBytesRead,&nCharsWritten,NULL))
            DisplayError("WriteConsole");
      }
   }


   /////////////////////////////////////////////////////////////////////// 
   // GetAndSendInputThread
   // Thread procedure that monitors the console for input and sends input
   // to the child process through the input pipe.
   // This thread ends when the child application exits.
   /////////////////////////////////////////////////////////////////////// 
   DWORD WINAPI GetAndSendInputThread(LPVOID lpvThreadParam)
   {
      CHAR read_buff[256];
      DWORD nBytesRead,nBytesWrote;
      HANDLE hPipeWrite = (HANDLE)lpvThreadParam;

      // Get input from our console and send it to child through the pipe.
      while (bRunThread)
      {
         if(!ReadConsole(hStdIn,read_buff,1,&nBytesRead,NULL))
            DisplayError("ReadConsole");

         read_buff[nBytesRead] = '\0'; // Follow input with a NULL.

         if (!WriteFile(hPipeWrite,read_buff,nBytesRead,&nBytesWrote,NULL))
         {
            if (GetLastError() == ERROR_NO_DATA)
               break; // Pipe was closed (normal exit path).
            else
            DisplayError("WriteFile");
         }
      }

      return 1;
   }


   /////////////////////////////////////////////////////////////////////// 
   // DisplayError
   // Displays the error number and corresponding message.
   /////////////////////////////////////////////////////////////////////// 
   void DisplayError(char *pszAPI)
   {
       LPVOID lpvMessageBuffer;
       CHAR szPrintBuffer[512];
       DWORD nCharsWritten;

       FormatMessage(
                FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
                NULL, GetLastError(),
                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                (LPTSTR)&lpvMessageBuffer, 0, NULL);

       wsprintf(szPrintBuffer,
         "ERROR: API    = %s.\n   error code = %d.\n   message    = %s.\n",
                pszAPI, GetLastError(), (char *)lpvMessageBuffer);

       WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE),szPrintBuffer,
                     lstrlen(szPrintBuffer),&nCharsWritten,NULL);

       LocalFree(lpvMessageBuffer);
       ExitProcess(GetLastError());
   }

   ////////////////////////////////////////////////////////////////////// 
   // child.c
   // Echoes all input to stdout. This will be redirected by the redirect
   // sample. Compile and build child.c as a Win32 Console application and
   // put it in the same directory as the redirect sample.
   // 
   #include<windows.h>
   #include<stdio.h>
   #include<string.h>

   void main ()
   {
      FILE*    fp;
      CHAR     szInput[1024];


      // Open the console. By doing this, you can send output directly to
      // the console that will not be redirected.

      fp = fopen("CON", "w");
      if (!fp) {
         printf("Error opening child console - perhaps there is none.\n");
         fflush(NULL);
      }
      else
      {

      // Write a message direct to the console (will not be redirected).

         fprintf(fp,"This data is being printed directly to the\n");
         fprintf(fp,"console and will not be redirected.\n\n");
         fprintf(fp,"Since the standard input and output have been\n");
         fprintf(fp,"redirected data sent to and from those handles\n");
         fprintf(fp,"will be redirected.\n\n");
         fprintf(fp,"To send data to the std input of this process.\n");
         fprintf(fp,"Click on the console window of the parent process\n");
         fprintf(fp,"(redirect), and enter data from it's console\n\n");
         fprintf(fp,"To exit this process send the string 'exit' to\n");
         fprintf(fp,"it's standard input\n");
         fflush(fp);
      }

      ZeroMemory(szInput,1024);
      while (TRUE)
      {
         gets(szInput);
         printf("Child echoing [%s]\n",szInput);
         fflush(NULL);  // Must flush output buffers or else redirection
                        // will be problematic.
         if (!_stricmp(szInput,"Exit") )
            break;

         ZeroMemory(szInput,strlen(szInput) );

      }
   }

関連情報

MSDN ライブラリ SDK ドキュメント : CreateProcess(); STARTUPINFO 構造体

以下のフォルダにある Win32 Platform SDK のサンプルを継承します。
   \MSSDK\samples\winbase\ipc\inherit
				

プロパティ

文書番号: 190351 - 最終更新日: 2006年3月1日 - リビジョン: 7.1
この資料は以下の製品について記述したものです。
  • Microsoft Win32 Application Programming Interface?を以下の環境でお使いの場合
    • Microsoft Windows NT 3.51 Service Pack 5
    • Microsoft Windows NT 4.0
    • Microsoft Windows 2000 Standard Edition
    • Microsoft Windows XP Professional
キーワード:?
kbhowto kbapi kbconsole kbkernbase kbipc kbfaq KB190351
"Microsoft Knowledge Baseに含まれている情報は、いかなる保証もない現状ベースで提供されるものです。Microsoft Corporation及びその関連会社は、市場性および特定の目的への適合性を含めて、明示的にも黙示的にも、一切の保証をいたしません。さらに、Microsoft Corporation及びその関連会社は、本文書に含まれている情報の使用及び使用結果につき、正確性、真実性等、いかなる表明・保証も行ないません。Microsoft Corporation、その関連会社及びこれらの権限ある代理人による口頭または書面による一切の情報提供またはアドバイスは、保証を意味するものではなく、かつ上記免責条項の範囲を狭めるものではありません。Microsoft Corporation、その関連会社 及びこれらの者の供給者は、直接的、間接的、偶発的、結果的損害、逸失利益、懲罰的損害、または特別損害を含む全ての損害に対して、状況のいかんを問わず一切責任を負いません。(Microsoft Corporation、その関連会社 またはこれらの者の供給者がかかる損害の発生可能性を了知している場合を含みます。) 結果的損害または偶発的損害に対する責任の免除または制限を認めていない地域においては、上記制限が適用されない場合があります。なお、本文書においては、文書の体裁上の都合により製品名の表記において商標登録表示、その他の商標表示を省略している場合がありますので、予めご了解ください。"

フィードバック

 

Contact us for more help

Contact us for more help
Connect with Answer Desk for expert help.
Get more support from smallbusiness.support.microsoft.com