Dll分配的内存块,应用释放的问题

rainman2001 2003-10-09 05:33:43
我在dll中分配了一块内存,通过PostMessage将其地址传给应用.
然后应用去释放它,结果总是报异常.

dll代码如下:
typedef struct _Test_Struct
{
char *lpszOutData;
UINT uDataLen;
}TESTSTRUCT,*LPTESTSTRUCT;

#define WM_TESTDLL (WM_USER + 10)
HWND m_hWnd;
DWORD WINAPI TestThreadProc(LPVOID pParam);

extern "C" void WINAPI TestFreeMemory(HWND hWnd)
{
m_hWnd = hWnd;
CreateThread(NULL,0,TestThreadProc,NULL,0,NULL);
}

DWORD WINAPI TestThreadProc(LPVOID pParam)
{
LPTESTSTRUCT lpTest = new TESTSTRUCT;
lpTest->lpszOutData = new char[100];
strcpy(lpTest->lpszOutData,"100000000000000000");
lpTest->uDataLen = strlen(lpTest->lpszOutData);

PostMessage(m_hWnd,WM_TESTDLL,0,(LPARAM)lpTest);

return 0;
}

应用程序的相应代码如下:
void CTestDlg::_TestFreeMemory(WPARAM wParam,LPARAM lParam)
{
LPTESTSTRUCT lpTest = (LPTESTSTRUCT) lParam;
AfxMessageBox(lpTest->lpszOutData);
delete[] lpTest->lpszOutData;
delete lpTest;
}

当我运行到delete[] lpTest->lpszOutData时内存报错.

我在应用程序写了和dll一样中处理代码没有问题.如下:
#define WM_TESTDLL2 (WM_USER + 11)

void CTestDlg::_TestFreeMemory2(WPARAM wParam,LPARAM lParam)
{
LPTESTSTRUCT lpTest = (LPTESTSTRUCT) lParam;
AfxMessageBox(lpTest->lpszOutData);
delete[] lpTest->lpszOutData;
delete lpTest;
}

DWORD WINAPI TestThreadProc(LPVOID pParam);
HWND g_wnd;

void CTestDlg::OnBtnfreememory2()
{
// TODO: Add your control notification handler code here
g_wnd = m_hWnd;
CreateThread(NULL,0,TestThreadProc,NULL,0,NULL);
}

DWORD WINAPI TestThreadProc(LPVOID pParam)
{
LPTESTSTRUCT lpTest = new TESTSTRUCT;
lpTest->lpszOutData = new char[100];
strcpy(lpTest->lpszOutData,"01111111111111111111");
lpTest->uDataLen = strlen(lpTest->lpszOutData);

PostMessage(g_wnd,WM_TESTDLL2,0,(LPARAM)lpTest);

return 0;
}

此段没有问题.

请指教!感谢!
...全文
3339 15 打赏 收藏 转发到动态 举报
写回复
用AI写文章
15 条回复
切换为时间正序
请发表友善的回复…
发表回复
sjdf 2003-10-11
  • 打赏
  • 举报
回复
我只知道在dll里加一个释放内存的函数可能是个办法。
《windows核心编程》19章有说明。
向楼上各位学习。
yafizyh 2003-10-11
  • 打赏
  • 举报
回复
比较规范点的做法一般是DLL分配的内存由DLL释放。
在DLL中加一个函数释放内存不是更好吗。
lop5712 2003-10-10
  • 打赏
  • 举报
回复
To bluebohe(薄荷)
啊,对不起,的确,不确定的事就不要乱说。是我不对,抱歉。
我已经试过了,失败,不过马上又成功了,因为一开始我的exe使用Single Thread(即静态连接),dll改成了MultiThread Dll(即动态连接),依然使用两个CRT,所以失败。后来将exe改成使用MultiThread Dll后就成功了,因为都使用一个CRT了——MSVCRT.DLL。
多谢bluebohe(薄荷)指教,现在比刚才对CRT有了更多的了解了
bluebohe 2003-10-10
  • 打赏
  • 举报
回复
——————————————————————————————————————————
To bluebohe(薄荷)
由于上面的原因,所以你说的第三个方法将无效,因为不能改变CRT静态连接和用C++编写的事实。不过由于没有试过,我也不敢妄下定论(我对CRT的运行机理还不了解),说它是错的,试过才知道。
————————————————————————————————————————————
我的第三个做法是对的,你也说了,试过才知道,你没有试过,单纯给出这些道理,怎么能让别人信服呢?
lop5712 2003-10-10
  • 打赏
  • 举报
回复
哈哈哈哈,真是搞笑,事实总是与想象差距甚远。一个模块一个堆,一个线程一个栈。

问题的一切都要归咎于C运行时期库是静态连接的。
我回去看了一下C运行时期库的源码,发现一个很搞笑的事实:CRT(C运行时期库)不是使用进程缺省的堆来实现malloc(new中调用malloc)的,而是使用一个全局句柄HANDLE __crtheap(好象是_crtheap,记不清了)来分配内存的。这个_crtheap是在XXXCRTStartUp(CRT提供的进口点函数)中创建的。
由于CRT静态连接,则楼主的DLL里有也有一个CRT,因此也有一个_crtheap。而在dll中的new使用dll中的_crtheap句柄分配堆,在exe中的delete使用exe中的_crtheap释放堆,当然失败!不信的话,楼主大可在你的exe和dll中各下一个断点,在变量查看中输入_crtheap来查看其值,一般会差个0x10000000-0x04000000,因为缺省dll的基址是0x10000000,而exe的是0x04000000。

因此你的模块也有一个与之相关联的堆了,因为使用C/C++语言编写,而且CRT静态连接。
To bluebohe(薄荷)
由于上面的原因,所以你说的第三个方法将无效,因为不能改变CRT静态连接和用C++编写的事实。不过由于没有试过,我也不敢妄下定论(我对CRT的运行机理还不了解),说它是错的,试过才知道。
bluebohe 2003-10-09
  • 打赏
  • 举报
回复
其实还有一个办法,就是把dll的Settings的C/C++选项卡的Code Generation的Use Run-time liberary改成Debug Multithreaded DLL,在Release版本中改成Multithreaded DLL,就可以直接使用new和delete了,没问题
lop5712 2003-10-09
  • 打赏
  • 举报
回复
真是非常非常非常之抱歉,我发现说错了的,不过看来已经晚了。哈哈
to bluebohe(薄荷)
对不起,我上面说错了,你的第一方法是对的,抱歉
只有进程才能有堆,而线程有栈,诚如bmon_xyz(钳工)所说,我学艺不精,才造成上面的谬论。失礼
至于为什么是错的,楼主执行时
AfxMessageBox(lpTest->lpszOutData);
的对话框弹出的字符串是对的?
我水平有限,不知原由,不好意思。
bmon_xyz 2003-10-09
  • 打赏
  • 举报
回复
to lop5712(LOP):
DLL使用的应该是应用程序的堆和调用线程的栈吧!

DLL使用的空间是属于应用程序的,只要内存指针是合法的,应用程序应该可以delete.

你可以调试一下在delete时指针是否是你分配时的值?看当时指针是否有效?
cxf1976 2003-10-09
  • 打赏
  • 举报
回复
dll可以有自己的堆吗?
tank198131 2003-10-09
  • 打赏
  • 举报
回复
因为DLL 生存空间和应用程序的生存空间不在一起,所以DLL 传的地址和应用程序所在的地址段不同,以至于应用程序释放这段内存空间时,产生了冲突。
bluebohe 2003-10-09
  • 打赏
  • 举报
回复
lop5712(LOP)
第一个是错了,不好意思,没看清楚题:(
lop5712 2003-10-09
  • 打赏
  • 举报
回复
我猜想楼主是想知道原因。
原因很简单,new分配的内存是从堆上的来的,而局部变量都是从栈上得到内存。每个模块都有一个与之相绑定的堆和一个栈,其两个的大小都可以通过编译器选项指定。所以楼主的new是从dll的堆上分,delete是从应用程序的堆上撤,根本不是同一个堆上,所以出错。

楼上已经给出解决方法,可惜第一个方法是错的。因为调用GetProcessHeap()得到的是当前堆(其实就跟new一样了),依然借还不在同一个地方,所以错的。
bluebohe 2003-10-09
  • 打赏
  • 举报
回复
还有别的答案
用GlobalAlloc()代替new, 用GlobalFree() 代替delete就不会出错了
bluebohe 2003-10-09
  • 打赏
  • 举报
回复
最简单的解决方法是使用单一的堆

把相应的new size改为 HeapAlloc(GetProcessHeap(),0,size);

把相应的delete []p 改为HeapFree(GetProcessHeap(),0,p);

mfc168 2003-10-09
  • 打赏
  • 举报
回复
在dll内部申请的内存,外部程序不可以DELETE,必须在你的DLL释放的时候,由你DLL内部函数来释放

15,471

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 进程/线程/DLL
社区管理员
  • 进程/线程/DLL社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧