重要提示:本文章是 Microsoft 软件自动翻译的结果,而非专业译者翻译的结果。 Microsoft 提供专业人员翻译的文章、由自动翻译生成的文章以及来自 Microsoft 社区的文章翻译,因此你能够以自己的语言阅读所有知识库文章。 需要注意的是,由自动翻译生成的文章(包括 Microsoft 社区审阅的文章)可能包含词汇、句法或语法错误。 对于因不正确的内容翻译或使用不正确的内容翻译而造成的不准确或错误或任何损害,Microsoft 概不负责。
查看原始英文文章:190351
概要
本文介绍如何为从标准输入句柄接收输入或将输出发送到标准输出句柄的子进程重定向输入和输出。Win32 API 使应用程序可以生成带重定向标准柄子控制台进程。此功能允许父进程发送和接收的输入和输出的子进程。
注意:某些基于控制台的应用程序不要为其输入/输出 (IO) 操作使用标准的句柄。Win32 API 不支持这些过程的重定向。
详细信息
STARTUPINFO 结构通过 CreateProcess() API 使您能够重定向标准子基于控制台进程的句柄。如果 dwFlags 成员设置为 STARTF_USESTDHANDLES,然后下面的 STARTUPINFO 成员指定子控制台基于进程的标准控点:
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.
您可以设置这些句柄到管道句柄、 文件句柄或同步读取和写入到 ReadFile() 和 WriteFile() API 可以执行任何处理。必须是可继承的句柄和 CreateProcess() API 必须指定可继承的句柄都要通过 bInheritHandles 参数中指定真由子进程继承。如果父进程只希望重定向一个或两个标准的句柄,指定特定的句柄的 GetStdHandle() 使子标准的句柄创建如通常一样不重定向。例如,如果只需要重定向标准输出和错误的子进程的父进程,会按如下所述填充 hStdInput STARTUPINFO 结构的成员:
hStdInput = GetStdHandle(STD_INPUT_HANDLE);
注意:重定向时,使用此类 C 运行时函数 printf () 和 fprintf() 的子进程可以不当行为。C 运行时函数维护单独的 IO 缓冲区。如果重定向,这些缓冲区可能不刷新每个 IO 调用后立即。因此,从 getch() 调用 printf () 调用或输入重定向管道的输出不立即刷新并出现延迟,有时无限延迟。如果子进程会 IO 缓冲区刷新到 C 运行时 IO 函数每次调用之后,为避免该问题。只有子进程可刷新其 C 运行时 IO 缓冲区。一个进程可以通过调用 fflush() 函数来刷新其 C 运行时 IO 缓冲区。
注意:重定向特定的子进程的标准柄时,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 平台 SDK 中的示例:
\MSSDK\samples\winbase\ipc\inherit