我正在查看CreateFont
API的示例https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createfonta
它清楚地表明,使用CreateFont
创建字体之后,应该通过DeleteObject
调用来销毁字体。 DeleteObject(hFont);
仅被调用一次。 CreateFont
被调用3次。这是MS文档中的错误吗?不应该通过使用SelectObject
检索旧字体来存储旧字体,并在使用新字体后重新设置旧字体吗?
答案 0 :(得分:3)
是的,两个创建的字体对象被泄漏。
请注意,MS示例代码通常在错误处理和对象清除方面很差(它们通常集中于演示示例中心的任何内容-这里是CreateFont调用-忽略或最小化这些问题)。
答案 1 :(得分:1)
文档中的示例确实导致字体对象泄漏
我构建了一个示例,如下所示:
#include <Windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR szCmdLine, _In_ int iCmdShow)
{
static TCHAR szAppName[] = TEXT("hello windows");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))
{
MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR);
}
hwnd = CreateWindow(szAppName,
TEXT("the hello program"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL);
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
while (GetMessageW(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_PAINT:
{
RECT rect;
HBRUSH hBrush;
HFONT hFont;
hdc = BeginPaint(hWnd, &ps);
//Logical units are device dependent pixels, so this will create a handle to a logical font that is 48 pixels in height.
//The width, when set to 0, will cause the font mapper to choose the closest matching value.
//The font face name will be Impact.
hFont = CreateFont(48, 0, 0, 0, FW_DONTCARE, FALSE, TRUE, FALSE, DEFAULT_CHARSET, OUT_OUTLINE_PRECIS,
CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, VARIABLE_PITCH, TEXT("Impact"));
SelectObject(hdc, hFont);
//Sets the coordinates for the rectangle in which the text is to be formatted.
SetRect(&rect, 100, 100, 700, 200);
SetTextColor(hdc, RGB(255, 0, 0));
DrawText(hdc, TEXT("Drawing Text with Impact"), -1, &rect, DT_NOCLIP);
//DeleteObject(hFont);
//Logical units are device dependent pixels, so this will create a handle to a logical font that is 36 pixels in height.
//The width, when set to 20, will cause the font mapper to choose a font which, in this case, is stretched.
//The font face name will be Times New Roman. This time nEscapement is at -300 tenths of a degree (-30 degrees)
hFont = CreateFont(36, 20, -300, 0, FW_DONTCARE, FALSE, TRUE, FALSE, DEFAULT_CHARSET, OUT_OUTLINE_PRECIS,
CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, VARIABLE_PITCH, TEXT("Times New Roman"));
SelectObject(hdc, hFont);
//Sets the coordinates for the rectangle in which the text is to be formatted.
SetRect(&rect, 100, 200, 900, 800);
SetTextColor(hdc, RGB(0, 128, 0));
DrawText(hdc, TEXT("Drawing Text with Times New Roman"), -1, &rect, DT_NOCLIP);
//DeleteObject(hFont);
//Logical units are device dependent pixels, so this will create a handle to a logical font that is 36 pixels in height.
//The width, when set to 10, will cause the font mapper to choose a font which, in this case, is compressed.
//The font face name will be Arial. This time nEscapement is at 250 tenths of a degree (25 degrees)
hFont = CreateFont(36, 10, 250, 0, FW_DONTCARE, FALSE, TRUE, FALSE, DEFAULT_CHARSET, OUT_OUTLINE_PRECIS,
CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, VARIABLE_PITCH, TEXT("Arial"));
SelectObject(hdc, hFont);
//Sets the coordinates for the rectangle in which the text is to be formatted.
SetRect(&rect, 500, 200, 1400, 600);
SetTextColor(hdc, RGB(0, 0, 255));
DrawText(hdc, TEXT("Drawing Text with Arial"), -1, &rect, DT_NOCLIP);
DeleteObject(hFont);
EndPaint(hWnd, &ps);
break;
}
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
在运行示例后启动任务管理器,您可以看到它的详细信息:
然后触发WM_PAINT消息:
我们发现它的GDI对象增加了2,并且每次触发它都会增加,因此此示例将导致对象泄漏。
每次使用后(在我的示例的第75行和第88行)调用DeleteObject(hFont);
,并重复上述步骤,我们会发现GDI对象不会增加,从而解决了对象泄漏的问题。