我正在尝试确定Windows应用程序中GDI泄漏的来源,如果我可以跟踪GDI句柄的所有分配和解除分配,这将有所帮助。是否有一些函数可以设置断点,以便我可以确定何时创建GDI句柄以及何时将其销毁?
我正在使用VC ++和Visual Studio 2013。
我找到了these,但如果能得到一个用于获取/释放所有GDI句柄的低级函数,我会更喜欢它。我试图追踪一些集会但没有成功。
答案 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();
}
我说几乎完整是因为:
无论如何,这应该让我找到泄漏的方法。我只是在应该分配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句柄分配/释放。
我希望这有帮助,祝你好运。