windows下printf重定向的實現
阿新 • • 發佈:2019-01-24
最近我們有一個需求,要求將windows控制檯程式中通過printf列印的字串資訊全部重定向到另一個遠端控制終端上去;並且也將標準輸入也重定向到遠端終端。重定向資訊的傳輸用TCP傳輸。解決
這個問題,其實就是要求能夠截獲printf的字元資料,使其不在控制檯上列印,而是通過socket傳到
遠端終端後列印。並且將標準輸入重定向到一個socket上,從上面阻塞收資料作為標準輸入。
解決過程:
1. 當然是到網上找答案了。的確發現有不少兄弟跟我們有同樣的需求。搜到了與之相關的三種解決
方案。
<1> 用freopen可以將標準輸出可以重定向到一個開啟的檔案中。呼叫該函式後就可以將printf的
資料自動寫入到一個檔案中了。這種方法確實實現了標準輸出的重定向,但是總不能讓我反覆的讀文
件,再把資料通過socket發出去吧。。。
<2> 用SetStdHandle重定向標準輸入和輸出。大家都找到了這個函式,表面看來通過它的確可以
很容易的將標準輸入和輸出重定向到一個管道中去。可是實踐證明,我們被這個“美好”的介面給涮
了。msdn中給出了這個介面的一個示例,那個示例還挺麻煩的,讓一個檔案中的資料通過管道傳送到
一個子程序中後再從子程序中通過管道發回來,繞了一圈最後列印到黑視窗控制檯上。費了半天事把
那個demo看明白了。滿懷欣喜的呼叫SetStdHandle把標準輸出重定向到一個管道後,調了一把
printf("say hi to everybody\n"),結果還是顯示到了黑視窗上,根本就沒有往管道里送!憤怒,幸
好N多人都被它蒙了一把,心裡才平衡一些。大家到這個時候一般就一籌莫展了。
<3> http://www.regexlab.com/zh/stdredir/
這個帖子中的哥們實現了一個RedirectStdout函式,可以重定向標準輸出/輸入/能通過callback函式截獲資料…….帖子寫的很好,可是那個哥們太吝嗇了,不提供原始碼!在這裡鄙視一下。把他的那個庫下載下來,在vc.2003下用也一下,根本沒有反應!!
2. 彷徨……
實在是在網上找不到答案了,彷徨中。。。
3. 曙光……
http://my.donews.com/wucr/2006/11/27/ipskeiovqkvhupzswvbxgqpwsiyjnezvurfz/
這篇帖子裡講了一種在視窗程式中用控制檯來顯示除錯列印的方法,其中的核心程式碼摘錄如下
HANDLE hOutput=GetStdHandle(STD_OUTPUT_HANDLE);
HANDLE hInPut=GetStdHandle(STD_INPUT_HANDLE);
//redirect stdout and stdin
int hCrt;
FILE *hf,*hf2;
hCrt = _open_osfhandle((intptr_t)hOutput,0×4000);
hf = _fdopen( hCrt, “w” );
*stdout = *hf;
hCrt = _open_osfhandle((intptr_t)hInPut, 0×4000);
hf2 = _fdopen(hCrt,”r”);
*stdin = * hf2;
寫到這裡大家應該明白了,要向重定向標準輸入和輸出還是要在CRT庫中的stdout和stdin上做文章, 這才是根本。
4. 終極解決
//*******************************************************************
// 建立一個管道,用於重定向標準輸出
if (! CreatePipe(&g_hChildStdoutRd, &g_hChildStdoutWr, &saAttr, 0))
{
printf("Stdout pipe creation failed\n");
}
// 用3中提到的方法來將標準輸出重定向到這個管道上
hCrt = _open_osfhandle((long)g_hChildStdoutWr, 0x4000);
hf = _fdopen( hCrt, "w" );
*stdout = *hf;
setvbuf(stdout,NULL,_IONBF,0);
// 建立一個執行緒,從管道的另一頭截獲資料
CreateThread(NULL,
NB_TPF_SERVER_INPUT_COMMAND_TASK_STACK_SIZE,
(LPTHREAD_START_ROUTINE)console_thread_out,
(LPVOID)NULL,
0,
NULL);
//***********************************************************************
OSP_STATUS console_thread_out(void)
{
CHAR chBuf[1024];
DWORD dwRead;
BOOL fSuccess;
while (1)
{
// 測試
printf("say hi to everybody\n");
// 從管道的另一頭截獲資料
fSuccess = ReadFile(g_hChildStdoutRd, chBuf, 1024, &dwRead, NULL);
// 用socket發到遠端終端
send(g_s32PrintMsgSockFd, chBuf, dwRead, 0);
}
}
}
這個問題,其實就是要求能夠截獲printf的字元資料,使其不在控制檯上列印,而是通過socket傳到
遠端終端後列印。並且將標準輸入重定向到一個socket上,從上面阻塞收資料作為標準輸入。
解決過程:
1. 當然是到網上找答案了。的確發現有不少兄弟跟我們有同樣的需求。搜到了與之相關的三種解決
方案。
<1> 用freopen可以將標準輸出可以重定向到一個開啟的檔案中。呼叫該函式後就可以將printf的
資料自動寫入到一個檔案中了。這種方法確實實現了標準輸出的重定向,但是總不能讓我反覆的讀文
件,再把資料通過socket發出去吧。。。
<2> 用SetStdHandle重定向標準輸入和輸出。大家都找到了這個函式,表面看來通過它的確可以
很容易的將標準輸入和輸出重定向到一個管道中去。可是實踐證明,我們被這個“美好”的介面給涮
了。msdn中給出了這個介面的一個示例,那個示例還挺麻煩的,讓一個檔案中的資料通過管道傳送到
一個子程序中後再從子程序中通過管道發回來,繞了一圈最後列印到黑視窗控制檯上。費了半天事把
那個demo看明白了。滿懷欣喜的呼叫SetStdHandle把標準輸出重定向到一個管道後,調了一把
printf("say hi to everybody\n"),結果還是顯示到了黑視窗上,根本就沒有往管道里送!憤怒,幸
好N多人都被它蒙了一把,心裡才平衡一些。大家到這個時候一般就一籌莫展了。
<3>
這個帖子中的哥們實現了一個RedirectStdout函式,可以重定向標準輸出/輸入/能通過callback函式截獲資料…….帖子寫的很好,可是那個哥們太吝嗇了,不提供原始碼!在這裡鄙視一下。把他的那個庫下載下來,在vc.2003下用也一下,根本沒有反應!!
2. 彷徨……
實在是在網上找不到答案了,彷徨中。。。
3. 曙光……
http://my.donews.com/wucr/2006/11/27/ipskeiovqkvhupzswvbxgqpwsiyjnezvurfz/
這篇帖子裡講了一種在視窗程式中用控制檯來顯示除錯列印的方法,其中的核心程式碼摘錄如下
HANDLE hOutput=GetStdHandle(STD_OUTPUT_HANDLE);
HANDLE hInPut=GetStdHandle(STD_INPUT_HANDLE);
//redirect stdout and stdin
int hCrt;
FILE *hf,*hf2;
hCrt = _open_osfhandle((intptr_t)hOutput,0×4000);
hf = _fdopen( hCrt, “w” );
*stdout = *hf;
hCrt = _open_osfhandle((intptr_t)hInPut, 0×4000);
hf2 = _fdopen(hCrt,”r”);
*stdin = * hf2;
寫到這裡大家應該明白了,要向重定向標準輸入和輸出還是要在CRT庫中的stdout和stdin上做文章, 這才是根本。
4. 終極解決
//*******************************************************************
// 建立一個管道,用於重定向標準輸出
if (! CreatePipe(&g_hChildStdoutRd, &g_hChildStdoutWr, &saAttr, 0))
{
printf("Stdout pipe creation failed\n");
}
// 用3中提到的方法來將標準輸出重定向到這個管道上
hCrt = _open_osfhandle((long)g_hChildStdoutWr, 0x4000);
hf = _fdopen( hCrt, "w" );
*stdout = *hf;
setvbuf(stdout,NULL,_IONBF,0);
// 建立一個執行緒,從管道的另一頭截獲資料
CreateThread(NULL,
NB_TPF_SERVER_INPUT_COMMAND_TASK_STACK_SIZE,
(LPTHREAD_START_ROUTINE)console_thread_out,
(LPVOID)NULL,
0,
NULL);
//***********************************************************************
OSP_STATUS console_thread_out(void)
{
CHAR chBuf[1024];
DWORD dwRead;
BOOL fSuccess;
while (1)
{
// 測試
printf("say hi to everybody\n");
// 從管道的另一頭截獲資料
fSuccess = ReadFile(g_hChildStdoutRd, chBuf, 1024, &dwRead, NULL);
// 用socket發到遠端終端
send(g_s32PrintMsgSockFd, chBuf, dwRead, 0);
}
}
}