2010年12月13日星期一

标准输出重定向

标准输入输出指的是控制台的输入输出,涉及的函数和对象包括C运行库中的printf,scanf,fprintf……,C++标准库中的cin,cout,cerr,……
这里只讨论标准输出的重定向。重定向包括定向到文件、屏幕(Console窗口)、GUI中。
(1)重定向到文件中
利用freopen函数实现。当将C运行库的stdout重定向到文件中后,C++标准库中的cout未做任何改变即已重定向了。
//将stdout重定向到文件中
FILE *stream;
if( (stream = freopen("file.txt", "w", stdout ) ) == NULL )
    exit(-1);
printf("this is stdout output");

//重新将stdout 重定向到console中
stream = freopen("CON","w",stdout);
pintf("And now back to the console once again");

(2)重定向到屏幕
参考资料:http://dslweb.nwnexus.com/~ast/dload/guicon.htm
对于console程序而言,谈论重定向到屏幕可能没有意义。所以这里谈论的是,对于GUI程序,也就是普通的Windows程序中,如何使用printf输出到console。
主要思路是,AllocConsole分配一个控制台窗口,GetStdHandle获得相应句柄再调用_open_osfhandle关联到C运行库的句柄,_fdopen打开句柄获得文件流指针,与stdout关联。
/*****************************************************
*guicon.cpp
*
*****************************************************/

#include <windows.h>
#include <io.h>
#include <stdio.h>
#include <fcntl.h>

#include "guicon.h"

static const WORD MAX_CONSOLE_LINES = 500;

void RedirectIOToConsole()
{
int hConHandle;
//HANDLE lStdHandle;
FILE *fp;
CONSOLE_SCREEN_BUFFER_INFO coninfo;

// allocate a console for this app
AllocConsole();

// set the screen buffer to be big enough to let us scroll text
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE),
&coninfo);
coninfo.dwSize.Y = MAX_CONSOLE_LINES;
SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE),
coninfo.dwSize);

// redirect unbuffered STDOUT to the console
hConHandle = _open_osfhandle((intptr_t)GetStdHandle(STD_OUTPUT_HANDLE), _O_TEXT);
fp = _fdopen( hConHandle, "w" );
*stdout = *fp;
setvbuf( stdout, NULL, _IONBF, 0 );

// redirect unbuffered STDIN to the console
hConHandle = _open_osfhandle((intptr_t)GetStdHandle(STD_INPUT_HANDLE), _O_TEXT);
fp = _fdopen( hConHandle, "r" );
*stdin = *fp;
setvbuf( stdin, NULL, _IONBF, 0 );

// redirect unbuffered STDERR to the console
hConHandle = _open_osfhandle((intptr_t)GetStdHandle(STD_ERROR_HANDLE), _O_TEXT);
fp = _fdopen( hConHandle, "w" );
*stderr = *fp;
setvbuf( stderr, NULL, _IONBF, 0 );

}

(3)重定向到GUI中
参考资料:http://blog.vckbase.com/michael/archive/2005/06/06/6163.aspx

将stdout重定向某个MFC程序View中显示出来,实现稍有点复杂。总体思路是
1)创建一个匿名管道(pipe)
2)将stdout重定向到这个pipe上
3)创建一个线程,读取pipe中的内容,然后将这些内容输出到你希望输出的地方,譬如一个view里。
启动进程时,先调用StdoutRedirect(),那么这个进程中所有的printf都可以输出到View里。

#include <windows.h>
#include <io.h>
#include <stdio.h>
#include <fcntl.h>

HANDLE hOutputReadTmp = 0;
HANDLE hOutputWrite = 0;
void DisplayError(char* szMsg)
{
 TRACE(szMsg);
 TRACE(" ");
}

DWORD WINAPI ReadStdout(LPVOID lpvThreadParam)
{
 CHAR lpBuffer[256];
 DWORD nBytesRead;

 while(TRUE)
 {
 if (!ReadFile(hOutputReadTmp, lpBuffer,sizeof(lpBuffer),
 &nBytesRead,NULL) || !nBytesRead)
 {
 if (GetLastError() == ERROR_BROKEN_PIPE)
 break; // pipe done - normal exit path.
 else
 DisplayError("ReadFile"); // Something bad happened.
 }

 if (nBytesRead >0 )
 {
 //获取到printf的输出
 lpBuffer[nBytesRead] = 0;

 //replace your code here. Do it yourself.
 DisplayInView(lpBuffer);
 }
 }

 return 0;
}

void StdoutRedirect()
{
 if (!CreatePipe(&hOutputReadTmp,&hOutputWrite,0,0))
 DisplayError("CreatePipe");

 int hCrt;
 FILE *hf;
 //AllocConsole();
 hCrt = _open_osfhandle(
 (long)hOutputWrite,
 _O_TEXT
 );
 hf = _fdopen( hCrt, "w" );
 *stdout = *hf;
 int i = setvbuf( stdout, NULL, _IONBF, 0 );

 // Launch the thread that gets the input and sends it to the child.
 DWORD ThreadID;
 HANDLE hThread = ::CreateThread(NULL,0,ReadStdout,
 0,0,&ThreadID);
}


后记:
想实现类似此帖中的接口,它把重定向到GUI的操作用一个回调函数来实现,可以很好地扩展,封装了匿名管道的操作,而不是像(3)中那样,用户需要在新创建的线程中修改。但尝试失败,可能是线程的操作不熟悉。

没有评论:

发表评论