windows 檔案描述符 _open_osfhandle

dolphin98629發表於2020-11-20

最近我們有一個需求,要求將windows控制檯程式中通過printf列印的字串資訊全部重定向到另一個遠端控制終端上去;並且也將標準輸入也重定向到遠端終端。重定向資訊的傳輸用TCP傳輸。

TAG: 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/
   這篇帖子裡講了一種在視窗程式中用控制檯來顯示除錯列印的方法,其中的核心程式碼摘錄如下

 
  1. HANDLE hOutput=GetStdHandle(STD_OUTPUT_HANDLE); 
  2. HANDLE hInPut=GetStdHandle(STD_INPUT_HANDLE); 
  3. //redirect stdout and stdin 
  4. int hCrt; 
  5. FILE *hf,*hf2; 
  6. hCrt = _open_osfhandle((intptr_t)hOutput,0×4000); 
  7. hf = _fdopen( hCrt, “w” ); 
  8. *stdout = *hf; 
  9. hCrt = _open_osfhandle((intptr_t)hInPut, 0×4000); 
  10. hf2 = _fdopen(hCrt,”r”); 
  11. *stdin = * hf2;   

   寫到這裡大家應該明白了,要向重定向標準輸入和輸出還是要在CRT庫中的stdout和stdin上做文章, 這才是根本。
4. 終極解決

 
  1. //************************************************ 
  2.    // 建立一個管道,用於重定向標準輸出 
  3.    if (! CreatePipe(&g_hChildStdoutRd, &g_hChildStdoutWr, &saAttr, 0)) 
  4.    { 
  5.        printf("Stdout pipe creation failed\n"); 
  6.    } 
  7.    // 用3中提到的方法來將標準輸出重定向到這個管道上 
  8.    hCrt = _open_osfhandle((long)g_hChildStdoutWr, 0x4000); 
  9.    hf = _fdopen( hCrt, "w" ); 
  10.    *stdout = *hf; 
  11.    setvbuf(stdout,NULL,_IONBF,0); 
  12.    // 建立一個執行緒,從管道的另一頭截獲資料 
  13.    CreateThread(NULL, 
  14.                 NB_TPF_SERVER_INPUT_COMMAND_TASK_STACK_SIZE, 
  15.                 (LPTHREAD_START_ROUTINE)console_thread_out, 
  16.                 (LPVOID)NULL, 
  17.                 0, 
  18.                 NULL); 
  19.    //************************************** 
  20.    OSP_STATUS console_thread_out(void) 
  21.    { 
  22.       CHAR chBuf[1024]; 
  23.       DWORD dwRead; 
  24.       BOOL fSuccess; 
  25.       while (1) 
  26.       { 
  27.          // 測試 
  28.          printf("say hi to everybody\n"); 
  29.          // 從管道的另一頭截獲資料 
  30.          fSuccess = ReadFile(g_hChildStdoutRd, chBuf, 1024, &dwRead, NULL); 
  31.          // 用socket發到遠端終端 
  32.          send(g_s32PrintMsgSockFd, chBuf, dwRead, 0);  
  33.       } 
  34.    } 

 

檢視evl的程式碼

#ifndef EV_FD_TO_WIN32_HANDLE
# define EV_FD_TO_WIN32_HANDLE(fd) _get_osfhandle (fd)
#endif
#ifndef EV_WIN32_HANDLE_TO_FD
# define EV_WIN32_HANDLE_TO_FD(handle) _open_osfhandle (handle, 0)
#endif
#ifndef EV_WIN32_CLOSE_FD
# define EV_WIN32_CLOSE_FD(fd) _close (fd)
#endif

可以將socket程式設計標準檔案描述符來進行_close。

相關文章