这个问题我仔细看了一下,问题出在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已
经完全重写了...