红山玉龙

导航

统计

[转]子进程继承父控制台

原文链接:https://support.microsoft.com/en-us/kb/190351

 

 

How to spawn console processes with redirected standard handles

 

SUMMARY
This article describes how to redirect the input and output of a child process that receives input from the standard input handle or sends output to the standard output handle. The Win32 API enables applications to spawn a child console process with redirected standard handles. This feature allows a parent process to send and receive the input and output of the child process. 

Note Some console based applications do not use the standard handles for their input/output (IO) operations. The Win32 API does not support redirection of these processes.
MORE INFORMATION
The CreateProcess() API through the STARTUPINFO structure enables you to redirect the standard handles of a child console based process. If the dwFlags member is set to STARTF_USESTDHANDLES, then the following STARTUPINFO members specify the standard handles of the child console based process:
   HANDLE hStdInput - Standard input handle of the child process.
   HANDLE hStdOutput - Standard output handle of the child process.
   HANDLE hStdError - Standard error handle of the child process.
				
You can set these handles to either a pipe handle, file handle, or any handle that can do synchronous reads and writes through the ReadFile() and WriteFile() API. The handles must be inheritable and the CreateProcess() API must specify that inheritable handles are to be inherited by the child process by specifying TRUE in the bInheritHandles parameter. If the parent process only wishes to redirect one or two standard handles, specifying GetStdHandle() for the specific handles causes the child to create the standard handle as it normally would without redirection. For example, if the parent process only needs to redirect the standard output and error of the child process, then the hStdInput member of the STARTUPINFO structure is filled as follows:
   hStdInput = GetStdHandle(STD_INPUT_HANDLE);
				
Note Child processes that use such C run-time functions as printf() and fprintf() can behave poorly when redirected. The C run-time functions maintain separate IO buffers. When redirected, these buffers might not be flushed immediately after each IO call. As a result, the output to the redirection pipe of a printf() call or the input from a getch() call is not flushed immediately and delays, sometimes-infinite delays occur. This problem is avoided if the child process flushes the IO buffers after each call to a C run-time IO function. Only the child process can flush its C run-time IO buffers. A process can flush its C run-time IO buffers by calling the fflush() function.

Note Windows 95 and Windows 98 require an extra step when you redirect the standard handles of certain child processes.

The following sample redirects the standard input, output, and error of the child process specified in the CreateProcess call. This sample redirects the provided console process (Child.c).

Sample code

  1 /*++
  2 
  3       Copyright (c) 1998  Microsoft Corporation
  4 
  5       Module Name:
  6 
  7          Redirect.c
  8 
  9       Description:
 10           This sample illustrates how to spawn a child console based
 11           application with redirected standard handles.
 12 
 13           The following import libraries are required:
 14           user32.lib
 15 
 16       Dave McPherson (davemm)   11-March-98
 17 
 18    --*/ 
 19 
 20    #include<windows.h>
 21    #pragma comment(lib, "User32.lib")
 22    void DisplayError(char *pszAPI);
 23    void ReadAndHandleOutput(HANDLE hPipeRead);
 24    void PrepAndLaunchRedirectedChild(HANDLE hChildStdOut,
 25                                      HANDLE hChildStdIn,
 26                                      HANDLE hChildStdErr);
 27    DWORD WINAPI GetAndSendInputThread(LPVOID lpvThreadParam);
 28 
 29    HANDLE hChildProcess = NULL;
 30    HANDLE hStdIn = NULL; // Handle to parents std input.
 31    BOOL bRunThread = TRUE;
 32 
 33 
 34    void main ()
 35    {
 36       HANDLE hOutputReadTmp,hOutputRead,hOutputWrite;
 37       HANDLE hInputWriteTmp,hInputRead,hInputWrite;
 38       HANDLE hErrorWrite;
 39       HANDLE hThread;
 40       DWORD ThreadId;
 41       SECURITY_ATTRIBUTES sa;
 42 
 43 
 44       // Set up the security attributes struct.
 45       sa.nLength= sizeof(SECURITY_ATTRIBUTES);
 46       sa.lpSecurityDescriptor = NULL;
 47       sa.bInheritHandle = TRUE;
 48 
 49 
 50       // Create the child output pipe.
 51       if (!CreatePipe(&hOutputReadTmp,&hOutputWrite,&sa,0))
 52          DisplayError("CreatePipe");
 53 
 54 
 55       // Create a duplicate of the output write handle for the std error
 56       // write handle. This is necessary in case the child application
 57       // closes one of its std output handles.
 58       if (!DuplicateHandle(GetCurrentProcess(),hOutputWrite,
 59                            GetCurrentProcess(),&hErrorWrite,0,
 60                            TRUE,DUPLICATE_SAME_ACCESS))
 61          DisplayError("DuplicateHandle");
 62 
 63 
 64       // Create the child input pipe.
 65       if (!CreatePipe(&hInputRead,&hInputWriteTmp,&sa,0))
 66          DisplayError("CreatePipe");
 67 
 68 
 69       // Create new output read handle and the input write handles. Set
 70       // the Properties to FALSE. Otherwise, the child inherits the
 71       // properties and, as a result, non-closeable handles to the pipes
 72       // are created.
 73       if (!DuplicateHandle(GetCurrentProcess(),hOutputReadTmp,
 74                            GetCurrentProcess(),
 75                            &hOutputRead, // Address of new handle.
 76                            0,FALSE, // Make it uninheritable.
 77                            DUPLICATE_SAME_ACCESS))
 78          DisplayError("DupliateHandle");
 79 
 80       if (!DuplicateHandle(GetCurrentProcess(),hInputWriteTmp,
 81                            GetCurrentProcess(),
 82                            &hInputWrite, // Address of new handle.
 83                            0,FALSE, // Make it uninheritable.
 84                            DUPLICATE_SAME_ACCESS))
 85       DisplayError("DupliateHandle");
 86 
 87 
 88       // Close inheritable copies of the handles you do not want to be
 89       // inherited.
 90       if (!CloseHandle(hOutputReadTmp)) DisplayError("CloseHandle");
 91       if (!CloseHandle(hInputWriteTmp)) DisplayError("CloseHandle");
 92 
 93 
 94       // Get std input handle so you can close it and force the ReadFile to
 95       // fail when you want the input thread to exit.
 96       if ( (hStdIn = GetStdHandle(STD_INPUT_HANDLE)) ==
 97                                                 INVALID_HANDLE_VALUE )
 98          DisplayError("GetStdHandle");
 99 
100       PrepAndLaunchRedirectedChild(hOutputWrite,hInputRead,hErrorWrite);
101 
102 
103       // Close pipe handles (do not continue to modify the parent).
104       // You need to make sure that no handles to the write end of the
105       // output pipe are maintained in this process or else the pipe will
106       // not close when the child process exits and the ReadFile will hang.
107       if (!CloseHandle(hOutputWrite)) DisplayError("CloseHandle");
108       if (!CloseHandle(hInputRead )) DisplayError("CloseHandle");
109       if (!CloseHandle(hErrorWrite)) DisplayError("CloseHandle");
110 
111 
112       // Launch the thread that gets the input and sends it to the child.
113       hThread = CreateThread(NULL,0,GetAndSendInputThread,
114                               (LPVOID)hInputWrite,0,&ThreadId);
115       if (hThread == NULL) DisplayError("CreateThread");
116 
117 
118       // Read the child's output.
119       ReadAndHandleOutput(hOutputRead);
120       // Redirection is complete
121 
122 
123       // Force the read on the input to return by closing the stdin handle.
124       if (!CloseHandle(hStdIn)) DisplayError("CloseHandle");
125 
126 
127       // Tell the thread to exit and wait for thread to die.
128       bRunThread = FALSE;
129 
130       if (WaitForSingleObject(hThread,INFINITE) == WAIT_FAILED)
131          DisplayError("WaitForSingleObject");
132 
133       if (!CloseHandle(hOutputRead)) DisplayError("CloseHandle");
134       if (!CloseHandle(hInputWrite)) DisplayError("CloseHandle");
135    }
136 
137 
138    /////////////////////////////////////////////////////////////////////// 
139    // PrepAndLaunchRedirectedChild
140    // Sets up STARTUPINFO structure, and launches redirected child.
141    /////////////////////////////////////////////////////////////////////// 
142    void PrepAndLaunchRedirectedChild(HANDLE hChildStdOut,
143                                      HANDLE hChildStdIn,
144                                      HANDLE hChildStdErr)
145    {
146       PROCESS_INFORMATION pi;
147       STARTUPINFO si;
148 
149       // Set up the start up info struct.
150       ZeroMemory(&si,sizeof(STARTUPINFO));
151       si.cb = sizeof(STARTUPINFO);
152       si.dwFlags = STARTF_USESTDHANDLES;
153       si.hStdOutput = hChildStdOut;
154       si.hStdInput  = hChildStdIn;
155       si.hStdError  = hChildStdErr;
156       // Use this if you want to hide the child:
157       //     si.wShowWindow = SW_HIDE;
158       // Note that dwFlags must include STARTF_USESHOWWINDOW if you want to
159       // use the wShowWindow flags.
160 
161 
162       // Launch the process that you want to redirect (in this case,
163       // Child.exe). Make sure Child.exe is in the same directory as
164       // redirect.c launch redirect from a command line to prevent location
165       // confusion.
166       if (!CreateProcess(NULL,"Child.EXE",NULL,NULL,TRUE,
167                          CREATE_NEW_CONSOLE,NULL,NULL,&si,&pi))
168          DisplayError("CreateProcess");
169 
170 
171       // Set global child process handle to cause threads to exit.
172       hChildProcess = pi.hProcess;
173 
174 
175       // Close any unnecessary handles.
176       if (!CloseHandle(pi.hThread)) DisplayError("CloseHandle");
177    }
178 
179 
180    /////////////////////////////////////////////////////////////////////// 
181    // ReadAndHandleOutput
182    // Monitors handle for input. Exits when child exits or pipe breaks.
183    /////////////////////////////////////////////////////////////////////// 
184    void ReadAndHandleOutput(HANDLE hPipeRead)
185    {
186       CHAR lpBuffer[256];
187       DWORD nBytesRead;
188       DWORD nCharsWritten;
189 
190       while(TRUE)
191       {
192          if (!ReadFile(hPipeRead,lpBuffer,sizeof(lpBuffer),
193                                           &nBytesRead,NULL) || !nBytesRead)
194          {
195             if (GetLastError() == ERROR_BROKEN_PIPE)
196                break; // pipe done - normal exit path.
197             else
198                DisplayError("ReadFile"); // Something bad happened.
199          }
200 
201          // Display the character read on the screen.
202          if (!WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE),lpBuffer,
203                            nBytesRead,&nCharsWritten,NULL))
204             DisplayError("WriteConsole");
205       }
206    }
207 
208 
209    /////////////////////////////////////////////////////////////////////// 
210    // GetAndSendInputThread
211    // Thread procedure that monitors the console for input and sends input
212    // to the child process through the input pipe.
213    // This thread ends when the child application exits.
214    /////////////////////////////////////////////////////////////////////// 
215    DWORD WINAPI GetAndSendInputThread(LPVOID lpvThreadParam)
216    {
217       CHAR read_buff[256];
218       DWORD nBytesRead,nBytesWrote;
219       HANDLE hPipeWrite = (HANDLE)lpvThreadParam;
220 
221       // Get input from our console and send it to child through the pipe.
222       while (bRunThread)
223       {
224          if(!ReadConsole(hStdIn,read_buff,1,&nBytesRead,NULL))
225             DisplayError("ReadConsole");
226 
227          read_buff[nBytesRead] = '\0'; // Follow input with a NULL.
228 
229          if (!WriteFile(hPipeWrite,read_buff,nBytesRead,&nBytesWrote,NULL))
230          {
231             if (GetLastError() == ERROR_NO_DATA)
232                break; // Pipe was closed (normal exit path).
233             else
234             DisplayError("WriteFile");
235          }
236       }
237 
238       return 1;
239    }
240 
241 
242    /////////////////////////////////////////////////////////////////////// 
243    // DisplayError
244    // Displays the error number and corresponding message.
245    /////////////////////////////////////////////////////////////////////// 
246    void DisplayError(char *pszAPI)
247    {
248        LPVOID lpvMessageBuffer;
249        CHAR szPrintBuffer[512];
250        DWORD nCharsWritten;
251 
252        FormatMessage(
253                 FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
254                 NULL, GetLastError(),
255                 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
256                 (LPTSTR)&lpvMessageBuffer, 0, NULL);
257 
258        wsprintf(szPrintBuffer,
259          "ERROR: API    = %s.\n   error code = %d.\n   message    = %s.\n",
260                 pszAPI, GetLastError(), (char *)lpvMessageBuffer);
261 
262        WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE),szPrintBuffer,
263                      lstrlen(szPrintBuffer),&nCharsWritten,NULL);
264 
265        LocalFree(lpvMessageBuffer);
266        ExitProcess(GetLastError());
267    }
268 
269    ////////////////////////////////////////////////////////////////////// 
270    // child.c
271    // Echoes all input to stdout. This will be redirected by the redirect
272    // sample. Compile and build child.c as a Win32 Console application and
273    // put it in the same directory as the redirect sample.
274    // 
275    #include<windows.h>
276    #include<stdio.h>
277    #include<string.h>
278 
279    void main ()
280    {
281       FILE*    fp;
282       CHAR     szInput[1024];
283 
284 
285       // Open the console. By doing this, you can send output directly to
286       // the console that will not be redirected.
287 
288       fp = fopen("CON", "w");
289       if (!fp) {
290          printf("Error opening child console - perhaps there is none.\n");
291          fflush(NULL);
292       }
293       else
294       {
295 
296       // Write a message direct to the console (will not be redirected).
297 
298          fprintf(fp,"This data is being printed directly to the\n");
299          fprintf(fp,"console and will not be redirected.\n\n");
300          fprintf(fp,"Since the standard input and output have been\n");
301          fprintf(fp,"redirected data sent to and from those handles\n");
302          fprintf(fp,"will be redirected.\n\n");
303          fprintf(fp,"To send data to the std input of this process.\n");
304          fprintf(fp,"Click on the console window of the parent process\n");
305          fprintf(fp,"(redirect), and enter data from it's console\n\n");
306          fprintf(fp,"To exit this process send the string 'exit' to\n");
307          fprintf(fp,"it's standard input\n");
308          fflush(fp);
309       }
310 
311       ZeroMemory(szInput,1024);
312       while (TRUE)
313       {
314          gets(szInput);
315          printf("Child echoing [%s]\n",szInput);
316          fflush(NULL);  // Must flush output buffers or else redirection
317                         // will be problematic.
318          if (!_stricmp(szInput,"Exit") )
319             break;
320 
321          ZeroMemory(szInput,strlen(szInput) );
322 
323       }
324    }

 

 

 

posted on 2015-10-27 11:41  红山玉龙  阅读(...)  评论(...编辑  收藏