The answer was GDI HFONT
handles.
It is a Windows 8 issue that GDIView cannot show the font handles.
I used hooking to intercept every call to:
and logged every handle creation, along with its stack trace of when it was allowed. At the end i created a report of all undeleted HFONT
s.
How did i do it?
I used the Detours library for Delphi.
Step 1 - For every GDI function there is that creates something, we ask Detours to intercept the function.
- We pass the address of our replacement function
- and it returns the address of the original function (so we can call it)
var
CreateFontIndirectAOriginal: function (const p1: TLogFontA): HFONT; stdcall = nil;
DeleteObjectOriginal: function (p1: HGDIOBJ): BOOL; stdcall = nil;
CreateFontIndirectAOriginal := InterceptCreate(@CreateFontIndirectA, @CreateFontIndirectAIntercept);
DeleteObjectOriginal := InterceptCreate(@DeleteObject, @DeleteObjectIntercept);
Step 2 - Declare our versions of the GDI functions:
function CreateFontIndirectAIntercept(const p1: TLogFontA): HFONT; stdcall;
begin
Result := CreateFontIndirectAOriginal(p1);
end;
function DeleteObjectIntercept(p1: HGDIOBJ): BOOL; stdcall;
begin
Result := DeleteObjectOriginal(p1);
end;
Step 3 - Add code to track every font created by CreateFont, and every font destruction by DestroyObject
function CreateFontIndirectAIntercept(const p1: TLogFontA): HFONT; stdcall;
begin
Result := TrampolineCreateFontIndirectA(p1);
GdiLeakTrackerSvc.AddFont(Result);
end;
function DeleteObjectIntercept(p1: HGDIOBJ): BOOL; stdcall;
var
objType: DWORD;
begin
objType := GetObjectType(p1);
Result := TrampolineDeleteObject(p1);
case objType of
OBJ_FONT: GdiLeakTrackerSvc.RemoveObject(p1);
end;
end;
And then the GdiLeakTrackerSvc
service tracks all font creations, font destructions, and can let us know during program shutdown if anything leaked.