重定向输入和输出

      本文介绍如何重定向输入和输出的子进程从标准输入句柄接收输入,或将输出发送到标准输出句柄。Win32 API 使应用程序可以生成与重定向标准的句柄的子控制台进程。此功能允许父进程发送和接收的输入和输出的子进程。

注意某些基于的控制台应用程序并为其输入/输出 (IO) 操作不使用标准的句柄。Win32 API 不支持这些进程的重定向。

通过 STARTUPINFO 结构 CreateProcess() API 使您能够重定向标准的句柄的子基于控制台进程。如果 dwFlags 成员被设置为 ST...

通过 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 参数中指定为 TRUE。如果父进程只希望重定向一个或两个标准的句柄,指定为特定的句柄的 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)。

示例代码
#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) );
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值