Опять GDI, опять утечка

263
24 декабря 2017, 09:41

Вот, код, в ходе которого происходит утечки памяти. Это происходит, когда изменяю размер окна

Изначально ClearSurface и ResizeSurface были одной функцией, я имею в виду, что освобождение памяти происходило в ResizeSurface, так что суть не меняется. Утечка происходит только во время изменения размеров окна через WM_NCHITTES. Когда изменение размеров окна прекращается, то прекращается и утечка.

void render(HWND id);
map<HWND, int>main_map;
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp);
class Card
{
public:
bool live;
HWND hwnd;
HDC hdc,hdcc;
HBITMAP bm;
HGDIOBJ bm_old;
bool pause_render;
bool render_stoped;
thread *render_thread;
void Create(int id, wchar_t *caption, int x, int y, int cx, int cy)
{
    ...
    WNDCLASS cl = {};
    cl.lpfnWndProc = WndProc;
    cl.hInstance = chin;
    cl.lpszClassName = L"class";
    RegisterClass(&cl);
    hwnd = CreateWindow(L"class", caption, WS_POPUP|WS_VISIBLE, x, y, cx, cy, 0, 0, chin, 0);
    hdc = GetDC(hwnd);
    hdcc = CreateCompatibleDC(hdc);
    bm = CreateCompatibleBitmap(hdc, cx, cy);
    bm_old=SelectObject(hdcc, bm);
    main_map[hwnd] = id;
    render_thread=new thread(render, hwnd);
    render_thread->detach();
    pause_render = false;
    render_stoped = false;
}
void Delete()
{
    live = false;
    delete render_thread;
}
void ClearSurface()
{
    ReleaseDC(hwnd, hdc);
    SelectObject(hdcc, bm_old);
    DeleteObject(bm);
    DeleteDC(hdcc);
}
void ResizeSurface()
{
    while (!render_stoped)
    { }
    hdc = GetDC(hwnd);
    hdcc = CreateCompatibleDC(hdc);
    bm = CreateCompatibleBitmap(hdc, cx, cy);
    bm_old = SelectObject(hdcc, bm);
    pause_render = false;
    render_stoped = true;
}
};
map<int, Card*>card2;
void render(HWND id)
{
for (; card2[main_map[id]]->live;)
{
    Card *card = card2[main_map[id]];
    if (!card->pause_render)
    {
        HDC hdc = card->hdcc;
        Rectangle(hdc, 0, 0, card->cx, card->cy);
        TextOut(hdc, 10, 10, card->caption, wcslen(card->caption));
        BitBlt(card->hdc, 0, 0, card->cx, card->cy, card->hdcc, 0, 0, SRCCOPY);
    }
    else
        card->render_stoped = true;
}
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp)
{
RECT r;
GetClientRect(hwnd, &r);
switch (message)
{
case WM_NCHITTEST:
    if (LOWORD(lp) > r.right - 5)
    {
        card2[main_map[hwnd]]->pause_render = true;
        while (!card2[main_map[hwnd]]->render_stoped)
        { }
        card2[main_map[hwnd]]->ClearSurface();
        return HTRIGHT;
    }
    break;
case WM_PAINT:
    break;
case WM_SIZE:
    card2[main_map[hwnd]]->pause_render = true;
    card2[main_map[hwnd]]->cx = r.right;
    card2[main_map[hwnd]]->cy = r.bottom;
    card2[main_map[hwnd]]->ResizeSurface();
    break;
case WM_KEYDOWN:
    wcscpy(card2[main_map[hwnd]]->caption, L"asd");
    break;
case WM_DESTROY:
    PostQuitMessage(0);
    break;
default:
    return DefWindowProc(hwnd, message, wp, lp);
}
return 0;
}
int main()
{
HideConsole();
card2[0] = new Card();
card2[0]->Create(0, L"WORK", 0, 0, 500, 500);
MSG msg;
while (GetMessage(&msg, 0, 0, 0))
{
    DispatchMessage(&msg);
    TranslateMessage(&msg);
}

return 0;
}

Как показывает GdiView, не удаляются битмапы и hdc. В чём проблема?

Answer 1

Вы создаете поток и обращаетесь к общим ресурсам безо всякой синхронизации. Соответственно тут вылезает неопределенное поведение. Используйте mutex или atomic механизмы для синхронизации, напишите RAII врапперы для GDI ресурсов. Я не могу привести источник, но использование GDI ресурсов в потоке, отличном от создавшего их, может быть чревато, так что не факт, что такой фоновый рендерер вообще жизнеспособен (ну или ресурсы для него надо выделять в том же потоке). Кроме того, необходимо корректно обрабатывать WM_PAINT хотябы вызвав ValidateRect.

READ ALSO
Работа с БД C++(QT)

Работа с БД C++(QT)

Здравствуйте, создаю приложение, которое работает с БДБД представляет собой данные о школе(ученики, сотрудники школы, классы, родители учеников...

308
Как заменить пробелы на символ &ldquo;$&rdquo;?

Как заменить пробелы на символ “$”?

Здравствуйте! Как заменить пробелы символом “$” с помощью функций и манипуляторов?

356
Выключение экрана WinAPI C++

Выключение экрана WinAPI C++

Только учусь winAPI, весь вечер вчера пытался написать программу, которая бы выключала экран и включала при нажатие на клавишу F2, код нижеПроблема...

338