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

5 篇文章 1 订阅

一、闲扯

今天领导心血来潮说服务器的运行界面是一个黑乎乎的控制台,太低端了,看我们能不能把它图形化,至少做成一个窗口有按钮点启动、停止。好吧,领导拍脑袋就叫人干活的事不少,也没有产品规划,也就是让他看的舒服,只能去做了。

其中有一个关键性问题,就是原先是往控制台输出的信息,使用mfc图形化之后该往哪输出,工程维护人员肯定还是希望能在图形界面上能够看到这些信息的,因此就涉及到了如何获取到输出控制台的消息(都是cout、printf这种标准输出),然后再将这些消息重写到view上。这时候想到了标准输出重定向,因此整理了下思路:

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

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

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

二、实现

1.创建管道

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

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

int nOpenHandle = _open_osfhandle((intptr_t)hWrite, _O_TEXT);
FILE* fp = _fdopen( nOpenHandle, "w");
*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是自定义的线程函数。

void CKDSShellView::print_cb()
{
	DWORD ReadNum = 0;
	char ReadBuf[1024] = {0};
	while (1)
	{
		memset(ReadBuf, 0, 1024);
		ReadFile(hRead, ReadBuf, 1024, &ReadNum, NULL);

		if (ReadNum == 0 || ReadBuf[0] == 0)
		{
			continue;
		}
		this->AppendLog(ReadBuf);
	}
}

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

void CKDSShellView::AppendLog(const std::string& strLog)
{
	if (log_vector.size() > 1024)
	{
		log_vector.erase(log_vector.begin());
	}

	std::vector<std::string> tmp;
	boost::split(tmp, strLog, boost::is_any_of("\n"));

	for (int i = 0; i < tmp.size(); ++i)
	{
		boost::trim(tmp[i]);
		if (!tmp[i].empty())
		{
			std::string str = tmp[i];
			std::string::iterator it = str.end();
			while(str.size() > 1)
			{
				it = str.end() - 1;
				if(*it == '\n' || *it == '\r')
				{
					str.erase(it);
				}
				else
				{
					break;
				}
			}
			log_vector.push_back(str);
		}
	}
	if(::IsWindow(m_hWnd))
	{
		Invalidate();
		this->UpdateWindow();
	}
}

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

void CKDSShellView::OnPaint()
{
	CPaintDC dc(this); // device context for painting
	// TODO: 在此处添加消息处理程序代码
	// Set the text color to red
	dc.SetTextColor(RGB(0, 0, 0)); // 可根据日志警告级别改变字体颜色

	// Set the background mode for text to transparent 
	// so background will show thru.
	dc.SetBkMode(TRANSPARENT);

	//
	TEXTMETRIC tm;
	dc.GetTextMetrics(&tm);

	int nFontHeight = tm.tmHeight;  // 字体高度
	int nExternal = tm.tmExternalLeading; // 估计是行距

	CRect rect;
	GetWindowRect(&rect);
	int nMaxShowLine = rect.Height() / (nFontHeight * 1.1);

	int i = 0;
	if(nMaxShowLine < log_vector.size())
	{
		i = log_vector.size() - nMaxShowLine;
	}
	int j = 0;
	for(; i < log_vector.size(); i++)
	{
		x = 4;
		y = j * nFontHeight * 1.1;
		dc.TextOut(x,y,log_vector[i].c_str(),log_vector[i].length());
		j++;
	}
	// 不为绘图消息调用 CView::OnPaint()
}

效果如下图:


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值