1. 程式人生 > >windows下printf重定向的實現

windows下printf重定向的實現

最近我們有一個需求,要求將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); 
      }
   }
}