ATL字符串转码的陷阱

AI权益加码!Claude Code、Cursor等20+工具免费用! 购周边限时加赠Coding Plan Lite,畅享主流AI工具!学习进阶更高效! 阅读详情

在代码中经常使用UTF8Wide Char的转换,每次手工调用WideCharToMultiByte转换很是麻烦,于是参考ATLW2AA2W这样的宏,自己写了两个宏:UTF82WW2UTF8

我写的UTF82WW2UTF8ATLW2AA2W一样,都是使用_alloca在栈上开辟临时空间存放转换结果的。后来,由于在使用中出现了一个函数中转换的次数过多导致栈溢出的问题。于是想改进这个宏。

比较新的ATL库里还有W2A_EX,这个宏第二个参数是本次转换在栈上最大开辟空间的长度(其实看过代码就知道,它无视了你的这个长度,总是使用一个硬编码的_ATL_SAFE_ALLOCA_DEF_THRESHOLD这个宏指定的1024作为最大开辟空间长度)。

我想要的正是这个功能。但上次仿写W2UTF8已经写伤了(看了实现你就知道了)。翻ATL源代码,找到好东西了:W2A_CP_EX,这个宏与W2A_EX类似,使用时多一个参数,用于指定转换的代码页。由于用WideCharToMultiByte来转换UTF8串时,是通过指定一个叫CP_UTF8的特殊代码页来实现的,于是,我天真的认为,只要指定CP_UTF8来使用W2A_CP_EX,就不用自己去写W2UTF8_EX了。

说用就用。在测试代码中试了一下,果然可以转,没多想就用上了。结果没几天,程序出现错误了,用这个宏,很多串都会转换失败。查代码,发现在这个宏调用WideCharToMultiByte时失败,LastError0x0000007aThe data area passed to a system call is too small。看现象应该是开辟的空间不够大。

这怎么可能呢?!查代码!结果让我大跌眼镜:ATLW2A_CP_EX宏在转换前计算需要开辟的空间尺寸时,没有用WideCharToMultiByte本身来计算,而是(出于什么我不清楚的设计的目的)使用了lstrlen得到串中的字符个数后乘以2sizeof(WCHAR))当做新串空间长。明显的,如果串中包含中文,转换后是3个字节,而不是2个字节。于是,空间不够是当然的了。

我认为,ATL这么做,主要是从性能上考虑,lstrlen不需要进行实际的转换,只是计算串中的字符数,性能当然好过WideCharToMultiByte

但这样的话,就出现了抽象泄漏:其它API指定代码页时,可以使用CP_UTF8,但这里指定代码页时就不能使用,你得知道它的实现才能正确使用。

一个好的设计,应当是不会被用错的。调用两次WideCharToMultiByte,时间复杂度不会变化。我认为这里没必要为了性能牺牲抽象。

一段好的代码,应当是严谨的。遵守API的使用条件非常重要。WideCharToMultiByte要求的转换后空间大小,只有他知道(你要知道还用他转换什么劲呀),那就应当让他告诉你准备多大的空间。

事情的结果:最终,我自己又仿照W2A_CP_EX重新实现了W2UTF8_EX,不同的是:我使用了WideCharToMultiByte来计算所需的空间。

 

附:

ATL的实现:

#define A2W_CP_EX(lpa, nChars, cp) (/

    ((_lpa_ex = lpa) == NULL) ? NULL : (/

        _convert_ex = (lstrlenA(_lpa_ex)+1),/

        FAILED(::ATL::AtlMultiply(&_convert_ex, _convert_ex, static_cast<int>(sizeof(WCHAR)))) ? NULL : /

        ATLA2WHELPER(   /

            (LPWSTR)_ATL_SAFE_ALLOCA(_convert_ex, _ATL_SAFE_ALLOCA_DEF_THRESHOLD), /

            _lpa_ex, /

            _convert_ex / sizeof(WCHAR), /

            (cp))))

 

#define W2A_CP_EX(lpw, nChars, cp) (/

    ((_lpw_ex = lpw) == NULL) ? NULL : (/

        _convert_ex = (lstrlenW(_lpw_ex)+1),/

        FAILED(::ATL::AtlMultiply(&_convert_ex, _convert_ex, static_cast<int>(sizeof(WCHAR)))) ? NULL : /

        ATLW2AHELPER(   /

            (LPSTR)_ATL_SAFE_ALLOCA(_convert_ex, _ATL_SAFE_ALLOCA_DEF_THRESHOLD), /

            _lpw_ex, /

            _convert_ex, /

            (cp))))

 

我的UTF8Wide Char转换宏:

 

#define USES_CONVERSION_UTF8 /

    USES_CONVERSION_EX; /

    int _convert_rhyme = 0; /

    (_convert_rhyme); /

    UINT _acp_rhyme = CP_UTF8 ; /

    (_acp_rhyme); /

    LPCTSTR _lpt_rhyme = NULL; /

    (_lpt_rhyme); /

    LPCSTR _lputf8_rhyme = NULL; /

    (_lputf8_rhyme)

 

 

#define UTF82W_EX(lputf8, nChars) (/

    ((_lputf8_rhyme = lputf8) == NULL) ? NULL : (/

    _convert_rhyme = MultiByteToWideChar(CP_UTF8, 0, _lputf8_rhyme, (int)(strlen(_lputf8_rhyme)), NULL, 0),/

    (_convert_rhyme == 0) ? NULL : /

    (RhymeUTF82WideCharHelper( /

    (LPWSTR)_ATL_SAFE_ALLOCA((_convert_rhyme + 1) * sizeof(WCHAR), nChars), /

    _lputf8_rhyme, /

    _convert_rhyme + 1))))/

 

 

#define UTF82W(lputf8) UTF82W_EX(lputf8, _ATL_SAFE_ALLOCA_DEF_THRESHOLD)

 

 

#define W2UTF8_EX(lpwide, nChars) (/

    ((_lpt_rhyme = lpwide) == NULL) ? NULL : (/

    _convert_rhyme = WideCharToMultiByte(CP_UTF8, 0, _lpt_rhyme, (int)(wcslen(_lpt_rhyme)), NULL, 0, NULL, NULL),/

    (_convert_rhyme == 0) ? NULL : /

    (RhymeWideChar2UTF8Helper( /

    (LPSTR)_ATL_SAFE_ALLOCA((_convert_rhyme + 1) * sizeof(char), nChars), /

    _lpt_rhyme, /

    _convert_rhyme + 1))))/

 

#define W2UTF8(lpwide) W2UTF8_EX(lpwide, _ATL_SAFE_ALLOCA_DEF_THRESHOLD)

 

 

    LPWSTR RhymeUTF82WideCharHelper(LPWSTR lpszWideString, LPCSTR lpszUTF8String, size_t nWideCharCount)

    {

        _ASSERTE(lpszWideString != NULL);

        _ASSERTE(lpszUTF8String != NULL);

        _ASSERTE(nWideCharCount <= TypeLimit<int>::Max());

        _ASSERTE(strlen(lpszUTF8String) <= TypeLimit<int>::Max());

 

        size_t nUTF8Length = strlen(lpszUTF8String);

 

        if(lpszWideString == NULL || lpszUTF8String == NULL ||

            nWideCharCount > TypeLimit<int>::Max() ||

            nUTF8Length > TypeLimit<int>::Max())

        {

            return NULL;

        }

 

        int nConverted = MultiByteToWideChar(

            CP_UTF8,

            0,

            lpszUTF8String,

            (int)(nUTF8Length),

            lpszWideString,

            (int)(nWideCharCount));

 

        if(nConverted == 0 && ERROR_NO_UNICODE_TRANSLATION == GetLastError())

        {

            return NULL;

        }

        lpszWideString[nConverted] = L'/0';

        return lpszWideString;

    }

 

    LPSTR RhymeWideChar2UTF8Helper(LPSTR lpszUTF8String, LPCWSTR lpszWideString, size_t nUTF8ByteCount)

    {

        _ASSERTE(lpszWideString != NULL);

        _ASSERTE(lpszUTF8String != NULL);

        _ASSERTE(nUTF8ByteCount <= TypeLimit<int>::Max());

        _ASSERTE(wcslen(lpszWideString) <= TypeLimit<int>::Max());

 

        size_t nWideCharCount = wcslen(lpszWideString);

 

        if(lpszWideString == NULL || lpszUTF8String == NULL ||

            nUTF8ByteCount > TypeLimit<int>::Max() ||

            nWideCharCount > TypeLimit<int>::Max())

        {

            return NULL;

        }

 

        int nConverted = WideCharToMultiByte(

            CP_UTF8,

            0,

            lpszWideString,

            (int)(nWideCharCount),

            lpszUTF8String,

            (int)(nUTF8ByteCount),

            NULL,

            NULL);

 

        if(nConverted == 0 && ERROR_NO_UNICODE_TRANSLATION == GetLastError())

        {

            return NULL;

        }

 

        lpszUTF8String[nConverted] = '/0';

        return lpszUTF8String;

    }

 

ATL 2013 ,纯静态版本的ATL ATL+WTL,Windows平台仍然是一对锋利的组合。 主要特点: http://blogs.msdn.com/b/vcblog/archive/2013/08/20/atl-and-mfc-changes-and-fixes-in-visual-studio-2013.aspx One of the major changes we made was to eliminate the ATL DLL altogether. All ATL code is now static, either in the header files or in the ATL static library. We also reduced the amount of code in the ATL static library substantially, so there are no longer multiple static libraries for debug/release mode or Unicode/ANSI character set. There is only one ATL static library that is common to all configurations. 立即下载

相关推荐

VC9中ATLatlconv.h中A2W_CP和W2A_CP的Bug

 VC9中ATLatlconv.h有Bug,请大家注意一下,这些问题网上也能搜索到:http://blog.csdn.net/BalonFan/archive/2009/04/24/4108985.aspx,但它对EX系列的转换宏理解错了。我这里澄清一下:W2A_CP转换宏,allocate分配的空间大小不对(它原来只固定乘2),但当cp为UTF8时,是有可能一个汉字转换

冰峰 的专栏 5709

USES_CONVERSION的相关宏A2W、W2A使用注意事项

有一天发现,这个多字节和unicode编码转换有了 USES_CONVERSION、A2W、W2A 几个宏后,简单了很多,用起来也方便的很。 以前有注意到,说 A2W、W2A 的“返回值”不用删除,它自己回收,立即感觉这个相当好,都不用管内存的分配和删除了,用完弃之即可。 如今涉及到这个问题,不得不面对。几乎花了半天的功夫终于弄清楚是啥情况了。先说问题。 MFC程序,有一个CListCtrl...

jszj的专栏 2090

关于A2W字符转换宏

在一个函数的循环体中使用A2W等字符转换宏可能引起栈溢出。#include void fn(){    while(true)    {        {            USES_CONVERSION;            DoSomething(A2W("SomeString"));        }    }}让我们来分析以上的转换宏#define A2W(lpa) (/

jia_xiaoxin的专栏 1627

UTF-8编解码之实现

根据对UTF8编码格式的研究,作出如下的简单的实现代码,希望大家指正批评并继续完善:int WideCharToUTF8(wchar_t* source, int sourceLen, unsigned char* dest, int& destLen)...{ destLen = 0; if (sourceLen  0) ...{  return destLen = 0; } wchar

winner82的专栏 1185

A2W W2A A2T T2A _T() 含义以及用法

A2W、W2A、A2T、T2A _T() 的含义及使用方法 1、A2W和W2A 在《Window核心编程》,多字节和宽字节之间转换比较麻烦的,MultiByteToWideChar函数和WideCharToMultiByte函数有足够多的参数的意义让我们去理解。那么使用ATL的一个很好的字符串的转换宏:A2W和W2A。 char:8位字节类型,表示ASCII码 WCHAR:16位字符类型,表示Un...

流楚丶格念的博客 6589

ATL的另一个陷阱

ATL早期提供了宏A2W,W2A进行编码转换,但是该组宏存在以下缺点: 1、默认认为转换前的参数(A2W)、转换后的结果(W2A)的编码是ANSI; 2、转换后结果保存在栈上。   该组宏无法满足我们从UTF8、UCS2之间的互相转换,并且有可能导致栈溢出。鉴于此,当我发

lyclowlevel的专栏 1840

C++/MFC Unicode 串口开发避坑指南:3步解决字符集转换与数据解析乱码

本文深入探讨了C++/MFC在Unicode环境下进行串口开发时遇到的字符集转换与数据解析乱码问题,提供了3步解决方案。通过Win32 APIATL转换宏的安全使用,以及二进制数据处理的正确方法,帮助开发者有效避免常见陷阱,特别适用于工业控制和光源控制器等场景。

weixin_34074740的博客 488

使用ATL的W2A和A2W宏

#include 代码如下://使用ATL的W2A和A2W宏必须使用USES_CONVERSIONUSES_CONVERSION; //Unicode字符串wchar_t* wszText=L"1.Unicode字符转换为ANSI;";printf("%s/n",W2A(wszText)); //用wprintf输出非英文字符,需要设置当前的地域信息setlocal

生命因你而精彩!明天更美好! 1655

UTF8转换宏和转换类

参考微软 ANSI和WCHAR的转换代码,编写的UTF8和ANSI、WCHAR转换代码。 可在ATL/MFC环境下使用。

crazy Crypto 3547

UTF-8 与UniCode 编码转换

 所谓“utf-8”只是UCS Transformation Format,只是UNICODE的一种表现形式,不等同于UNICODE,一般汉字在UNICODE中为两个(双)字节表示,而我们看到实际保存的文档确是三个字节表示一个汉字的,看看下表:U-00000000 - U-0000007F:  0xxxxxxxU-00000080 - U-000007FF:  110xxxxx 10xxx

提升自己-虚心钻研 1412

UTF8和UNICODE互转

/* * ===================================================================================== * * Filename: iconv.c * * Description: j * * Version: 1.0 * Created: 08/05...

CJJ的专栏 407

UNICODE与UTF-8的转换

3.3 UTF-8的编码方式 UTF-8是UNICODE的一种变长度的编码表达方式(一般UNICODE为双字节[指UCS2]),UTF-8就是以8位为单元对UCS进行编码,而UTF-8不使用大尾序和小尾序的形式,每个使用UTF-8储存的字符,除了第一个字节外,其余字节的头两个位元都是以"10"开始,使文字处理器能够较快地找出每个字符的开始位置。 为了与以前的ASCII码相容(ASCII为一个字节),因此 UTF-8 选择了使用可变长度字节来储存 Unicode,具体转换关...

满腹的小不甘 1万+

BIG5编码, GB编码(GB2312, GBK, ...), Unicode编码, UTF8, WideChar, MultiByte, Char 说明与区别

一. BIG5编码, GB编码(GB2312, GBK, ...), Unicode编码 1.1. BIG5编码 BIG5字集是台湾繁体字集,共包括国标繁体汉字13053个 1.2. GB编码 GB2312字集是简体字集, 全称为GB2312(80)字集, 共包括国标简体汉字6763个; GB2312是中国规定的汉字编码, 也可以说是简体中文的字符集编码; GBK包含全部中文字

cay22的专栏 1万+

ATL字符串转换类

字符串的ASCII和UNICODE之间的转换很常用,Win32提供了API函数MultiByteToWideChar和WideCharToMultiByte来提供这种功能。但凡Win32API,参数调用都不会少,因此使用起来也就觉得有点繁琐。ATL3中提供了字符串转换宏,如T2W、A2T等,不过使用它们需要借助本地变量,因此在使用之前需要声明USES_CONVERSION宏来声明本地变量.而且还...

weixin_30354675的博客 347

A2W和W2A 很好的多字节和宽字节字符串的转换宏

A2W和W2A 很好的多字节和宽字节字符串的转换宏

gdruhv的博客 5205

【VS开发】GDI+ 用CImage类来显示PNG、JPG等图片

系统环境:Windows 7 软件环境:Visual Studio 2008 SP1 本次目的:实现VC单文档、对话框程序显示图片效果 CImage是VC.NET中定义的一种MFC/ATL共享类,也是ATL的一种工具类,它提供增强型的(DDB和DIB)位图支持,可以装入、显示、...

weixin_30344995的博客 605
上一篇: 运行期显示多态指针的类型名
下一篇: 条件断点引发的血案
BalonFan
博客等级 码龄26年 47粉丝 41原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值