Fork me on GitHub

记CRenderTarget:DrawText()绘制中文乱码的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()调用处,F11进入afxrendertarget.cpp文件中的函数CRenderTarget::DrawText() 内部,查看函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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)),就可以正常地显示中文了,如下:
1
2
3
4
5
6
7
8
9
10
11
12
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编码,这样也不会出现乱码(简单粗暴,哈哈),只是有些工程出于种种原因只能用多字节编码方式,这样就可以使用上面的方法了。

搞定!

 

posted @   江湖码客Mark  阅读(1129)  评论(0编辑  收藏  举报
编辑推荐:
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· 展开说说关于C#中ORM框架的用法!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
点击右上角即可分享
微信分享提示