원문 : http://kkamagui.springnote.com/pages/410158
원문 : http://cafe.naver.com/winmain.cafe?iframe_url=/ArticleRead.nhn%3Farticleid=62
Q190351
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.
이 글은 표준 입력 핸들에서 입력을 가져오거나 표준 출력 핸들로 출력을 보내는 자식 프로세스의 입출력을 리다이렉트(redirect)하는 방법을 기술한다. Win32 API는 애플리케이션이 자식 콘솔 프로세스를 리다이렉트된 표준 핸들과 함께 생성할 수 있도록 해준다. 이 기능은 부모 프로세스가 자식 프로세스의 입출력을 보내거나 가져오는 일을 가능하게 해준다.
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.
NOTE: 몇몇 콘솔 기반 애플리케이션은 입출력 처리에 있어 표준 핸들을 사용하지 않는다. Win32 API는 이러한 프로세스들의 리다이렉트를 지원하지 않는다.
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:
CreateProcess() API를 통해 넘겨지는 STARTUPINFO 구조체는 여러분이 자식 콘솔 기반 프로세스의 표준 핸들을 리다이렉트할 수 있도록 해준다. 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.
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:
여러분은 이 핸들들을 파이프 핸들, 파일 핸들 또는 ReadFile()과 WriteFile() API를 통해 동기화된 읽기와 쓰기를 할 수 있는 어떠한 핸들로도 설정할 수 있다. 이 핸들들은 상속 가능해야 하며, CreateProcess API는 bInheritHandles 파라미터에 TRUE를 설정함으로써 이 상속 가능한 핸들들이 상속되도록 설정해야 한다. 부모 프로세스가 한 개나 두 개의 표준 핸들만을 리다이렉트하길 원한다면, GetStdHandle()을 그 핸들에 지정하여 자식 프로세스가 리다이렉트가 없는 것처럼 정상적으로 표준 핸들을 생성하도록 한다. 예를 들어 부모 프로세스가 자식 프로세스의 표준 출력과 에러 핸들만을 리다이렉트할 필요가 있다면, STARTUPINFO 구조체의 hStdInput 멤버는 다음과 같이 채워져야 한다:
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: printf()와 fprintf()와 같은 C 런타임 함수들을 사용하는 자식 프로세스들은 리다이렉트되었을 때 이상하게 작동할 수 있다. C 런타임 함수들은 별도의 IO 버퍼들을 유지한다. 리다이렉트되었을 때, 이 버퍼들은 IO 호출이 끝날 때마다 즉시 버퍼를 비우지(flush) 않을 수도 있다. 결과적으로, printf() 호출의 리다이렉트 파이프로의 출력이나 getch() 호출로부터의 입력은 즉시 버퍼를 비우지 않고 지연되고, 얼마간에서-무한한 지연이 발생한다. 이 문제는 자식 프로세스가 C 런타임 IO 함수에 대한 호출이 끝날때마다 IO 버퍼를 비운다면 피할 수 있다. 오직 자식 프로세스만이 자신의 C 런타임 IO 버퍼를 비울 수 있다. 프로세스는 fflush() 함수를 호출함으로써 C 런타임 IO 버퍼를 비울 수 있다.
NOTE: Windows 95 and Windows 98 require an extra step when you redirect the standard handles of certain child processes. For additional information, please see the following article in the Microsoft Knowledge Base:
NOTE: 윈도우 95와 윈도우 98은 어떤 자식 프로세스의 표준 핸들을 리다이렉트할 때 추가적인 과정이 필요하다. 추가적인 정보에 대해서는 마이크로소프트 Knowledge Base에 있는 다음 글을 보기 바란다.
Q150956 INFO: Redirection Issues on Windows 95 MS-DOS Applications
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).
다음 예제는 CreateProcess 호출에서 지정된 자식 프로세스의 표준 입력 출력, 에러를 리다이렉트한다. 이 예제는 제공된 콘솔 프로세스(Child.c)를 리다이렉트한다.
MSDN Library SDK documentation: CreateProcess(); STARTUPINFO structure
Inherit sample in the Win32 Platform SDK under:
\MSSDK\samples\winbase\ipc\inherit
Additional query words: Inheritance redirection redirected stdhandles
Keywords : kbAPI kbConsole kbIPC kbKernBase kbOSWinNT400 kbOSWin2000 kbSDKPlatform kbOSWin95 kbOSWin98 kbFAQ kbDSupport kbGrpDSKernBase
Issue type : kbhowto
Technology : kbAudDeveloper kbWin32sSearch kbWin32API
원문 : http://www.codeproject.com/threads/redir.asp
첨부 : redir_demo.zip, redir_src.zip
To redirect the input/output of a console application is interesting and useful. You can display the child's output in a window (just like Visual Studio's output window), or search some keywords in the output string to determine if the child process has completed its work successfully. An old, 'ugly' DOS program could become an useful component of your fancy Win32 GUI program.
My idea is to develop a simple, easy to use redirector class which can redirect an arbitrary console, and won't be affected by the behavior of the child process.
The technique of redirecting the input/output of a console process is very sample: The CreateProcess() API through the STARTUPINFO structure enables us to redirect the standard handles of a child console based process. So we can set these handles to either a pipe handle, file handle, or any handle that we can read and write. The detail of this technique has been described clearly in MSDN: HOWTO: Spawn Console Processes with Redirected Standard Handles.
However, MSDN's sample code has two big problem. First, it assumes the child process will send output at first, then wait for input, then flush the output buffer and exit. If the child process doesn't behave like that, the parent process will be hung up. The reason of this is the ReadFile() function remains blocked untill the child process sends some output, or exits.
Second, It has problem to redirect a 16-bit console (including console based MS-DOS applications.) On Windows 9x, ReadFile remains blocked even after the child process has terminated; On Windows NT/XP, ReadFile always returns FALSE with error code set to ERROR_BROKEN_PIPE if the child process is a DOS application.
To prevent the parent process from being blocked by ReadFile, we can simply pass a file handle as stdout to the child process, then monitor this file. A more simple way is to call PeekNamedPipe() function before calling ReadFile(). The PeekNamedPipe function checks information about data in the pipe, then returns immediately. If there's no data available in the pipe, don't call ReadFile.
By calling PeekNamedPipe before ReadFile, we also solve the block problem of redirecting a 16-bit console on Windows 9x.
The class CRedirector creates pipes and launchs the child process at first. then creates a listener thread to monitor the output of the child process. This is the main loop of the listener thread:
for (;;) { // redirect stdout till there's no more data. nRet = pRedir->RedirectStdout(); if (nRet <= 0) break; // check if the child process has terminated. DWORD dwRc = ::WaitForMultipleObjects( 2, aHandles, FALSE, pRedir->m_dwWaitTime); if (WAIT_OBJECT_0 == dwRc) // the child process ended { ... break; } if (WAIT_OBJECT_0+1 == dwRc) // m_hEvtStop was signalled, exit { ... break; } }
This is the main loop of the RedirectStdout() function:
for (;;) { DWORD dwAvail = 0; if (!::PeekNamedPipe(m_hStdoutRead, NULL, 0, NULL, &dwAvail, NULL)) // error, the child process might ended break; if (!dwAvail) // no data available, return return 1; char szOutput[256]; DWORD dwRead = 0; if (!::ReadFile(m_hStdoutRead, szOutput, min(255, dwAvail), &dwRead, NULL) || !dwRead) // error, the child process might ended break; szOutput[dwRead] = 0; WriteStdOut(szOutput); // display the output }
WriteStdOut is a virtual member function. It does nothing in CRedirector class. However it can be overrided to achieve our specific target, like I did in the demo project:
int nSize = m_pWnd->GetWindowTextLength(); // m_pWnd points to a multiline Edit control m_pWnd->SetSel(nSize, nSize); m_pWnd->ReplaceSel(pszOutput); // add the message to the end of Edit control
MSDN's solution is to launch an intermediate Win32 Console application as a stub process between the Win32 parent and the 16-bit console based child. In fact the DOS prompt program (on NT/XP it's cmd.exe, on 9x it's command.com) is a natural stub process we just need. We can test this in RedirDemo.exe:
Apparently this is not a good solution because it's too complicated. A more effective way is to use a batch file as the stub. Edit stub.bat file like this:
%1 %2 %3 %4 %5 %6 %7 %8 %9
Then run a command like 'stub.bat dosapp.exe', then the 16-bit DOS console application runs OK.
Other popular Threads, Processes & IPC articles:
|