I have an MFC (C++) dialog-based project that is compiled with Visual Studio 2017. I've added the following code to track for possible memory leaks as I build it:
From within ProjectName.cpp
before my CWinApp
-derived class is initialized.
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#include <Wtsapi32.h>
#pragma comment(lib, "Wtsapi32.lib")
struct CatchMemLeaks{
CatchMemLeaks()
{
HANDLE ghDebugLogFile = ::CreateFile(L".\\dbg_output.txt",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
//Enable logging into that file
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE));
_CrtSetReportFile(_CRT_WARN, ghDebugLogFile);
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE));
_CrtSetReportFile(_CRT_ERROR, ghDebugLogFile);
_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE));
_CrtSetReportFile(_CRT_ASSERT, ghDebugLogFile);
//Try to break on the error reported
_CrtSetBreakAlloc(75);
}
~CatchMemLeaks()
{
if(_CrtDumpMemoryLeaks())
{
DWORD dwRespMsgBx;
::WTSSendMessage(NULL, ::WTSGetActiveConsoleSessionId(),
L"MemLeak", lstrlen(L"MemLeak") * sizeof(WCHAR),
L"MemLeak", lstrlen(L"MemLeak") * sizeof(WCHAR),
MB_OK | MB_ICONERROR | MB_SYSTEMMODAL,
0, &dwRespMsgBx, TRUE);
}
}
};
CatchMemLeaks cml;
//Then the usual MFC CWinApp-app derived class stuff:
// CProjectNameApp
BEGIN_MESSAGE_MAP(CProjectNameApp, CWinApp)
ON_COMMAND(ID_HELP, &CWinApp::OnHelp)
END_MESSAGE_MAP()
// CProjectNameApp construction
CProjectNameApp::CProjectNameApp()
{
// support Restart Manager
m_dwRestartManagerSupportFlags = AFX_RESTART_MANAGER_SUPPORT_RESTART;
// TODO: add construction code here,
// Place all significant initialization in InitInstance
}
// The one and only CProjectNameApp object
CProjectNameApp theApp;
//....
Then when the project runs and then exits, I'm getting my WTSSendMessage
triggered:
Which gives me the following output:
Detected memory leaks!
Dumping objects ->
{75} normal block at 0x0000029BA5EA75D0, 16 bytes long.
Data: < G > B0 86 D0 47 F7 7F 00 00 00 00 00 00 00 00 00 00
{74} normal block at 0x0000029BA5ECE930, 48 bytes long.
Data: <0 0 > 30 E9 EC A5 9B 02 00 00 30 E9 EC A5 9B 02 00 00
{73} normal block at 0x0000029BA5EA82F0, 16 bytes long.
Data: <p G > 70 86 D0 47 F7 7F 00 00 00 00 00 00 00 00 00 00
{72} normal block at 0x0000029BA5ECEA80, 48 bytes long.
Data: < > 80 EA EC A5 9B 02 00 00 80 EA EC A5 9B 02 00 00
{71} normal block at 0x0000029BA5EA8070, 16 bytes long.
Data: < G > 20 86 D0 47 F7 7F 00 00 00 00 00 00 00 00 00 00
{70} normal block at 0x0000029BA5E98BA0, 120 bytes long.
Data: < > A0 8B E9 A5 9B 02 00 00 A0 8B E9 A5 9B 02 00 00
Object dump complete.
But then on the next debug run, when I add the _CrtSetBreakAlloc(75);
line showed in the code above, the breakpoint on error 75
never triggers, although the output still remains the same.
Then another interesting discovery is that if I remove the _CrtDumpMemoryLeaks()
function from my ~CatchMemLeaks
destructor, those memory leaks go away.
PS. I know that this is something peculiar for this particular project because I don't get the same behavior if I try it with a stock MFC-dialog-based app.
Any idea how to track where those leaks are coming from?
Oh shoot, I got it. (Thanks to @RbMm in the comments!) The catch is to make this leak detecting code initialize before (and un-initialize after) all other CRT and MFC constructors and other stuff. The trick is to use
#pragma init_seg(compiler)
directive. My original mistake was to use it in the.cpp
file where theCWinApp
-derived class was defined. That caused a crash when the app was exiting because that#pragma
directive applies to the entire.cpp
file.So the solution is to create a separate
.h
and.cpp
files for myCatchMemLeaks
class and set that#pragma
directive there, as such:CatchMemLeaks.h
file:and
CatchMemLeaks.cpp
file:then lastly include it in the
stdafx.h
file:It's most likely that your
CatchMemLeaks
class is created and then destroyed before all other objects in your program are cleaned up, so in effect you're reporting a false-positive (the other objects will get cleaned up after)But it's hard to tell without a fully running program.