I load a dll with win32 LoadLibrary, and when I am done with it, I call FreeLibrary, destorying all the memory allocated in the dll ect... Actually, memory leak problem only occures with std containers. It seems they are not willing to release their memory on destroy. Here is the code that leaks.
namespace ToolKit
{
class Game : public GamePlugin
{
public:
void Init(ToolKit::Main* master);
void Destroy();
void Frame(float deltaTime, Viewport* viewport);
void Resize(int width, int height);
void Event(SDL_Event event);
std::vector<int> point; // If I remove this line, no leaks are reported.
};
}
extern "C" TK_GAME_API ToolKit::Game * __stdcall GetInstance()
{
return new ToolKit::Game(); // Instance is deleted in the caller process than FreeLibrary() is called.
}
All functions are no-op in GamePlugin, and the process is not reporting any memory issue if there is no std container. I trap the leak in here. For the completion, I am sharing my standart CRT memory dump code.
int main(int argc, char* argv[])
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
return ToolKit_Main(argc, argv);
}
Load and unload code for the dll
void PluginManager::Load(const String& name)
{
HINSTANCE hinstLib;
TKPROC ProcAdd;
BOOL fRunTimeLinkSuccess = FALSE;
String dllName = name;
hinstLib = LoadLibrary(dllName.c_str());
if (hinstLib != NULL)
{
m_moduleHandle = (void*)hinstLib;
ProcAdd = (TKPROC)GetProcAddress(hinstLib, "GetInstance");
if (NULL != ProcAdd)
{
fRunTimeLinkSuccess = TRUE;
m_plugin = (ProcAdd)();
m_plugin->Init(ToolKit::Main::GetInstance());
}
}
if (!fRunTimeLinkSuccess)
{
m_reporterFn("Can not load plugin module " + dllName);
}
}
void PluginManager::Unload()
{
if (m_plugin)
{
m_plugin->Destroy();
SafeDel(m_plugin);
}
if (m_moduleHandle)
{
FreeLibrary((HINSTANCE)m_moduleHandle);
m_moduleHandle = nullptr;
}
}
Inorder to add some more clarification to the question, I am describing the program flow here:
Here is the minimalcase that reproduces the leak. Process side:
#include <stdio.h>
#include <cstdlib>
#include <crtdbg.h>
#include <string>
#include <functional>
#include <Windows.h>
#include "Plugin.h"
using namespace std;
class PluginManager
{
public:
void Load(const string& plugin);
void Unload();
public:
GamePlugin* m_plugin = nullptr;
void* m_moduleHandle = nullptr;
};
typedef GamePlugin* (__cdecl* TKPROC)();
void PluginManager::Load(const string& name)
{
HINSTANCE hinstLib;
TKPROC ProcAdd;
hinstLib = LoadLibrary(name.c_str());
if (hinstLib != NULL)
{
m_moduleHandle = (void*)hinstLib;
ProcAdd = (TKPROC)GetProcAddress(hinstLib, "GetInstance");
if (NULL != ProcAdd)
{
m_plugin = (ProcAdd)();
m_plugin->Init();
}
}
}
void PluginManager::Unload()
{
if (m_plugin)
{
m_plugin->Destroy();
delete m_plugin;
}
if (m_moduleHandle)
{
FreeLibrary((HINSTANCE)m_moduleHandle);
m_moduleHandle = nullptr;
}
}
int main(int argc, char* argv[])
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
PluginManager* pm = new PluginManager();
pm->Load("plugin.dll");
pm->Unload();
delete pm;
return 0;
}
Plugin interface:
#pragma once
#ifdef _WIN32
# ifdef TK_EXPORTS
# define TK_GAME_API __declspec(dllexport)
# else
# define TK_GAME_API __declspec(dllimport)
# endif
#elif
# define TK_GAME_API
#endif
struct GamePlugin
{
virtual void Init() = 0;
virtual void Destroy() = 0;
virtual void Frame() = 0;
};
DLL side:
#define TK_EXPORTS
#include "Plugin.h"
#include <vector>
class Game : public GamePlugin
{
public:
void Init() {}
void Destroy() {}
void Frame() {}
std::vector<int> point;
};
extern "C" TK_GAME_API GamePlugin * __stdcall GetInstance()
{
return new Game();
}
Exactly the same, if we remove the std::vector<int> point
, there is no leak.
As can be seen in the minimal case, I am creating the plugin instance in the dll however deleting it in the process / executable. Eventough the memory is being freed, it gets reported as leak because that memory allocated within the dll and not freed there. Wow what a hassle was that ....
There are better ways of doing this, but in case some one lives the same problem, here is a quick fix.
Process:
void PluginManager::Unload()
{
if (m_plugin)
{
m_plugin->Destroy();
m_plugin = nullptr; // Clear the memory in where it is allocated.
}
if (m_moduleHandle)
{
FreeLibrary((HINSTANCE)m_moduleHandle);
m_moduleHandle = nullptr;
}
}
DLL:
void Destroy() { delete this; } // Now CRT Debug is happy. No false leak repots.