记CRenderTarget:DrawText()绘制中文乱码的BUG及解决办法

摘要:
如下图:二、问题分析这个问题出现之后,我设置断点定位到CRenderTarget::DrawText()调用处,F11进入afxrendertarget.cpp文件中的函数CRenderTarget::DrawText()内部,查看函数如下:voidCRenderTarget::DrawText{USES_CONVERSION;if{ASSERT;return;}if(!这里应该是CRenderTarget中的一个bug,正确的第二个参数应该是第一个参数T2CW的实际长度wcslen。到这里我们验证了一个结论,这种情况下中文显示出现乱码是因为MFC中CRenderTarget类内部的一个BUG,这样我们就找到了问题的根源。

转载请注明出处:http://www.cnblogs.com/Ray1024

一、问题描述

在MFC中使用Direct2D有现成的方法,在Visual Studio 2010 SP1及以上环境中MFC封装了Direct2D,我们就可以更加方便、更加简洁地使用Direct2D来进行高效率绘图了,详细教程见msdnhttps://msdn.microsoft.com/zh-cn/library/gg482848.aspx
但是在实际项目中遇到一个问题:MFC项目编码方式为unicode时,CRenderTarget::DrawText()方法正常;但是编码方式为多字节编码时,CRenderTarget::DrawText()绘制中文出现乱码。如下图:
记CRenderTarget:DrawText()绘制中文乱码的BUG及解决办法第1张

二、问题分析

这个问题出现之后,我设置断点定位到CRenderTarget::DrawText()调用处,F11进入afxrendertarget.cpp文件中的函数CRenderTarget::DrawText() 内部,查看函数如下:

void CRenderTarget::DrawText(const CString& strText,
	const CD2DRectF& rect, CD2DBrush* pForegroundBrush,
	CD2DTextFormat* textFormat,
	D2D1_DRAW_TEXT_OPTIONS options,
	DWRITE_MEASURING_MODE measuringMode)
{
	USES_CONVERSION;

	if (m_pRenderTarget == NULL)
	{
		ASSERT(FALSE);
		return;
	}

	if (!VerifyResource(pForegroundBrush))
	{
		return;
	}

	if (textFormat == NULL)
	{		
		// Use default text format
		if (m_pTextFormatDefault == NULL)
		{
			NONCLIENTMETRICS NonClientMetrics;
			NonClientMetrics.cbSize = sizeof(NONCLIENTMETRICS);

			::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, NonClientMetrics.cbSize, &NonClientMetrics, 0);

			m_pTextFormatDefault = new CD2DTextFormat(this, NonClientMetrics.lfMenuFont.lfFaceName, (FLOAT)abs(NonClientMetrics.lfMenuFont.lfHeight));
		}

		textFormat = m_pTextFormatDefault;
	}

	if (!textFormat->IsValid())
	{
		ASSERT(FALSE);
		return;
	}

	m_pRenderTarget->DrawText(T2CW(strText), strText.GetLength(), textFormat->m_pTextFormat, &rect, 
		*pForegroundBrush, options, measuringMode);
}

这是CRenderTarget封装的DrawText,内部使用ID2D1RenderTarget对象调用DrawText函数,第一个参数为宽字节字符串,第二个参数为宽字节字符串的长度。

但是这里有一个问题,假如字符串为"中文中文123", 当工程编码为Unicode时,CString使用wchar_t初始化,GetLength获取的长度是7 ;当工程编码为多字节时,CString使用char初始化,GetLength获取的长度是11(因为多字节编码只能用两个ANSI字符表示一个中文字符) 。而你的工程是多字节编码,下面的第一个参数为宽字节字符串,长度为7;但是第二个参数获取到的长度却是11,所以会出现乱码 。这里应该是CRenderTarget中的一个bug,正确的第二个参数应该是第一个参数T2CW(strText)的实际长度wcslen(T2CW(strText)) 。
到这里我们验证了一个结论,这种情况下中文显示出现乱码是因为MFC中CRenderTarget类内部的一个BUG,这样我们就找到了问题的根源。

三、解决方案

然而,知道了这个BUG并没有什么卵用,因为afxrendertarget.cpp文件是MFC内部的只读文件,我们并不能对CRenderTarget类做任何改动(摊手)。
但是,我们可以绕个路来解决这个问题:可以使用Direct2D原生API(ID2D1RenderTarget::DrawText())来实现绘制中文,我们在这里把第二个参数改成wcslen(T2CW(strText)),就可以正常地显示中文了,如下:
CString strText(_T("中文Hello, World!"));

//pRenderTarget->DrawText(strText, rect, m_pBlackBrush, m_pTextFormat);

// 把原来的DrawText替换成这个
USES_CONVERSION;
pRenderTarget->GetHwndRenderTarget()->DrawText(
	T2CW(strText),
	wcslen(T2CW(strText)),
	m_pTextFormat->Get(),
	&D2D1::Rect(rect.left, rect.top, rect.right, rect.bottom),
	m_pBlackBrush->Get());

当然,我们也可以直接改工程编码方式为Unicode编码,这样也不会出现乱码(简单粗暴,哈哈),只是有些工程出于种种原因只能用多字节编码方式,这样就可以使用上面的方法了。

搞定!

免责声明:文章转载自《记CRenderTarget:DrawText()绘制中文乱码的BUG及解决办法》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇WordPress隐藏后台左侧菜单如何操作JAVA编程---------11、有1,2,3,4四个数字,能组成多少个互不相同且一个数字中无重复数字的三位数?并把他们都输出下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

随便看看

Android开发者选项——Gpu呈现模式分析

对于Android用户来说,无论你用的什么品牌的手机,在开发者选项中都能发现“玄学曲线”的开关,之所以称其为玄学曲线,还是因为它被很多网友用于测试一个说不清道不明的东西——流畅度。说到玄学曲线其实它的真名叫做“ProfileGPURendering”,在中文安卓系统下译为“GPU显示配置文件”或“GPU呈现模式分析”等,根据不同厂商定制系统叫法稍有不同。...

NodeJs使用jwt生成token以及使用express-jwt校验和解密token

=0){//当数据库有当前用户时,它返回tokenlettoken=jwt.sign;res.send}else{res.send}}catch{//p抛出异常并将其发送到错误中间件以处理console.log;next;}})//注册接口路由器。post('/register',异步(req,res,next)=˃{let{用户名,密码,昵称}=req-b...

js学习-es6实现枚举

最近,我大部分时间都在写dart,突然使用了js。我发现js不能直接声明枚举。目录枚举功能对象冻结()符号实现反映了不可更改值的唯一性。请注意,枚举特性枚举值不能重复,也不能修改。Switchcase可以直接判断对象。冻结()对象。方法可以冻结对象。无法更改实现constEnumSex=Object。冷冻枚举性别。人=1;安慰日志;//符号(男性)表示值co...

如何让FireFox/chrome新打开的标签页在后台打开,而不是立即跳转过去

Firefox:在地址栏中输入about:config以查找以下三项,所有这些项都设置为true browser。选项卡。加载后台浏览器。选项卡。加载DivertedInBackgroundbrowser。tabs LoadBookmarksInBackgroundchrome:Mac OS:按住命令键,用鼠标单击,您可以在后台打开一个新选项卡,进入Goog...

关于异常STATUS_IN_PAGE_ERROR(0xC0000006)

表示0x%p处的指令引用了位于0x%p的内存。由于0x%x的I/O错误状态,未将所需数据放入内存。设备如果读取出错,驱动程序可以返回此异常。其定义如下:////MessageId:STATUS_IN_PAGE_ERROR////MessageText:////Theinstructionat0x%preferencedmemoryat0x%p.Therequ...

app中的弹窗—(对话框,动作菜单,浮出层,toast,snackbar等)

对话框,警告框,toast......都属于弹窗,但是使用场景都不一样。两者相比,alert出现在屏幕中央,actionsheet出现在屏幕底部。视觉角度上alert相较于actionsheet更有阻断感,对用户而言干扰性更大。...