摘录:VC6自带的MFC4.2中CString.Format与CRecordSet的兼容性问题
2005-05-19 15:27
651 查看
VC6自带的MFC4.2中CString.Format与CRecordSet的兼容性问题 |
《世界商业评论》ICXO.COM ( 日期:2005-01-17 16:53) |
今天我在bbs的vc版上转悠,看到由个哥们出了这样的问题: 说他在编写mfc的数据库程序(odbc)的时候出现了错误,再插入新记录后调用update的时候出现了assert,由于再bbs上,我和他通过信息交流了一下,发现他在addnew和update之间调用了format。直觉告诉我问题出在这里。 于是分析了一下。这个是我在bbs上发的帖子。 这个问题我仔细看了一下,问题出在mfc内部:下面所述仅适用于vc6带的mfc4.2 我们在使用odbc进行数据库的插入操作时,都是这么一个流程: addnew() //给成员赋值 update() 而在mfc的源文件dbcore.cpp 1040行,有这样一行注释: // buffer address must not change - odbcs sqlbindcol depends upon this 由于mfc在进行默认的数据源绑定时,使用cstring绑定字符串型的成员,而cstring使用的是动态的内存管理方式,因此这个缓冲区地址其实是可以改变的,因此,在dbcore.cpp的1041行开始便是这样几句: void* pvbind; pvbind = value.getbuffer(0); value.releasebuffer(); if (pvbind != pinfo->m_pvbindaddress) { trace1(error: cstring buffer (column %u) address has changed!n, nfield); assert(false); } 因此,如果你在调用addnew和update之间把cstring的缓冲区移动了,对不起,你必须收到一个assert。(nickshen好像就是这里的问题吧) 这样问题就很清楚了,就是在你调用addnew和update之间不能移动缓冲区。但是据我和nickshen私下讨论的结果,他只是在其中调用了cstring的成员format,想要把一个浮点数转换成字符串,如果这么做就会有问题,但是直接赋值就不会,难道format会移动cstring的缓冲区? 于是我跟踪了一下cstring的format函数,发现在被format函数调用的formatv函数的流程是这样的:先根据格式串算出大约格式化之后的字符串要占多大的空间,然后就是看是否分配新的缓冲区,然后sprintf。这个学过数据结构的都可以理解。远离很简单,但是有这么一个问题:在formatv函数中,有这么一段 (strex.cpp, 659行起) case f: va_arg(arglist, double_arg); nitemlen = 128; // width isnt truncated // 312 == strlen(-1 (309 zeroes).) // 309 zeroes == max precision of a double nitemlen = max(nitemlen, 312 nprecision); break; 这个是format对于格式串中的%f的处理,在一个switch块中。 switch之后, // adjust nmaxlen for output nitemlen nmaxlen = nitemlen; ... getbuffer(nmaxlen); 看到了没?如果你使用了%f,mfc会很保守地认为你的一个%f会占用312的字符的位置(的确够保守的,至于为何时312,注释说得很清楚),于是用这个巨大的数调用getbuffer。 然后是cstring的operator=(lpctstr),这个就简单多了,不用保守的计算,源字符串有多少个字符就分配多少个字节,同样通过getbuffer。 在getbuffer的实现中,简单的说就是看看原来的长度够不够,不够重新分配一块够大的,然后memcpy,于是,缓冲区移动了。 慢着! 如果说长度不够就要移动缓冲区,而且两种操作都会移动缓冲区,那么为何只有format会出错,赋值不会? 谁说不会?你尝试赋给你的变量一个长度超过256的字符串试试,肯定出错,我试过了。 那么,这个256又是何处来的?你在用一个recordset第一步一定是open吧。跟踪一下发现,open中有一步是bindfieldtocolumns (dbcore.cpp 3854),经过一系列的分发,程序到了dbrfx.cpp 777: case cfieldexchange::bindfieldtocolumn: ... // constrain to user specified max length, subject to 256 byte min if (cbcolumn > (uint)nmaxlength || cbcolumn < 256) cbcolumn = nmaxlength;// set up binding addres void* pvdata; value.getbuffersetlength(cbcolumn 1); pvdata = value.lockbuffer(); // will be overwritten if unicode 那么这个nmaxlength是多少呢?这个看看afxdb.h中对于rfx_text的声明,255! 明白了? 这么一说事情就很清楚了,所有的一切都是由于mfc内部造成的,由于我们大多数时候都不会像数据库中插入一个长度超过255的字符串(事实上access和sql server都只支持最多255个字符),因此不会有问题,但是偏偏mfc的工程师们在做format函数的时候保守了,于是,只要你用了%f格式符,就有问题无疑了。 知道了原因,解决方案就很简单了: 1、如果你可以改数据库,不妨把那个string(varchar)类型的字段改成double。 2、如果你没有这个权限,或者数据太多已经不能改了,那么只有退而求其次,先定义一个buffer,sprintf一下,然后赋值给cstring。 的确很麻烦。 同样的程序,再vc7下调试没有问题,有空再跟踪一下看看吧。mfc7.1的cstring已 经完全重写了... |
相关文章推荐
- VC6自带的MFC4.2中CString.Format与CRecordSet的兼容性问题
- CString怎么转成LPVOID的问题 vc/mfc
- MFC里面CString 与 string 互相转化 的问题。_基础类_VC/MFC-ITnose http://www.itnose.net/detail/6110703.html
- MFC中CString.Format的详细用法
- [vc]CString 及char 指针的常见问题
- MFC中Unicode与多字节编码中遇到的CString与char或char*的转化问题
- vc2005编译过程中"没有找到MFC80UD.DLL,因此这个程序未能启动.重新安装应用程序可能会修复此问题"? 的彻底解决
- VC 和 MFC 的一些常见问题
- (来自百度知道)c/c++ VC++ MFC 关于在Static 控件里面作图的问题 详见问题补充,希望能留下QQ,交流一下
- MFC总结(17) --- CString 参数传递问题
- 关于MFC程序中CString转char*与char*转CString的几个问题
- VC程序在非MFC中使用CString
- 定时关机程序源代码 VC MFC 权限问题
- MFC CFile追加字符串到问题件,追加CString到文件。追加创建Unicode格式
- 关于对于VC/MFC/ATL的评论问题
- CString.Format详解【摘录】
- [VC/MFC]GDI+的一个编译问题解决方案
- vc中CString::Format出现的Buffer too small错误(转)
- docablepane 可停靠窗口在使用过程中的问题小结 vc mfc
- 如何对MFC的CString::Format的封装