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

有一天发现,这个多字节和unicode编码转换有了 USES_CONVERSION、A2W、W2A 几个宏后,简单了很多,用起来也方便的很。

以前有注意到,说 A2W、W2A 的“返回值”不用删除,它自己回收,立即感觉这个相当好,都不用管内存的分配和删除了,用完弃之即可。

如今涉及到这个问题,不得不面对。几乎花了半天的功夫终于弄清楚是啥情况了。先说问题。

MFC程序,有一个CListCtrl,其中显示了很多的文件名,有好几千个,每个文件名再加上相关的信息,长度大概有180个字符。现在的目的是要把这些内容写到一个文本文件中,代码大致如下:

void CMyDlg::ExportFiles()
{
    USES_CONVERSION;
    CString strText;
    CListCtrl *pLst = &GetListCtrl();
    int count = pLst->GetItemCount();
    char *ptmp = NULL;
    FILE *pFile = NULL;
    errno_t et = _tfopen_s(&pFile, TEXT("d:/1.txt"), TEXT("w+"));

    for (int im = 0; im < count; im++)
    {
        strText = pLst->GetItemText(im, 0);
        strText += TEXT("\n");
        ptmp = W2A(strText);
        fwrite(ptmp, 1, strlen(ptmp), pFile);
    }
    fclose(pFile);
    pFile = NULL;
}

这是功能的主要代码,这个看上去似乎问题不大。

之前碰到过这样的情况:当for循环的次数非常大的时候,如果变量放在for内去声明,则会导致这些变量反复的进栈出栈,从而严重拖慢运行速度,所以我会尽量把所有变量的声明放在循环的外面。

在for循环里,就是获取一行,然后转换成多字节,然后保存到文件。经过测试,如果count不超过2000时,没有出现过问题,当超过3000时就一定出问题,在出问题时,im的值在2205左右。

之前也有次遇到了相关问题,试了各种办法,但均不管用,主要有:

1. 在每次写文件之后,strText.ReleaseBuffer()

2. 把W2A放在小的域内:

for (int im = 0; im < count; im++)
{
    strText = pLst->GetItemText(im, 0);
    strText += TEXT("\n");
    {
        char *ptmp1 = W2A(strText);
        fwrite(ptmp1, 1, strlen(ptmp1), pFile);
    }
}

3. 把CString放在for内声明

最后没办法,就把for内的内容放在一个单独的函数里解决了问题,大致如下:

void CMyDlg::WriteOneLine(FILE *pFile, int line)
{
    USES_CONVERSION;
    CString strText = GetListCtrl().GetItemText(line, 0);
    char *p = W2A(strText);
    fwrite(p, 1, strlen(p), pFile);
}

void CMyDlg::ExportFiles()
{
    ...
    for (im = 0; im < count; im++)
        WriteOneLine(pFile, im);
}

今天又遇到这个问题,搜索了一下CString的使用注意事项,没有看到有价值的内容。仔细查看了下CString的一些内存释放,看上去似乎也是正常的,比如附值为TEXT("")时,会把之前的内存给删除掉,所以对它进行+、=也不应该有问题。

没办法,就想到了关于A2W、W2A返回指针不需要删除的情形。后通过msdn查看W2A的说明,其示例也没有特别的,比如:

void StringFunc(LPSTR lpsz)
{
   USES_CONVERSION;

   LPWSTR x = A2W(lpsz);
   // Do something with x
   wprintf_s(L"x is %s", x);
}

这似乎和我的用法一样,也不见用完后再来个delete[]操作。

但是有如下两行说明,仔细捉摸:

The destination string is created using _alloca, except when the destination type is BSTR. Using _alloca allocates memory off the stack, so that when your function returns, it is automatically cleaned up. By default this macro will only convert up to 500KB at one time.

注意最后的500KB字样。综合情况为,在栈上分配内存,所以不能无限制分配,就限制了500KB,当超过500KB时,自然就失败,而在使用过程中,没有加失败的处理,所以就只能崩溃。

知道了原因,就好处理了,把它改为堆上分配,每次用完立即删除即可。为了达到这个目的,仿照W2A添加自己的转换宏(直接拷贝一份,然后稍做修改):

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

#define FREEWA(lp) if (lp) {delete[] (lp); (lp) = NULL;}

第一个只是把 alloca 改成 malloc ,然后再加了一个自己的释放宏 FREEWA。

修改for循环的代码如下所示:

for (int im = 0; im < count; im++)
{
    strText = pLst->GetItemText(im, 0);
    strText += TEXT("\n");
    ptmp = MYW2A(strText);
    fwrite(ptmp, 1, strlen(ptmp), pFile);
    FREEWA(ptmp);
}

再编译,运行,问题解决

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值