我有一个使用Visual Studio 2017编译的基于MFC(C++)对话框的项目。在添加该代码时,我添加了以下代码来跟踪可能的内存泄漏:
在初始化ProjectName.cpp
派生的类之前,从CWinApp
中进行。
#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;
//....
然后,当项目运行并退出时,我触发了
WTSSendMessage
:这给了我以下输出:
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.
但是随后在下一次调试运行中,当我添加上面代码中显示的
_CrtSetBreakAlloc(75);
行时,错误75
的断点将永远不会触发,尽管输出仍保持不变。然后另一个有趣的发现是,如果我从
_CrtDumpMemoryLeaks()
析构函数中删除~CatchMemLeaks
函数,这些内存泄漏将消失。PS。我知道这对于该特定项目来说是特有的,因为如果我在基于MFC对话框的常规应用程序中尝试该操作,则不会得到相同的行为。
知道如何追踪这些泄漏的来源吗?
最佳答案
哦,开枪,我明白了。 (感谢注释中的@RbMm!)要进行的工作是使此泄漏检测代码在所有其他CRT和MFC构造函数以及其他内容之前初始化(并在之后取消初始化)。诀窍是使用#pragma init_seg(compiler)
指令。我最初的错误是在定义了.cpp
派生类的CWinApp
文件中使用它。在退出应用程序时导致崩溃,因为该#pragma
指令适用于整个.cpp
文件。
因此,解决方案是为我的.h
类创建一个单独的.cpp
和CatchMemLeaks
文件,并在其中设置该#pragma
指令,例如:CatchMemLeaks.h
文件:
#pragma once
//Only debugger builds
#ifdef _DEBUG
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#include <Strsafe.h>
#include <Wtsapi32.h>
#pragma comment(lib, "Wtsapi32.lib")
struct CatchMemLeaks{
CatchMemLeaks(int nMemLeakCodeToCatch);
~CatchMemLeaks();
};
#endif
和
CatchMemLeaks.cpp
文件:#include "StdAfx.h"
#include "CatchMemLeaks.h"
//Only debugger builds
#ifdef _DEBUG
#pragma warning( push )
#pragma warning( disable : 4074)
#pragma init_seg(compiler) //Make this code execute before any other code in this project (including other static constructors).
//This will also make its destructors run last.
//WARNING: Because of this do not call any CRT functions from this .cpp file!
#pragma warning( pop )
CatchMemLeaks cml(0); //Set to (0) to monitor memory leaks, or to any other value to break on a specific leak number
CatchMemLeaks::CatchMemLeaks(int nMemLeakNumberToBreakOn)
{
HANDLE ghDebugLogFile = ::CreateFile(.\\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);
if(nMemLeakNumberToBreakOn)
{
_CrtSetBreakAlloc(nMemLeakNumberToBreakOn);
}
}
CatchMemLeaks::~CatchMemLeaks()
{
//Dump memory leaks, if any
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);
}
}
#endif
然后最后将其包含在
stdafx.h
文件中:#include "CatchMemLeaks.h"
关于c++ - 困惑MFC项目中的内存泄漏,如果从不调用_CrtDumpMemoryLeaks(),该泄漏将消失,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/54105801/