근 한달동안 모든 코딩을 어셈블리어로만 하고 있다보니 호출 규약(Calling Convention)을 다시 살펴보고 있습니다. C로 프로그래밍하던 시절에는 호출 규약은 별로 신경쓰지 않았는데, 어셈블리어로 함수를 호출하려니 안 볼수가 없더군요. ㅠㅠ


MINT64 OS를 만들 때 어셈블리어로 코딩했으면서 왠 엄살이냐고 생각하실텐데... 리눅스에서 사용하는 호출 규약(Calling Convention)과 윈도우에서 사용하는 호출 규약은 상당한 차이가 있더라구요. ㅠㅠ 그래서 적응하는데 살짝 힘들었습니다. 사실 적응한다기 보다는 차이를 아는데 시간이 좀 걸렸지요. ㅠㅠ

64비트 리눅스의 호출 규약

리눅스는 64비트 모드에서 파라미터를 전달할 때 윈도우보다 레지스터를 더 많이 사용합니다. 정수 타입의 파라미터를 전달할 때는 순서대로 RDI, RSI, RDX, RCX, R8, R9까지 6개의 레지스터를 사용하고 7개 이상이면 스택을 통해 전달합니다. 실수 타입의 파라미터의 경우는 XMM0 ~ XMM7까지 8개를 순서대로 사용하고 그 이상이면 스택으로 전달하지요. ;)


반환값은 정수일 때 RAX(하위 64비트), RDX(상위 64비트)를 사용하고, 실수일때는 XMM0(하위 128비트), XMM1(상위 128비트)를 사용합니다. 아래는 64비트 멀티코어 OS 원리와 구조의 11.2.2 장에서 추출한 호출 규약 그림입니다. ;)




64비트 윈도우의 호출 규약

반면, 윈도우는 64비트 모드에서 파라미터를 전달할 때 레지스터 4개만 사용합니다. 정수 타입의 경우는 순서대로 RCX, RDX, R8, R9를 사용하고 나머지는 스택으로 전달합니다. 실수의 경우는 XMM0 ~ XMM3까지 4개를 순서대로 사용하며 나머지는 스택으로 전달합니다. 여기까지 보면 파라미터로 사용하는 레지스터의 종류와 개수만 차이가 나는 것 같습니다만.... 실제로 보면 스택을 사용하는 방법도 차이가 있습니다.


리눅스의 경우 파라미터를 전달할 때 스택을 꽉꽉 채워서 사용하는 반면, 윈도우의 경우는 4개의 레지스터가 들어갈 공간만큼을 띄워서 사용합니다. 즉, 파라미터 4개가 RCX, RDX, R8, R9를 통해 전달되지만, 스택에 이 4개를 위한 공간을 할당해놓는 것이죠. 아래 그림을 보시면 좀 더 이해하시기 편할 겁니다. ^^;;;





이 차이 때문에 한참을 헤맸네요. ㅠㅠ 스택에 저 공간을 안 할당해놓으면 파라미터가 잘못 전될되서 보기좋게 함수 호출이 실패를... 쿨럭..;;; 그리고 주의할 점은 비록 파라미터가 4개 미만이더라도 함수 호출 시 저 영역은 무조건 할당해야 한다는 겁니다. 에궁... 진짜 알고나면 별 것 아닌데... 코드를 몇 번이나 갈아 엎었는지 모르겠네요. ㅠㅠ


C로 코딩을 했으면 아예 신경을 안써도 되는 부분인데... 어셈블리어로 코딩하다보니 삽질을 하고 말았군요. ㅎㅎ 윈도우 호출 규약에 대한 보다 자세한 내용이 궁금하시다면 MSDN 사이트를 참고하시기 바랍니다. ^^


그나저나 PE32 파일 포멧을 읽어 메모리에 로딩하여 실행하는 가볍게 어셈블리어로 작성하는 괴수(?)를 어떻게해야 따라잡을 수 있을까요? 아우... 따라가려니 죽을 것만 같네요. ㅠㅠ


그럼 좋은 밤 되세요 ;)

04 윈도우 콘솔(console) 입출력 리다이렉션(Input/Out Redirection)

원문 : https://kkamagui.tistory.com/85

들어가기 전에...

첫번째 방법

** 원문 :** http://cafe.naver.com/winmain.cafe?iframe_url=/ArticleRead.nhn%3Farticleid=62

HOWTO: Spawn Console Processes with Redirected Standard Handles

Q190351

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.

이 글은 표준 입력 핸들에서 입력을 가져오거나 표준 출력 핸들로 출력을 보내는 자식 프로세스의 입출력을 리다이렉트(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)를 리다이렉트한다.

Sample Code

/*++
    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>

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) );
    }
 }

REFERENCES

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](http://kkamagui.springnote.com/pages/410158/attachments/171448 "redir_demo.zip"), [redir_src.zip**](http://kkamagui.springnote.com/pages/410158/attachments/171449 "redir_src.zip")

Introduction

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.

Background

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.

Solving the block problem of ReadFile

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

To redirect DOS console based applications on NT/2000/XP

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:

  1. Input 'cmd.exe' in Command Editbox, then press Run button.
  2. Input the name of the 16-bit console based application (dosapp.exe for example) in the Input Editbox, then press Input button. Now we can see the output of the 16-bit consol.
  3. Input 'exit' in the Input Editbox, then press Input button to terminate cmd.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.

About nickadams

  Nick Adams is one of my favorite figures in Hemingway's stories. I use it because Jeff Lee has been occupied on Codeproject.

Click here to view nickadams's online profile.

Other popular Threads, Processes & IPC articles:

+ Recent posts