控制台输出重定向到MFC的view对象里

  转载自:https://www.xuebuyuan.com/2017000.html

一、思路:

1.将标准输出重定向到管道

2.创建一个线程从管道里取出数据

3.在view的OnPaint中将数据显示出来

二、实现

1.创建管道

1 BOOL bRet = CreatePipe(&hRead, &hWrite, NULL, 0); // 创建匿名管道
2 if (bRet != TRUE)
3     printf("创建匿名管道失败,错误代码:%d\n", GetLastError());

2.将标准输出重定向到管道的写句柄中

1 int nOpenHandle = _open_osfhandle((intptr_t)hWrite, _O_TEXT);
2 FILE* fp = _fdopen( nOpenHandle, "w");
3 *stdout = *fp;

  注意:这里不能使用SetStdHandle(STD_OUTPUT_HANDLE, hWrite),在MFC中SetStdHandle是没有效果的。

3.创建线程从管道取数据

  boost::thread thr(boost::bind(&CKDSShellView::print_cb, this));

  这里用到boost的thread创建线程,CKDSShellView就是你自己的view了,print_cb是自定义的线程函数。

 1 void CKDSShellView::print_cb()
 2 {
 3     DWORD ReadNum = 0;
 4     char ReadBuf[1024] = {0};
 5     while (1)
 6     {
 7         memset(ReadBuf, 0, 1024);
 8         ReadFile(hRead, ReadBuf, 1024, &ReadNum, NULL);
 9 
10         if (ReadNum == 0 || ReadBuf[0] == 0)
11         {
12             continue;
13         }
14         this->AppendLog(ReadBuf);
15     }
16 }

  死循环,通过ReadFile函数从管道的读句柄hRead中取出数据,然后AppendLog到一个vector容器中。

 1 void CKDSShellView::AppendLog(const std::string& strLog)
 2 {
 3     if (log_vector.size() > 1024)
 4     {
 5         log_vector.erase(log_vector.begin());
 6     }
 7 
 8     std::vector<std::string> tmp;
 9     boost::split(tmp, strLog, boost::is_any_of("\n"));
10 
11     for (int i = 0; i < tmp.size(); ++i)
12     {
13         boost::trim(tmp[i]);
14         if (!tmp[i].empty())
15         {
16             std::string str = tmp[i];
17             std::string::iterator it = str.end();
18             while(str.size() > 1)
19             {
20                 it = str.end() - 1;
21                 if(*it == '\n' || *it == '\r')
22                 {
23                     str.erase(it);
24                 }
25                 else
26                 {
27                     break;
28                 }
29             }
30             log_vector.push_back(str);
31         }
32     }
33     if(::IsWindow(m_hWnd))
34     {
35         Invalidate();
36         this->UpdateWindow();
37     }
38 }

  由于输出的字符串有\n\r的换行符,并且可能有多条日志同时输出,在view中显示不美观,因此做了一些字串的调整,主要是有log_vector.push_back(str),这是将处理好的字符串装入容器中,然后通过UpdateWindow()触发view的OnPaint()。

 1 void CKDSShellView::OnPaint()
 2 {
 3     CPaintDC dc(this); // device context for painting
 4     // TODO: 在此处添加消息处理程序代码
 5     // Set the text color to red
 6     dc.SetTextColor(RGB(0, 0, 0)); // 可根据日志警告级别改变字体颜色
 7 
 8     // Set the background mode for text to transparent 
 9     // so background will show thru.
10     dc.SetBkMode(TRANSPARENT);
11 
12     //////////////////////////////////////////////////////////////////////////
13     TEXTMETRIC tm;
14     dc.GetTextMetrics(&tm);
15 
16     int nFontHeight = tm.tmHeight;  // 字体高度
17     int nExternal = tm.tmExternalLeading; // 估计是行距
18 
19     CRect rect;
20     GetWindowRect(&rect);
21     int nMaxShowLine = rect.Height() / (nFontHeight * 1.1);
22 
23     int i = 0;
24     if(nMaxShowLine < log_vector.size())
25     {
26         i = log_vector.size() - nMaxShowLine;
27     }
28     int j = 0;
29     for(; i < log_vector.size(); i++)
30     {
31         x = 4;
32         y = j * nFontHeight * 1.1;
33         dc.TextOut(x,y,log_vector[i].c_str(),log_vector[i].length());
34         j++;
35     }
36     // 不为绘图消息调用 CView::OnPaint()
37 }

posted @ 2020-12-02 17:48  傍风无意  阅读(344)  评论(0)    收藏  举报