十年前用C++玩会opc client后就一直想玩server,但因工作原因一直没有使用的机会和紧迫感,也因为其难度,就一直搁置着。年后因为新的工作原因,必须搞会opc server。自知年纪大了,从头啃COM技术几乎是不可能,使用网上现成的dll又担心不稳定或是出问题后不知如何查故障。后从老毛子网站得到了lightopc的源码,便花了半个月啃下了这个,虽然已经是15年前的技术,但依然光彩闪耀。
刚给客户移交了个opcserver,突然忘了测试是否内存泄漏情况,赶紧重测一遍。糟糕,加载变量时每个变量都报一次“Access in invalid memory: Attempt to access 1 byte(s) at 0x00000000.”追踪到是
mbstowcs(0x00000000, 0x03E1FD3C ["HTDOPC.bianliang01"], 0x13 [19])
这可如何是好,赶紧定位到 loAddRealTag,实现函数在dll中,还好有源码。一步步找到 mbstowcs,原来是以下一段代码:
typedef WCHAR loWchar; static void *trm[8000]; static unsigned tra; #define SIZEOF_ARRAY(x) (sizeof(x)/sizeof(x[0])) long mallocX_count = -1; void TRAP_ALLOC(void *buf) { if(tra < SIZEOF_ARRAY(trm)) { //UL_TRACE((LOGID, "TRA: %u", tra)); trm[tra++] = buf; } else { //UL_ERROR((LOGID, "mallocX TRAP OVERFLOW (%p)", buf)); } mallocX_count++; } int lo_mbstowcs(loWchar *wcs, const char *mbs, int nn) { int ii; // if (-1 == nn) nn = strlen(mbs); #ifdef _WIN32 # if 0/* This is not quite right call but in this file 'len' is already counted */ ii = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, mbs, nn, wcs, nn); # else ii = mbstowcs(wcs, mbs, nn);//wcs 参数为0即报内存故障 # endif if(wcs && ii <= 0) #endif for(ii = 0; ii < nn; ii++) { #if 0 wcs[ii] = (unsigned char)mbs[ii]; #else ((char *)wcs)[ii << 1] = mbs[ii]; ((char *)wcs)[ii << 1 | 1] = '\0'; #endif if(!mbs[ii]) break; } /* if (wcs && ii < len) wcs[ii] = 0;*/ return ii; } void *mallocX(unsigned size) { unsigned *buf; // mallocX_trap(); size += 4 * sizeof(unsigned) - 1; size /= sizeof(unsigned); buf = new unsigned[size]; if(!buf) return 0; *buf++ = (size - 1) * sizeof(unsigned); TRAP_ALLOC(buf); return (void *)buf; } loWchar *loMWstrdup(const char *str) { loWchar *ns; unsigned len; if(!str) return 0; len = strlen(str) + 1; len = lo_mbstowcs(0, str, len); /* lo_mbstowcs() never returns -1 */ //追踪到这里报不正常,第一个参数是0 ns = (loWchar *)mallocX((len + 1) * sizeof(loWchar)); if(ns) { lo_mbstowcs(ns, str, len); ns[len] = 0; } return ns; }
为了便于重新调试,以上代码是节选。
测试代码:
char x[24]="abcd0123"; WCHAR *Y=loMWstrdup(x));
不开启内存检测时,执行正常,已开启内存检测就定位到了
ii = mbstowcs(wcs, mbs, nn);//wcs 参数为0即报内存故障
查了下 mbstowcs原型,size_t mbstowcs(wchar_t *dest, const char *src, size_t n);
特意注明了:
如dest 非NULL,则mbstowcs() 函数把多字符src转换成宽字符dest,最多转换到n个宽字符(即wchar_t);
如dest为NULL,则忽略参数n,转换仍然继续,只是不写到dest,最终返回转换成功的宽字符个数(不包括终止符'\0')。
原作者真巧妙,第一次获取需要的内存大小,第二次再转换,还特意注明
never returns -1
学艺不精,虚惊一场!