【发布时间】:2019-06-03 23:19:53
【问题描述】:
我有一个使用 Visual Studio 2017 编译的基于 MFC (C++) 对话框的项目。我添加了以下代码以在构建它时跟踪可能的内存泄漏:
在我的CWinApp 派生类被初始化之前,从ProjectName.cpp 内部开始。
#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 上的断点永远不会触发,尽管输出仍然保持不变。
另一个有趣的发现是,如果我从 ~CatchMemLeaks 析构函数中删除 _CrtDumpMemoryLeaks() 函数,那些内存泄漏就会消失。
PS。我知道这是这个特定项目的特殊之处,因为如果我使用基于 MFC 对话框的股票应用程序尝试它,我不会得到相同的行为。
知道如何跟踪这些泄漏的来源吗?
【问题讨论】:
-
我无法复制泄漏。您尚未展示完整的 MCVE。也可以使用
CWinAppEx而不是CWinApp,这在使用框架窗口时会有所不同。 -
@BarmakShemirani:哦,等等,我才意识到。谢谢。该类派生自
CWinApp。自从 MFC wizzard 创建它以来,我没有更改它。虽然现在当我将CWinApp替换为CWinAppEx时,我在ENSURE(afxShellManager == NULL);这一行的CShellManager构造函数中得到了一个断言。你知道我该如何设置afxShellManager吗?这可能是我的问题。 -
好的,找到导致
ENSURE(afxShellManager == NULL)断言的原因。如果我将CWinApp替换为CWinAppEx,MFC 在InitInstance()中有这行会触发它:CShellManager *pShellManager = new CShellManager;将其注释掉会删除断言。我不知道它做了什么,但它不“喜欢”CWinAppEx-派生类。不过,不幸的是,这并不能解决原来的问题...... -
再说一次关于您复制问题的问题。我也不能在股票项目中做到这一点。这就是具有挑战性的原因......
-
在
CatchMemLeaks cml;之前使用最小的#pragma init_seg(compiler )forcml在 exe 中构造第一个并最后销毁
标签: c++ winapi memory-leaks mfc visual-studio-2017