ATL字符串转码的陷阱

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. 立即下载

在代码中经常使用UTF8与Wide Char的转换,每次手工调用WideCharToMultiByte转换很是麻烦,于是参考ATL的W2A、A2W这样的宏,自己写了两个宏:UTF82W、W2UTF8。

我写的UTF82W、W2UTF8与ATL的W2A、A2W一样,都是使用_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时失败,LastError为0x0000007a:The data area passed to a system call is too small。看现象应该是开辟的空间不够大。

这怎么可能呢?!查代码!结果让我大跌眼镜:ATL的W2A_CP_EX宏在转换前计算需要开辟的空间尺寸时,没有用WideCharToMultiByte本身来计算,而是(出于什么我不清楚的设计的目的)使用了lstrlen得到串中的字符个数后乘以2(sizeof(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))))

 

我的UTF8与Wide 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;

    }

 

ESP32 BLE Mesh Provisioner实战:手把手教你用ESP-IDF配网智能灯泡(附常见错误排查) 本文提供了一份基于ESP-IDF的ESP32 BLE Mesh Provisioner实战指南,详细解析了配网器的核心角色与工作流程。文章手把手教你从工程搭建、代码初始化到核心回调解析,并重点针对常见的‘Model not bound to AppKey’等错误提供了深度排查思路与解决方案,帮助你构建一个稳定可靠的智能照明网关。 阅读详情

相关推荐

基于STM32的PID恒温恒湿控制系统 | 保姆式教程 开源可复刻

本项目基于STM32F103C8T6核心控制器,完整实现了基于双路PID算法的恒温恒湿闭环控制系统,覆盖高精度温湿度采集、双模式控制、参数在线配置、异常安全保护、掉电记忆等全功能,全程采用无跳步保姆式教学,从硬件选型、接线、CubeMX配置、代码开发、调试排坑全流程拆解,跟着步骤一步步操作,就能顺利完成项目复刻

ZhongWeiXiang9的博客 346

VC9中ATL的atlconv.h中A2W_CP和W2A_CP的Bug

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

冰峰 的专栏 5710

立创EDA开源足轮机器人:基于STM32F407与ESP32的LQR自平衡与FOC驱动设计全解析

本文详细解析了基于立创EDA平台的开源足轮机器人项目。该项目采用STM32F407作为主控,ESP32负责无线通信,并应用LQR自平衡算法与FOC驱动技术,实现了机器人的稳定站立、移动转向及复杂地形适应能力。文章深入剖析了其硬件架构、软件设计及调试经验,为机器人运动控制学习提供了宝贵参考。

weixin_42608299的博客 232

关于A2W字符转换宏

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

jia_xiaoxin的专栏 1659

基于KiCad的STM32最小系统设计实战案例(含PCB布线)

手把手带你用KiCad完成STM32最小系统设计,涵盖原理图绘制、封装匹配、规则约束设置及关键信号PCB布线技巧;全程基于开源EDA工具KiCad,强调实操细节与常见坑点规避。

weixin_42452483的博客 357

扣子(coze)实战 | 一键生成爆款书单视频

摘要:本文介绍了一个基于DeepSeek和Coze平台实现短视频高效生成的工作流方案。该方案耗时仅1-2分钟即可完成"假如书籍会说话"类短视频制作,包含书籍内容深度解读、对话台词生成、自动配音、素材合成等环节,支持通过剪映进行二次编辑。作者分享了完整开发思路和8个工作模块,并开放了包含该工具在内的100+智能体共享空间,提供会员专属服务。该方案为非真人出镜短视频的批量化生产提供了可行方案。

yueyehuguang的博客 2395

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

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

jszj的专栏 2093

UsualToolCMS-验证码绕过漏洞复现

本文以UsualToolCMS为靶场,复现了验证码绕过漏洞

小小小屿屿屿的博客 919

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的专栏 1186

数字IC后端时序违例修复:setup/hold冲突与时钟树优化实战

时序收敛是数字IC后端设计中最关键的环节之一,直接影响芯片能否在目标频率下稳定工作。建立时间与保持时间的本质是寄存器对数据稳定性的物理约束,而实际工程中,setup违例和hold违例往往此消彼长,冲突的根源在于数据路径与时钟路径的延迟匹配。时钟树综合(CTS)作为连接逻辑与物理的关键步骤,其skew、insertion delay和transition的优化直接决定了时序修复的成败。通过useful skew技术、合理的单元替换和路径结构调整,可以系统性地解决setup/hold冲突,实现时序收敛。本文结合

weixin_28292761的博客 222

联想开天N70z/N79Z G1d笔记本Win驱动下载

本文介绍了联想开天N70Z/N79Z G1d笔记本在Win11系统下的驱动安装方法。提供了WiFi网卡驱动(RealtekWlan)和显卡驱动(ZX C960 GPU)的详细安装步骤,包括手动更新驱动和卸载流程。驱动安装后成功支持5G WiFi连接并正确识别显卡型号。文中附有系统信息和安装效果截图,同时提供了官方驱动下载地址及备用下载链接。该指南适用于需要为联想开天N70Z/N79Z G1d笔记本安装Win11驱动的用户参考。

信创运维工程师 · 银河麒麟 & 容器实战 | 金融生产环境迁移·等保加固 | 142篇原创·17万+阅读 3875

使用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

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

SOME/IP协议详解「4.1·经典AutoSAR下的SOME/IP架构」

SOME/IP协议详解「4.1·经典AutoSAR下的SOME/IP架构」 点击返回雪云飞星的SOME/IP协议详解「总目录」 SOME/IP协议详解「4.1·经典AutoSAR下的SOME/IP架构」1 CP的SOME/IP架构概览2 业务报文通信流3 服务发现通信和控制流 1 CP的SOME/IP架构概览 2 业务报文通信流 3 服务发现通信和控制流 点击返回雪云飞星的SOME/IP协议详解「总目录」 ...

雪云飞星的博客 6067

ATL的另一个陷阱

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

lyclowlevel的专栏 1848

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

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

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

weixin_34074740的博客 491

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

UTF8转换宏和转换类

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

crazy Crypto 3547

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...

流楚丶格念的博客 6633

UTF-8 与UniCode 编码转换

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

提升自己-虚心钻研 1414

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万+

UTF8和UNICODE互转

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

CJJ的专栏 408

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万+

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

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

gdruhv的博客 5209

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

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

weixin_30344995的博客 607

ATL字符串转换类

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

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

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值