困惑MFC项目中的内存泄漏,如果从不调用_CrtDumpMemoryLeaks(),该泄漏将消失 [英] Puzzled over memory leaks in MFC project that disappear if _CrtDumpMemoryLeaks() is never called

查看:78
本文介绍了困惑MFC项目中的内存泄漏,如果从不调用_CrtDumpMemoryLeaks(),该泄漏将消失的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个使用Visual Studio 2017编译的基于MFC(C ++)对话框的项目.我添加了以下代码来跟踪在构建时可能发生的内存泄漏:

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:

CWinApp派生类初始化之前,在ProjectName.cpp内部.

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;

//....

然后,当项目运行然后退出时,我触发了WTSSendMessage:

Then when the project runs and then exits, I'm getting my WTSSendMessage triggered:

哪个给了我以下输出:

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的断点将永远不会触发,尽管输出仍然保持不变.

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.

另一个有趣的发现是,如果我从~CatchMemLeaks析构函数中删除了_CrtDumpMemoryLeaks()函数,这些内存泄漏将消失.

Then another interesting discovery is that if I remove the _CrtDumpMemoryLeaks() function from my ~CatchMemLeaks destructor, those memory leaks go away.

PS.我知道这对于该特定项目来说是特有的,因为如果我在基于MFC对话框的普通应用程序中尝试该应用程序,将不会得到相同的行为.

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?

推荐答案

哦,我明白了. (感谢注释中的 @RbMm !)要注意的是使此泄漏检测代码在初始化之前(并取消初始化)之后)所有其他CRT和MFC构造函数以及其他内容.诀窍是使用#pragma init_seg(compiler)指令.我最初的错误是在定义了CWinApp的类的.cpp文件中使用它.在退出应用程序时导致崩溃,因为该#pragma指令适用于整个.cpp文件.

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 the CWinApp-derived class was defined. That caused a crash when the app was exiting because that #pragma directive applies to the entire .cpp file.

因此解决方案是为我的CatchMemLeaks类创建单独的.h.cpp文件,并在其中设置该#pragma指令,例如:

So the solution is to create a separate .h and .cpp files for my CatchMemLeaks class and set that #pragma directive there, as such:

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文件中:

then lastly include it in the stdafx.h file:

#include "CatchMemLeaks.h"  

这篇关于困惑MFC项目中的内存泄漏,如果从不调用_CrtDumpMemoryLeaks(),该泄漏将消失的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆