CreateFont,DeleteObject和GDI处理/内存泄漏

时间:2020-09-01 06:00:45

标签: c++ winapi gdi

我正在查看CreateFont API的示例https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createfonta

它清楚地表明,使用CreateFont创建字体之后,应该通过DeleteObject调用来销毁字体。 DeleteObject(hFont); 仅被调用一次。 CreateFont被调用3次。这是MS文档中的错误吗?不应该通过使用SelectObject检索旧字体来存储旧字体,并在使用新字体后重新设置旧字体吗?

2 个答案:

答案 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;
}

在运行示例后启动任务管理器,您可以看到它的详细信息:

enter image description here enter image description here

然后触发WM_PAINT消息:

enter image description here

我们发现它的GDI对象增加了2,并且每次触发它都会增加,因此此示例将导致对象泄漏。

每次使用后(在我的示例的第75行和第88行)调用DeleteObject(hFont);,并重复上述步骤,我们会发现GDI对象不会增加,从而解决了对象泄漏的问题。

相关问题