有没有地方放置一个断点来捕获GDI句柄的所有分配/解除分配?

时间:2015-04-08 21:20:23

标签: winapi visual-c++ visual-studio-2013

我正在尝试确定Windows应用程序中GDI泄漏的来源,如果我可以跟踪GDI句柄的所有分配和解除分配,这将有所帮助。是否有一些函数可以设置断点,以便我可以确定何时创建GDI句柄以及何时将其销毁?

我正在使用VC ++和Visual Studio 2013。

我找到了these,但如果能得到一个用于获取/释放所有GDI句柄的低级函数,我会更喜欢它。我试图追踪一些集会但没有成功。

2 个答案:

答案 0 :(得分:1)

我已经忘记了@ {DavidHefferman建议的Detouring(感谢大卫的建议)。我以前从未尝试过这个,但去年我偶然发现了它。因此,经过一天的试验和错误以确保类型正确,这是一种几乎完整的方法来实现这一点。

#include <utility>
#include <map>
#include <tuple>
#include <type_traits>
#include "detours.h"

namespace
{
    // To allow for a consistent type for mapping a function pointer
    typedef void(WINAPI *winApiFnPtr)();

    // Storage for funcion info
    struct detourInfo
    {
        LPCSTR fnName;
        LPCSTR typeName;
        winApiFnPtr pOriginalFn;
        winApiFnPtr pTrampolineFn;
        winApiFnPtr pDetourFn;
        detourInfo()
        {
        }

        detourInfo(LPCSTR fnName, LPCSTR typeName, winApiFnPtr pOriginalFn, winApiFnPtr pDetourFn)
            : fnName(fnName)
            , typeName(typeName)
            , pOriginalFn(pOriginalFn)
            , pTrampolineFn(pOriginalFn)
            , pDetourFn(pDetourFn)
        {
        }
    };

    // Stores all the function pointers and other info used by detour.
    std::map<winApiFnPtr, detourInfo> detourFnToInfo;

    // Stores the type of the handle
    std::map<void*, LPCSTR> handleToType;

    // Cast a function pointer to a void *
    template <typename RET, typename...ARGs>
    void* fnToVoidPtr(RET(WINAPI* pOriginalFunction)(ARGs...))
    {
        return (void*)pOriginalFunction;
    }

    // Cast a function double pointer to a void **
    template <typename RET, typename...ARGs>
    void** fnPtrPtrToVoidPtrPtr(RET(WINAPI** pOriginalFunction)(ARGs...))
    {
        return (void**)pOriginalFunction;
    }

    // Sets a value in a map but will assert if value is already there for the
    // given key.
    template <typename K, typename V>
    void set(std::map<K, V>& map, K const& key, V& value)
    {
        auto found = map.find(key);
        ASSERT(found == map.end());
        map[key] = value;
    }

    // Erases a value key pair but will asserts if it doesn't exist.
    template <typename K, typename V>
    void erase(std::map<K, V>& map, K const& key)
    {
        auto found = map.find(key);
        ASSERT(found != map.end());
        map.erase(found);
    }

    UINT fn_ReportIndent = 0;

    // used for type erasure
    void WINAPI fn_CreateReport(void* result, detourInfo const& info, DWORD guiResources)
    {
        TRACE("Created % *sv %s(%p) using function %s. %d handles used.\n"
            , fn_ReportIndent, ""
            , info.typeName
            , result
            , info.fnName
            , GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS) - guiResources);
        if (handleToType.find((void*)result) != handleToType.end())
        {
            TRACE("% *sWARNING: Didn't find a delete for GDI handle %p of type %s\n"
                , fn_ReportIndent + 10, ""
                , (void*)result
                , info.typeName);
        }
        else
        {
            ++fn_ReportIndent;
        }
        handleToType[(void*)result] = info.typeName;
    }

    template<int DESTROY_PARAM, typename...ARGs>
    static typename std::enable_if<(DESTROY_PARAM == -1)>::type fn_DeleteReportFromCreate(ARGs..., detourInfo const&, BOOL, DWORD)
    {
        // Nothing to do here.
    }

    template<int DESTROY_PARAM, typename...ARGs>
    static typename std::enable_if<(DESTROY_PARAM  > -1) > ::type fn_DeleteReportFromCreate(ARGs...args, detourInfo const& info, BOOL result, DWORD guiResources)
    {
        // The creation is done when an object is deleted.
        // Report the deleted information.
        fn_DeleteReport(std::get<DESTROY_PARAM>(std::make_tuple(args...)), info, result, guiResources);
    }

    // My detour function for a function that creates an object.
    template <int UNIQUE_ID, int DESTROY_PARAM, typename RET, typename...ARGs>
    auto WINAPI fn_Create(ARGs...args) -> RET
    {
        winApiFnPtr pDetourFunction = (winApiFnPtr)fn_Create < UNIQUE_ID, DESTROY_PARAM, RET, ARGs... >;
        typedef RET(WINAPI * fnPtr_t)(ARGs...);
        detourInfo const& info = detourFnToInfo[pDetourFunction];
        DWORD guiResources = GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS);
        fnPtr_t pOriginalFunction = (fnPtr_t)info.pTrampolineFn;
        RET result = pOriginalFunction(args...);
        // This should work, but has not been tested.
        fn_DeleteReportFromCreate<DESTROY_PARAM, ARGs...>(args..., info, (BOOL)result, guiResources);
        fn_CreateReport((void*)result, info, guiResources);
        return result;
    }

    // used for type erasure
    void fn_EnableCreateDetour(winApiFnPtr pOriginalFunction, winApiFnPtr pDetourFunction, LPCSTR fnName, LPCSTR typeName)
    {
        // if assert occurs within this function, change the UNIQUE_ID to something unique
        set(detourFnToInfo, pDetourFunction, detourInfo(fnName, typeName, (winApiFnPtr)pOriginalFunction, pDetourFunction));

        VERIFY(DetourTransactionBegin() == NO_ERROR);
        VERIFY(DetourUpdateThread(GetCurrentThread()) == NO_ERROR);
        VERIFY(DetourAttach(fnPtrPtrToVoidPtrPtr(&detourFnToInfo[pDetourFunction].pTrampolineFn), fnToVoidPtr(pDetourFunction)) == NO_ERROR);
        VERIFY(DetourTransactionCommit() == NO_ERROR);
    }

    // Enables a detour function for a create command.
    template <int UNIQUE_ID, int DESTROY_PARAM = -1, typename RET, typename...ARGs>
    void fn_EnableCreateDetour(RET(WINAPI * pOriginalFunction)(ARGs...), LPCSTR fnName, LPCSTR typeName)
    {
        winApiFnPtr pDetourFunction = (winApiFnPtr)fn_Create < UNIQUE_ID, DESTROY_PARAM, RET, ARGs... >;
        fn_EnableCreateDetour((winApiFnPtr)pOriginalFunction, pDetourFunction, fnName, typeName);
    }

    // used for type erasure
    void fn_DisableCreateDetour(winApiFnPtr pDetourFunction)
    {
        VERIFY(DetourTransactionBegin() == NO_ERROR);
        VERIFY(DetourUpdateThread(GetCurrentThread()) == NO_ERROR);
        VERIFY(DetourDetach(fnPtrPtrToVoidPtrPtr(&detourFnToInfo[pDetourFunction].pTrampolineFn), fnToVoidPtr(pDetourFunction)) == NO_ERROR);
        VERIFY(DetourTransactionCommit() == NO_ERROR);

        erase(detourFnToInfo, pDetourFunction);
    }

    // Disables a detour function for a create command.
    template <int UNIQUE_ID, typename RET, typename...ARGs>
    void fn_DisableCreateDetour(RET(WINAPI * pOriginalFunction)(ARGs...))
    {
        winApiFnPtr pDetourFunction = (winApiFnPtr)fn_Create < UNIQUE_ID, RET, ARGs... >;
        fn_DisableCreateDetour(pDetourFunction);
    }

    // used for type erasure
    void WINAPI fn_DeleteReport(void * handle, detourInfo const& info, BOOL result, DWORD guiResources)
    {
        auto found = handleToType.find(handle);
        if (found != handleToType.end())
        {
            TRACE("Deleted % *s^ %s(%p) using function %s. Success = %d. %d handles released.\n"
                , --fn_ReportIndent, ""
                , found->second
                , handle
                , info.fnName
                , result
                , guiResources - GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS));
            handleToType.erase(found);
        }
        else
        {
            TRACE("Deleted % *s| UNKNOWN(%p) using function %s. Success = %d. %d handles released.\n"
                , fn_ReportIndent, ""
                , handle
                , info.fnName
                , result
                , guiResources - GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS));
        }
    }

    // My detour function for a function that destroys an object.
    template <int UNIQUE_ID, int DESTROY_PARAM, typename...ARGs>
    auto WINAPI fn_Delete(ARGs... args) -> BOOL
    {
        typedef BOOL(WINAPI * fnPtr_t)(ARGs...);
        winApiFnPtr pDetourFunction = (winApiFnPtr)fn_Delete < UNIQUE_ID, DESTROY_PARAM, ARGs... >;
        detourInfo const& info = detourFnToInfo[pDetourFunction];
        DWORD guiResources = GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS);
        fnPtr_t pOriginalFunction = (fnPtr_t)info.pTrampolineFn;
        BOOL result = pOriginalFunction(args...);
        void* handle = (void*)std::get<DESTROY_PARAM>(std::make_tuple(args...));
        fn_DeleteReport(handle, info, result, guiResources);
        return result;
    }

    // used for type erasure
    void fn_EnableDestroyDetour(winApiFnPtr pOriginalFunction, winApiFnPtr pDetourFunction, LPCSTR fnName)
    {
        auto found = detourFnToInfo.find(pDetourFunction);
        if (found == detourFnToInfo.end())
        {
            set(detourFnToInfo, pDetourFunction, detourInfo(fnName, "", (winApiFnPtr)pOriginalFunction, pDetourFunction));

            VERIFY(DetourTransactionBegin() == NO_ERROR);
            VERIFY(DetourUpdateThread(GetCurrentThread()) == NO_ERROR);
            VERIFY(DetourAttach(fnPtrPtrToVoidPtrPtr(&detourFnToInfo[pDetourFunction].pTrampolineFn), fnToVoidPtr(pDetourFunction)) == NO_ERROR);
            VERIFY(DetourTransactionCommit() == NO_ERROR);
        }
        else
        {
            ASSERT(found->second.pOriginalFn == (winApiFnPtr)pOriginalFunction);
            ASSERT(found->second.fnName == fnName);
        }
    }

    // Enables a detour function for a destroy command.
    template <int UNIQUE_ID, int DESTROY_PARAM, typename...ARGs>
    void fn_EnableDestroyDetour(BOOL(WINAPI * pOriginalFunction)(ARGs...), LPCSTR fnName)
    {
        winApiFnPtr pDetourFunction = (winApiFnPtr)fn_Delete < UNIQUE_ID, DESTROY_PARAM, ARGs... >;
        fn_EnableDestroyDetour((winApiFnPtr)pOriginalFunction, pDetourFunction, fnName);
    }

    // used for type erasure
    void fn_DisableDestroyDetour(winApiFnPtr pDetourFunction)
    {
        auto found = detourFnToInfo.find(pDetourFunction);
        if (found != detourFnToInfo.end())
        {
            VERIFY(DetourTransactionBegin() == NO_ERROR);
            VERIFY(DetourUpdateThread(GetCurrentThread()) == NO_ERROR);
            VERIFY(DetourDetach(fnPtrPtrToVoidPtrPtr(&detourFnToInfo[pDetourFunction].pTrampolineFn), fnToVoidPtr(pDetourFunction)) == NO_ERROR);
            VERIFY(DetourTransactionCommit() == NO_ERROR);

            erase(detourFnToInfo, pDetourFunction);
        }
    }

    // Disables a detour function for a destroy command.
    template <int UNIQUE_ID, int DESTROY_PARAM, typename...ARGs>
    void fn_DisableDestroyDetour(BOOL(WINAPI * pOriginalFunction)(ARGs...))
    {
        winApiFnPtr pDetourFunction = (winApiFnPtr)fn_Delete < UNIQUE_ID, DESTROY_PARAM, ARGs... >;
        fn_DisableDestroyDetour(pDetourFunction);
    }
}

void captureAllGdiCalls()
{
    fn_EnableCreateDetour<0>(CreateBitmap, "CreateBitmap", "HBITMAP");
    fn_EnableCreateDetour<0>(CreateBitmapIndirect, "CreateBitmapIndirect", "HBITMAP");
    fn_EnableCreateDetour<0>(CreateCompatibleBitmap, "CreateCompatibleBitmap", "HBITMAP");
    fn_EnableCreateDetour<0>(CreateDIBitmap, "CreateDIBitmap", "HBITMAP");
    fn_EnableCreateDetour<0>(CreateDIBSection, "CreateDIBSection", "HBITMAP");
    fn_EnableCreateDetour<1>(CreateDiscardableBitmap, "CreateDiscardableBitmap", "HBITMAP");
    fn_EnableDestroyDetour<0, 0>(DeleteObject, "DeleteObject");

    fn_EnableCreateDetour<0>(CreateIcon, "CreateIcon", "HICON");
    fn_EnableCreateDetour<0>(CopyIcon, "CopyIcon", "HICON");
    fn_EnableCreateDetour<0>(CreateIconFromResource, "CreateIconFromResource", "HICON");
    fn_EnableCreateDetour<0>(CreateIconFromResourceEx, "CreateIconFromResourceEx", "HICON");
    fn_EnableCreateDetour<0>(CreateIconIndirect, "CreateIconIndirect", "HICON");
    fn_EnableDestroyDetour<0, 0>(DestroyIcon, "DestroyIcon");

    fn_EnableCreateDetour<0>(CreateBrushIndirect, "CreateBrushIndirect", "HBRUSH");
    fn_EnableCreateDetour<0>(CreateDIBPatternBrush, "CreateDIBPatternBrush", "HBRUSH");
    fn_EnableCreateDetour<0>(CreateDIBPatternBrushPt, "CreateDIBPatternBrushPt", "HBRUSH");
    fn_EnableCreateDetour<0>(CreateHatchBrush, "CreateHatchBrush", "HBRUSH");
    fn_EnableCreateDetour<0>(CreatePatternBrush, "CreatePatternBrush", "HBRUSH");
    fn_EnableCreateDetour<0>(CreateSolidBrush, "CreateSolidBrush", "HBRUSH");
    fn_EnableDestroyDetour<0, 0>(DeleteObject, "DeleteObject");

    fn_EnableCreateDetour<0>(CreateDCA, "CreateDCA", "HDC");
    fn_EnableDestroyDetour<0, 0>(DeleteDC, "DeleteDC");
    fn_EnableDestroyDetour<0, 1>(ReleaseDC, "ReleaseDC");

    fn_EnableCreateDetour<0>(CreateEnhMetaFileA, "CreateEnhMetaFileA", "HDC");
    fn_EnableCreateDetour<0, 0>(CloseEnhMetaFile, "CloseEnhMetaFile", "HENHMETAFILE"); // closing the HDC metafile creates an HENHMETAFILE
    fn_EnableDestroyDetour<0, 0>(DeleteEnhMetaFile, "DeleteEnhMetaFile"); // deleting the HENHMETAFILE deletes it all

    fn_EnableCreateDetour<0>(CreateFontA, "CreateFontA", "HFONT");
    fn_EnableCreateDetour<0>(CreateFontIndirectA, "CreateFontIndirectA", "HFONT");
    fn_EnableCreateDetour<0>(CreateFontIndirectExA, "CreateFontIndirectExA", "HFONT");
    fn_EnableDestroyDetour<0, 0>(DeleteObject, "DeleteObject");

    fn_EnableCreateDetour<0>(CreateCompatibleDC, "CreateCompatibleDC", "HDC");
    fn_EnableDestroyDetour<0, 0>(DeleteDC, "DeleteDC");

    fn_EnableCreateDetour<0>(CreateMetaFileA, "CreateMetaFileA", "HDC");
    fn_EnableCreateDetour<0, 0>(CloseMetaFile, "CloseMetaFile", "HMETAFILE"); // closing the HDC metafile creates an HMETAFILE
    fn_EnableDestroyDetour<0, 0>(DeleteMetaFile, "DeleteMetaFile"); // deleting the HMETAFILE deletes it all

    fn_EnableCreateDetour<0>(CreatePalette, "CreatePalette", "HPALETTE");
    fn_EnableDestroyDetour<0, 0>(DeleteObject, "DeleteObject");

    fn_EnableCreateDetour<0>(CreatePen, "CreatePen", "HPEN");
    fn_EnableCreateDetour<0>(CreatePenIndirect, "CreatePenIndirect", "HPEN");
    fn_EnableCreateDetour<0>(ExtCreatePen, "ExtCreatePen", "HPEN");
    fn_EnableDestroyDetour<0, 0>(DeleteObject, "DeleteObject");

    WINGDIAPI int     WINAPI CombineRgn(_In_opt_ HRGN hrgnDst, _In_opt_ HRGN hrgnSrc1, _In_opt_ HRGN hrgnSrc2, _In_ int iMode);
    fn_EnableCreateDetour<0>(CreateEllipticRgn, "CreateEllipticRgn", "HRGN");
    fn_EnableCreateDetour<0>(CreateEllipticRgnIndirect, "CreateEllipticRgnIndirect", "HRGN");
    fn_EnableCreateDetour<0>(CreatePolygonRgn, "CreatePolygonRgn", "HRGN");
    fn_EnableCreateDetour<1>(CreateRectRgn, "CreateRectRgn", "HRGN");
    fn_EnableCreateDetour<2>(CreateRectRgnIndirect, "CreateRectRgnIndirect", "HRGN");
    fn_EnableCreateDetour<3>(CreateRoundRectRgn, "CreateRoundRectRgn", "HRGN");
    fn_EnableCreateDetour<4>(ExtCreateRegion, "ExtCreateRegion", "HRGN");
    fn_EnableCreateDetour<5>(PathToRegion, "PathToRegion", "HRGN");
    fn_EnableDestroyDetour<0, 0>(DeleteObject, "DeleteObject");
}

void stopCaptureOfAllGdiCalls()
{
    for (auto& info : detourFnToInfo)
    {
        VERIFY(DetourTransactionBegin() == NO_ERROR);
        VERIFY(DetourUpdateThread(GetCurrentThread()) == NO_ERROR);
        VERIFY(DetourDetach(fnPtrPtrToVoidPtrPtr(&info.second.pTrampolineFn), fnToVoidPtr(info.second.pDetourFn)) == NO_ERROR);
        VERIFY(DetourTransactionCommit() == NO_ERROR);
    }
    detourFnToInfo.clear();
}

void showAllTrackedHandles()
{
    TRACE("Active GDI handles (%d):\n", handleToType.size());
    UINT counter = 0;
    for (auto handleTypeTuple : handleToType)
    {
        TRACE("  % 7u: %p %s\n", ++counter, handleTypeTuple.first, handleTypeTuple.second);
    }
    TRACE("End of active GDI handles\n");
}

void clearAllTrackedHandles()
{
    handleToType.clear();
}

我说几乎完整是因为:

  1. 似乎并非document中描述的所有函数实际上都使用了GDI句柄,但这可能会获得预先分配的库存对象。
  2. 同样,所有指定的函数都不是唯一分配GDI句柄的函数,因为有些函数显示为UNKNOWN。
  3. 无论如何,这应该让我找到泄漏的方法。我只是在应该分配GDI对象的操作之前调用captureAllGdiCalls(),当我认为已经分配的所有GDI元素都应该被释放时,我调用stopCaptureOfAllGdiCalls()。通过调用showAllTrackedHandles()列出剩余的句柄及其类型,并通过调用clearAllTrackedHandles()来清除它们。然后,只需要尝试获取堆栈跟踪以查看应该销毁的句柄的位置,并找出原因。

    对于那些感兴趣的人,以下是跟踪输出的示例:

    d:\projects\test\test\test.cpp(174) : atlTraceGeneral - Created v HBITMAP(73052029) using function CreateDIBitmap. 1 handles used.
    d:\projects\test\test\test.cpp(174) : atlTraceGeneral - Created  v HBITMAP(C5050792) using function CreateDIBitmap. 1 handles used.
    d:\projects\test\test\test.cpp(280) : atlTraceGeneral - Deleted   | UNKNOWN(DA014EBF) using function ReleaseDC. Success = 1. 1 handles released.
    d:\projects\test\test\test.cpp(174) : atlTraceGeneral - Created   v HDC(93011E42) using function CreateCompatibleDC. 1 handles used.
    d:\projects\test\test\test.cpp(174) : atlTraceGeneral - Created    v HBITMAP(CE051F02) using function CreateBitmap. 1 handles used.
    d:\projects\test\test\test.cpp(269) : atlTraceGeneral - Deleted    ^ HBITMAP(C5050792) using function DeleteObject. Success = 1. 1 handles released.
    d:\projects\test\test\test.cpp(269) : atlTraceGeneral - Deleted   ^ HDC(93011E42) using function DeleteDC. Success = 1. 1 handles released.
    d:\projects\test\test\test.cpp(174) : atlTraceGeneral - Created   v HBITMAP(170564FF) using function CreateDIBitmap. 1 handles used.
    d:\projects\test\test\test.cpp(174) : atlTraceGeneral - Created    v HBITMAP(27051EDB) using function CreateDIBitmap. 1 handles used.
    d:\projects\test\test\test.cpp(280) : atlTraceGeneral - Deleted     | UNKNOWN(DA014EBF) using function ReleaseDC. Success = 1. 1 handles released.
    d:\projects\test\test\test.cpp(174) : atlTraceGeneral - Created     v HDC(C3012732) using function CreateCompatibleDC. 1 handles used.
    d:\projects\test\test\test.cpp(174) : atlTraceGeneral - Created      v HBITMAP(2D051DFA) using function CreateBitmap. 1 handles used.
    d:\projects\test\test\test.cpp(269) : atlTraceGeneral - Deleted      ^ HBITMAP(27051EDB) using function DeleteObject. Success = 1. 1 handles released.
    d:\projects\test\test\test.cpp(269) : atlTraceGeneral - Deleted     ^ HDC(C3012732) using function DeleteDC. Success = 1. 1 handles released.
    d:\projects\test\test\test.cpp(280) : atlTraceGeneral - Deleted     | UNKNOWN(C7050792) using function DeleteObject. Success = 1. 1 handles released.
    d:\projects\test\test\test.cpp(280) : atlTraceGeneral - Deleted     | UNKNOWN(ED051E2A) using function DeleteObject. Success = 1. 1 handles released.
    d:\projects\test\test\test.cpp(280) : atlTraceGeneral - Deleted     | UNKNOWN(018503F1) using function DeleteObject. Success = 1. 0 handles released.
    d:\projects\test\test\test.cpp(280) : atlTraceGeneral - Deleted     | UNKNOWN(211064D6) using function DeleteObject. Success = 1. 2 handles released.
    d:\projects\test\test\test.cpp(280) : atlTraceGeneral - Deleted     | UNKNOWN(FF1041E0) using function DeleteObject. Success = 1. 0 handles released.
    d:\projects\test\test\test.cpp(280) : atlTraceGeneral - Deleted     | UNKNOWN(DA014EBF) using function ReleaseDC. Success = 1. 1 handles released.
    d:\projects\test\test\test.cpp(280) : atlTraceGeneral - Deleted     | UNKNOWN(DA014EBF) using function ReleaseDC. Success = 1. 1 handles released.
    d:\projects\test\test\test.cpp(280) : atlTraceGeneral - Deleted     | UNKNOWN(DA014EBF) using function ReleaseDC. Success = 1. 1 handles released.
    d:\projects\test\test\test.cpp(280) : atlTraceGeneral - Deleted     | UNKNOWN(DA014EBF) using function ReleaseDC. Success = 1. 1 handles released.
    d:\projects\test\test\test.cpp(280) : atlTraceGeneral - Deleted     | UNKNOWN(DA014EBF) using function ReleaseDC. Success = 1. 1 handles released.
    d:\projects\test\test\test.cpp(280) : atlTraceGeneral - Deleted     | UNKNOWN(DA014EBF) using function ReleaseDC. Success = 1. 1 handles released.
    d:\projects\test\test\test.cpp(280) : atlTraceGeneral - Deleted     | UNKNOWN(DA014EBF) using function ReleaseDC. Success = 1. 1 handles released.
    d:\projects\test\test\test.cpp(280) : atlTraceGeneral - Deleted     | UNKNOWN(DA014EBF) using function ReleaseDC. Success = 1. 1 handles released.
    d:\projects\test\test\test.cpp(174) : atlTraceGeneral - Created     v HBRUSH(221064D6) using function CreatePatternBrush. 2 handles used.
    d:\projects\test\test\test.cpp(280) : atlTraceGeneral - Deleted      | UNKNOWN(DA014EBF) using function ReleaseDC. Success = 1. 1 handles released.
    d:\projects\test\test\test.cpp(174) : atlTraceGeneral - Created      v HBRUSH(001041E0) using function CreateSolidBrush. 0 handles used.
    d:\projects\test\test\test.cpp(280) : atlTraceGeneral - Deleted       | UNKNOWN(DA014EBF) using function ReleaseDC. Success = 1. 1 handles released.
    d:\projects\test\test\test.cpp(280) : atlTraceGeneral - Deleted       | UNKNOWN(DA014EBF) using function ReleaseDC. Success = 1. 1 handles released.
    d:\projects\test\test\test.cpp(280) : atlTraceGeneral - Deleted       | UNKNOWN(DA014EBF) using function ReleaseDC. Success = 1. 1 handles released.
    d:\projects\test\test\test.cpp(280) : atlTraceGeneral - Deleted       | UNKNOWN(DA014EBF) using function ReleaseDC. Success = 1. 1 handles released.
    d:\projects\test\test\test.cpp(280) : atlTraceGeneral - Deleted       | UNKNOWN(DA014EBF) using function ReleaseDC. Success = 1. 1 handles released.
    d:\projects\test\test\test.cpp(280) : atlTraceGeneral - Deleted       | UNKNOWN(DA014EBF) using function ReleaseDC. Success = 1. 1 handles released.
    d:\projects\test\test\test.cpp(280) : atlTraceGeneral - Deleted       | UNKNOWN(DA014EBF) using function ReleaseDC. Success = 1. 1 handles released.
    d:\projects\test\test\test.cpp(280) : atlTraceGeneral - Deleted       | UNKNOWN(DA014EBF) using function ReleaseDC. Success = 1. 1 handles released.
    d:\projects\test\test\test.cpp(280) : atlTraceGeneral - Deleted       | UNKNOWN(DA014EBF) using function ReleaseDC. Success = 1. 1 handles released.
    d:\projects\test\test\test.cpp(280) : atlTraceGeneral - Deleted       | UNKNOWN(DA014EBF) using function ReleaseDC. Success = 1. 1 handles released.
    d:\projects\test\test\test.cpp(280) : atlTraceGeneral - Deleted       | UNKNOWN(DA014EBF) using function ReleaseDC. Success = 1. 1 handles released.
    d:\projects\test\test\test.cpp(280) : atlTraceGeneral - Deleted       | UNKNOWN(DA014EBF) using function ReleaseDC. Success = 1. 1 handles released.
    d:\projects\test\test\test.cpp(280) : atlTraceGeneral - Deleted       | UNKNOWN(DA014EBF) using function ReleaseDC. Success = 1. 1 handles released.
    d:\projects\test\test\test.cpp(280) : atlTraceGeneral - Deleted       | UNKNOWN(DA014EBF) using function ReleaseDC. Success = 1. 1 handles released.
    d:\projects\test\test\test.cpp(280) : atlTraceGeneral - Deleted       | UNKNOWN(DA014EBF) using function ReleaseDC. Success = 1. 1 handles released.
    d:\projects\test\test\test.cpp(174) : atlTraceGeneral - Created       v HRGN(EA041E0B) using function CreateRectRgn. 1 handles used.
    d:\projects\test\test\test.cpp(269) : atlTraceGeneral - Deleted       ^ HRGN(EA041E0B) using function DeleteObject. Success = 1. 0 handles released.
    d:\projects\test\test\test.cpp(280) : atlTraceGeneral - Deleted       | UNKNOWN(030A1E2A) using function DeleteObject. Success = 1. 1 handles released.
    d:\projects\test\test\test.cpp(280) : atlTraceGeneral - Deleted       | UNKNOWN(040A1E2A) using function DeleteObject. Success = 1. 1 handles released.
    d:\projects\test\test\test.cpp(280) : atlTraceGeneral - Deleted       | UNKNOWN(050A1E2A) using function DeleteObject. Success = 1. 1 handles released.
    d:\projects\test\test\test.cpp(280) : atlTraceGeneral - Deleted       | UNKNOWN(060A1E2A) using function DeleteObject. Success = 1. 1 handles released.
    d:\projects\test\test\test.cpp(280) : atlTraceGeneral - Deleted       | UNKNOWN(070A1E2A) using function DeleteObject. Success = 1. 1 handles released.
    d:\projects\test\test\test.cpp(280) : atlTraceGeneral - Deleted       | UNKNOWN(080A1E2A) using function DeleteObject. Success = 1. 1 handles released.
    d:\projects\test\test\test.cpp(280) : atlTraceGeneral - Deleted       | UNKNOWN(090A1E2A) using function DeleteObject. Success = 1. 1 handles released.
    d:\projects\test\test\test.cpp(280) : atlTraceGeneral - Deleted       | UNKNOWN(0A0A1E2A) using function DeleteObject. Success = 1. 1 handles released.
    d:\projects\test\test\test.cpp(280) : atlTraceGeneral - Deleted       | UNKNOWN(0B0A1E2A) using function DeleteObject. Success = 1. 1 handles released.
    ...
    

    它为每个创建缩进一个空格,并在被跟踪的句柄被销毁时删除缩进空间。

    以下是尚未删除的跟踪句柄示例:

    d:\projects\test\test\test.cpp(435) : atlTraceGeneral - Active GDI handles (225):
    d:\projects\test\test\test.cpp(439) : atlTraceGeneral -         1: 000563C0 HBITMAP
    d:\projects\test\test\test.cpp(439) : atlTraceGeneral -         2: 001041E0 HBRUSH
    d:\projects\test\test\test.cpp(439) : atlTraceGeneral -         3: 0205072F HBITMAP
    d:\projects\test\test\test.cpp(439) : atlTraceGeneral -         4: 0513085F HICON
    d:\projects\test\test\test.cpp(439) : atlTraceGeneral -         5: 060A1AF1 HFONT
    d:\projects\test\test\test.cpp(439) : atlTraceGeneral -         6: 070A0161 HICON
    d:\projects\test\test\test.cpp(439) : atlTraceGeneral -         7: 07102AED HBRUSH
    d:\projects\test\test\test.cpp(439) : atlTraceGeneral -         8: 07790699 HICON
    d:\projects\test\test\test.cpp(439) : atlTraceGeneral -         9: 08055812 HBITMAP
    d:\projects\test\test\test.cpp(439) : atlTraceGeneral -        10: 09050DF2 HBITMAP
    d:\projects\test\test\test.cpp(439) : atlTraceGeneral -        11: 0A051467 HBITMAP
    d:\projects\test\test\test.cpp(439) : atlTraceGeneral -        12: 0F050DAF HBITMAP
    d:\projects\test\test\test.cpp(439) : atlTraceGeneral -        13: 0F05470B HBITMAP
    d:\projects\test\test\test.cpp(439) : atlTraceGeneral -        14: 100522F7 HBITMAP
    d:\projects\test\test\test.cpp(439) : atlTraceGeneral -        15: 10054212 HBITMAP
    d:\projects\test\test\test.cpp(439) : atlTraceGeneral -        16: 13055D51 HBITMAP
    d:\projects\test\test\test.cpp(439) : atlTraceGeneral -        17: 1405084D HICON
    d:\projects\test\test\test.cpp(439) : atlTraceGeneral -        18: 140530EF HBITMAP
    d:\projects\test\test\test.cpp(439) : atlTraceGeneral -        19: 16103C8B HBRUSH
    d:\projects\test\test\test.cpp(439) : atlTraceGeneral -        20: 170564FF HBITMAP
    ...
    d:\projects\test\test\test.cpp(439) : atlTraceGeneral -       220: F5301F67 HPEN
    d:\projects\test\test\test.cpp(439) : atlTraceGeneral -       221: F60507C0 HBITMAP
    d:\projects\test\test\test.cpp(439) : atlTraceGeneral -       222: F7052C7E HBITMAP
    d:\projects\test\test\test.cpp(439) : atlTraceGeneral -       223: F81066CA HBRUSH
    d:\projects\test\test\test.cpp(439) : atlTraceGeneral -       224: FF0127BE HDC
    d:\projects\test\test\test.cpp(439) : atlTraceGeneral -       225: FF054CD8 HBITMAP
    d:\projects\test\test\test.cpp(441) : atlTraceGeneral - End of active GDI handles
    

答案 1 :(得分:0)

我不知道直接,简单的方法。

您可以做的一种方法是备份所有Windows头文件,然后注释掉您在Windows头文件中使用的GDI函数。然后,自己重新声明函数并像这样实现它:

#define CreateSolidBrush(a) _mCreateSolidBrush(__FILE__, __LINE__, a)

WINGDIAPI HBRUSH WINAPI _mCreateSolidBrush(const char *_file, unsigned long _line, COLORREF c)
{
    typedef HBRUSH (*csb)(COLORREF);
    csb _CreateSolidBrush = (csb) GetProcAddress(GetModuleHandle("gdi32.dll"), "CreateSolidBrush");
    HBRUSH retVal = _CreateSolidBrush(c);

    // do reference counting for number of open handles per file and line
    return retVal;
}

如果您不想这样做,可以尝试通过排除过程进行调试。基本上,注释掉所有GDI句柄分配/释放,除了一个。测试它是否泄漏内存,如果没有,请取消注释另一个GDI句柄分配/释放。

我希望这有帮助,祝你好运。