内存泄漏涉及从DLL导出的类

时间:2014-07-21 17:10:05

标签: c++ class dll memory-leaks export

我读了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版本将使用相同的堆。美好的一天! :)

0 个答案:

没有答案