我读了Eli Bendersky关于从DLL导出C ++类的优秀article。
我想创建一个创建按钮控件的类,但是必须从DLL导出此类。所以,我遵循Eli在他的网站上提供的步骤,除了一件事,我将在本文结束时解释:)
我写了一个非常简单的接口IChildWindow只是为了测试:
// IChildWindow.H
class IChildWindow
{
public:
virtual void Create(HWND hParent) = 0;
virtual ~IChildWIndow() {} // UPDATE: Added a virtual destructor as suggested by
// Brandon, PaulMcKenzie and WhozCraig.
};
然后,对于我的DLL,我编写了一个实现此接口并导出了几个函数的类 - 一个用于为此类的对象分配内存并将其返回给调用者,另一个用于释放它。
// ButtonControl.H
#include <Windows.H>
#include "..\MainApp\IChildWindow.H"
class ButtonControl: public IChildWindow
{
HWND m_hButton;
public:
ButtonControl() : m_hButton(NULL) {}
void Create(HWND hParent);
};
// ButtonControl.CPP
#include "ButtonControl.H"
void ButtonControl::Create(HWND hParent)
{
m_hButton = CreateWindow(L"BUTTON", L"Hello World", WS_CHILD |
WS_VISIBLE, 0, 0, 100, 100, hParent, NULL,
(HINSTANCE)GetWindowLongPtr(hParent, GWLP_HINSTANCE),
NULL);
}
ButtonControl.CPP中此类定义的正下方是两个导出函数:
extern "C" __declspec(dllexport) IChildWindow * __cdecl CreateButton()
{
return new ButtonControl();
}
extern "C" __declspec(dllexport) void __cdecl DestroyButton(IChildWindow * ptrButton)
{
delete ptrButton;
}
我编译了这段代码并创建了ButtonControl.DLL。接下来,我制作了一个使用此控件的Windows应用程序。我只是跳过这个应用程序的创建代码,直接进入DLL魔术发生的Window程序。
// MainProc.CPP
LRESULT CALLBACK MainProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_CREATE:
if(!Initialize(hWnd))
MessageBox(NULL, L"Something went wrong!", L"Error", MB_ICONSTOP);
return 0;
case WM_CLOSE:
CloseApplication();
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
bool Initialize(HWND hWnd)
{
// Load the DLL.
m_dllButton = LoadLibrary(L"ButtonControl.DLL");
if(m_dllButton == NULL)
return false;
// Once we have the DLL loaded. Import those two functions.
CreateButton = (LPCREATEBUTTON)GetProcAddress(m_dllButton, "CreateButton");
DestroyButton = (LPDESTROYBUTTON)GetProcAddress(m_dllButton, "DestroyButton");
if(CreateButton == NULL || DestroyButton == NULL)
return false;
// We have obtained the functions. Let's create a button.
m_pButton = CreateButton();
if(m_pButton != NULL)
m_pButton->Create(hWnd);
return true;
}
void CloseApplication()
{
// Destroy the button.
if(m_pButton != NULL && DestroyButton != NULL)
DestroyButton(m_pButton);
m_pButton = NULL;
// Free the DLL.
FreeLibrary(m_dllButton);
m_dllButton = NULL;
}
问题和几个问题
该程序能够在其客户区域内创建和显示按钮。但每次关闭应用程序时,我的调试器都会报告内存泄漏。即使使用从CreateButton()获取的相同内存指针调用DestroyButton(...)函数,也会发生这种情况。
我没有从Eli的文章中得到的一个部分是他取消了他的对象使用的内存。在他的界面示例中,他有一个&#34; destroy&#34;方法原型由类如此实现:
void destroy()
{
delete this;
}
我通过宣布&#34; DestroyButton&#34;在IChildWindow中并且已经实现了ButtonControl。它就像一个魅力。没有内存泄漏。
所以,我的问题是为什么当我按照我的方式取消分配内存时,我会有内存泄漏,但是Eli专注于&#34;销毁&#34;方法不?
我认为,因为&#34;这个&#34;指针指向类对象或其实例,然后&#34;删除ptrButton&#34;与&#34;删除此内容相同。&#34;
另外,我从另一个编程论坛(我忘了哪里)读到,DLL有自己的内存堆与进程分开,并且DLL中分配的任何内存都必须由DLL释放。但后来在另一个论坛(我知道我谷歌很多:),他们说如果一个DLL是动态加载的,那么该进程负责释放DLL中分配的内存。
由于这些信息,我甚至尝试在CloseApplication函数中为对象(删除m_pButton)取消分配内存,但它没有解决问题。 更新:在向IChildWindow添加虚拟析构函数后,现在可以正常工作。请参阅&#34;解决方案&#34;下方。
那么,有人可以帮助我澄清一下我和我一样困惑的每一个身体 - 由拥有过程释放由动态加载的DLL分配的记忆以防止内存泄漏吗?那个静态链接的DLL怎么样?谢谢您的时间:))
的解决方案的
我按照Brandon,PaulMcKenzie和WhozCraig的建议在IChildWindow界面添加了一个虚拟析构函数。这解决了内存泄漏问题。你们真是太棒了!谢谢!
再次感谢PaulMcKenzie和drescherjm澄清,对于app和DLL使用相同的DLL版本将使用相同的堆。美好的一天! :)