使用atlcov.h/W2A和中文文本的C++Utf-8转换

C++ Utf-8 conversion using atlconv.h / W2A and Chinese texts

本文关键字:文本 C++Utf-8 转换 中文 atlcov W2A 使用      更新时间:2023-10-16

我正在执行wchar_t*到UTF-8的转换,如下所示:

char* DupString(wchar_t* t)
{ 
    if(!t) return strdup("");
    USES_CONVERSION;
    _acp = CP_UTF8;
    return strdup(W2A(t));
}

通常情况下,它运行良好,但现在我找到了一个中文文本"主体"-转换无法正常工作。

宏本身的定义如下:

#define W2A(lpw) (
    ((_lpw = lpw) == NULL) ? NULL : (
        (_convert = (lstrlenW(_lpw)+1), 
        (_convert>INT_MAX/2) ? NULL : 
        ATLW2AHELPER((LPSTR) alloca(_convert*sizeof(WCHAR)), _lpw, _convert*sizeof(WCHAR), _acp))))

在我的情况下_convert=2+1=3。传递给函数调用时,3*sizeof(WCHAR)=6。

在atlcov.h/AtlW2AHelper中,它命中WideCharToMultiByte,ret==0。

_Ret_opt_z_cap_(nChars) inline LPSTR WINAPI AtlW2AHelper(
    _Out_opt_z_cap_(nChars) LPSTR lpa, 
    _In_opt_z_ LPCWSTR lpw, 
    _In_ int nChars, 
    _In_ UINT acp) throw()
{
    ATLASSERT(lpw != NULL);
    ATLASSERT(lpa != NULL);
    if (lpa == NULL || lpw == NULL)
        return NULL;
    // verify that no illegal character present
    // since lpa was allocated based on the size of lpw
    // don't worry about the number of chars
    *lpa = '';
    int ret = WideCharToMultiByte(acp, 0, lpw, -1, lpa, nChars, NULL, NULL);
    if(ret == 0)
    {
        ATLASSERT(FALSE);
        return NULL;
    }
    return lpa;
}

@"监视"窗口中的err显示错误代码122=error_INSUFFIENT_BUFFER。

我已经尝试将缓冲区增加一个字节-nChar=7-然后转换成功。缓冲区填充了6个字节+1个ascii零终止,因此填充了7个字节。

这是W2A宏的一个错误吗?没有考虑ascii零?

有人见过类似的问题吗?

作为一个平台,我正在使用visualstudio2010,不确定其他visualstudio是否也存在问题。

在一些头文件中,这个问题似乎已经解决了——例如:

https://github.com/kxproject/kx-audio-driver/blob/master/h/gui/kDefs.h

但它适用于某些第三方项目,而不是Visual studio本身。

W2A错误地认为每个字符两个字节的缓冲区就足以进行转换。您的字符串扩展为一个包含七个字节的UTF-8字符串,其中包括以零结尾的字符串。WideCharToMultiByte因缓冲区不足而失败-这是您已经发现的。

它看起来像是一个你可以在atlcov.h:中自己修复的错误(微软不会更新VS 2010,我想更新到2015年可能已经晚了)

#define W2A(lpw) (
    ((_lpw = lpw) == NULL) ? NULL : (
        (_convert = (static_cast<int>(wcslen(_lpw))+1), 
        (_convert>INT_MAX/2) ? NULL : 
        ATLW2AHELPER((LPSTR) alloca(_convert*sizeof(WCHAR)), _lpw, _convert*4, _acp)))) //sizeof(WCHAR), _acp))))

或者,您可以使用更新的CW2A转换宏,这些宏已经分配了更大的缓冲区(每个字符4个字节,请参阅CW2AEX::Init):

static const LPCWSTR g_psz = L"主体";
LPCSTR psz = _strdup(CW2A(g_psz, CP_UTF8));

从Microsoft论坛复制粘贴,从这里:

https://social.msdn.microsoft.com/Forums/en-US/262e7b83-8cf4-45ed-a3db-5dc6064612f2/c-utf8-conversion-using-atlconvh-w2a-and-chinese-texts?forum=vcgeneral&prof=所需

您是否考虑过使用改进的ATL7宏?https://msdn.microsoft.com/en-us/library/87zae4a3.aspx#atl70stringconversionclassesmacros

CW2A pA( pW, CP_UTF8 );

这似乎假设每个Unicode字符最多4个字节,而不是2个老的那个。

这似乎是字符串的更好用法,因为CW2A的析构函数将释放转换缓冲区。

 wchar_t* pStr = NULL;
 {
     CW2A pA( pW, CP_UTF8 );
     pStr = pA;
     // pStr is valid
 }
 // pStr is invalid