回 帖 发 新 帖 刷新版面

主题:调用DLL中的API时,传递给它的参数为什么一定要堆上的?

调用DLL中的API时,传递给它的参数为什么一定要堆上的?

近来一直忙于编写短信服务程序,
前阵子由于开展一个大活动,大量短信涌进来,
多的时候一秒钟8条,
但是我给他们发送回复却达不到这样的速度(移动网关限速),
于是大量待发短信积压在内存中,导致两个问题:
一是内存使用爆涨,
二是这个服务程序不能退出(因为待发短信没发完)。
那个活动是下午五点钟结束的,但是待发短信却到七点才发完。

于是我给程序做了改变,将立即发送回复改为写入数据库,
然后从数据库里一次提取几条短信发送,
这样不会积压在内存里,而且随时可以退出服务。
下次启动服务可以继续发送。

由于不再积压于内存,
我想就不用new内存来存放了吧,我就用用栈上的内存算了,
于是做了这样的改动:

以前是这样的:
CMt *pMt = new CMt(...);//堆上申请实例
PostThreadMessage(....,(LPARAM)pMt);//将要发送的内容通知负责发送的线程
CMt *pMt = (CMt*)lParam;//负责发送的线程拿到了消息
pMt->Submit();//Submit是成员函数,里面调用了DLL中的API
delete pMt;

现在是这样做的:
线程之间根本不用消息通信,直接写入数据库,
发送线程从数据库里读到一个记录
既然不用存在内存里,就不用new内存了,直接用栈吧。
CMt mt;//栈内开劈实例
......//将数据库的内容填入mt
mt.Submit();//调用API时出错

百思不得其解,
想来想去,这次的改变主要在于用数据库代替了消息,
但是这个与调用DLL中的API无关啊。
那只有内存了。
以前的做法传递给API的参数是堆内存中的,
现在的做法传递给API的参数是栈内存中的,
于是我试了一下,仍旧在堆上申请实例,
这下居然成功了,调用API不出错了。

那么,调用API时,传递给它的参数为什么一定要堆上的?


顺便说一下:
我是在Visual Studio .NET 2002 DEBUG下编译的。

对于VC,我觉得有许多不可思议之处:
下面提两点:
以前我用的是隐式链接,编译连接的时候就把DLL里的函数连进来了,
这样的程序运行了一年多的时间,没有任何问题
后来改成显式连接,但是冒出了一个问题:
CMt *pMt;
....
pMt->Submit();
delete pMt;//出错,该实例无法删除。
我改为RELEASE编译,就正常了。
这个问题在VC++6中出现的,.Net没有出现。

现在又做了个改动,改成使用栈内存,
结果调用DLL里的函数出错,
不得不又改为使用堆内存。

那么,这两个情况放一起,总结一下:
一、必须用new来申请堆内存,否则用栈内存就无法传递到API中,
二、必须用RELEASE,否则就无法delete。(对于VC++6)

回复列表 (共1个回复)

沙发

CMt是我自己写的类,该类封装了一条短信的所有内容,
其所有成员变量要么是int,要么是(unsigned )char,
绝不含任何复杂的成员,
所以,它的构造与析构函数特别简单,绝不可能出现内存错误这样的问题。
事实上,这个类长久使用中也的的确确没有出过任何问题。
但是后来我将API由隐式调用改为显式调用,发现不能析构的问题,
现在又发现如果把CMT类对象建在栈内就无法传递给API,

那么这个API就一定存在着某种错误,
它的错误估计在于它对内存做了某些不允许的修改,
这样的非法修改要想进行,就只能用堆内存,而不能用栈内存,
而且经过这样的非法修改,该对像的虚析构函数指针被破坏。

顺便说一下,
这个API是华为公司为移动SP提供的,
华为公司在说明里指出的用法是“隐式连接”,
没有提显式连接的事,
是不是华为公司当初设计的函数是专为隐式连接做的?

我来回复

您尚未登录,请登录后再回复。点此登录或注册