十年前用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
学艺不精,虚惊一场!