[WinApi] Program "Rysowanie elips v. 1.0" i błąd

0

Napisałem program, który rysuje elipsy w podanym przez użytkownika miejscu. Problem polega na tym, że program działa dobrze jakiś czas, a potem dzieją się dziwne rzeczy. W programie rysowane elipsy są czerwone, a tło jest szare. Jak program zaczyna dziwnie działać, elipsy zmieniają kolor na czarny, a belka okna znika i przy próbie przesunięcia okna rozwala się cały windows (wizualnie), aż się nie zamknie programu i nie odświeży pulpitu - wtedy wszystko wraca do normy. Podaję kod:

#include <windows.h>
#include <stdio.h>

POINT pt[1000][2] = {{{0, 0}, {0, 0}}};
HWND uchwytOkna = 0;
int i = 0;
int ostatni = 0;
char belka[100];

LRESULT CALLBACK ProceduraOkna(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
void RysujElipse(HWND uO, INT lewo, INT gora, INT prawo, INT dol);
void RysujTlo(HWND u0);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
    WNDCLASSEX wndclassex;

    wndclassex.cbSize = sizeof (WNDCLASSEX);
    wndclassex.style = CS_VREDRAW | CS_HREDRAW;
    wndclassex.lpfnWndProc = ProceduraOkna;
    wndclassex.cbClsExtra = 0;
    wndclassex.cbWndExtra = 0;
    wndclassex.hInstance = hInstance;
    wndclassex.hIcon = LoadIcon(0, (LPCTSTR)IDI_APPLICATION);
    wndclassex.hCursor = LoadCursor(0, (LPCTSTR)IDC_ARROW);
    wndclassex.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
    wndclassex.lpszMenuName = 0;
    wndclassex.lpszClassName = "Standardowe Okno";
    wndclassex.hIconSm = LoadIcon(0, (LPCTSTR)IDI_APPLICATION);

    RegisterClassEx(&wndclassex);

    uchwytOkna = CreateWindowEx(
                0,
                "Standardowe Okno",
                "Program \"Rysowanie Elips v. 1.0\" - Elipsy: 0",
                WS_OVERLAPPEDWINDOW,
                0, 0,
                800, 500,
                0,
                0,
                hInstance,
                0);

    ShowWindow(uchwytOkna, SW_NORMAL);
    UpdateWindow(uchwytOkna);

    MSG msg;

    for (;;)
    {
        if (0 != GetMessage(&msg, 0, 0, 0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        if (msg.message == WM_QUIT) break;
    }

    return (int)msg.wParam;
}

LRESULT CALLBACK ProceduraOkna(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
        case WM_CREATE:
            MessageBox(hWnd, "Program do rysowania elips.\nMozna narysowac max. 1000 elips.\nz - cofanie\nx - ponawianie\n\nAutor: Michal Pawlowski", "informacja", MB_OK | MB_ICONINFORMATION);
            return 0;
        case WM_LBUTTONDOWN:
            {
                pt[i][0].x = LOWORD(lParam);
                pt[i][0].y = HIWORD(lParam);
            }
            return 0;
        case WM_LBUTTONUP:
            {
                if (pt[i][0].x != LOWORD(lParam) && pt[i][0].y != HIWORD(lParam) && i < 1000)
                {
                    pt[i][1].x = LOWORD(lParam);
                    pt[i][1].y = HIWORD(lParam);
                    RysujElipse(hWnd, pt[i][0].x, pt[i][0].y, pt[i][1].x, pt[i][1].y);
                    i++;
                    ostatni = i;
                    sprintf(belka, "Program \"Rysowanie elips v. 1.0\" - Elipsy: %d", i);
                    SetWindowText(hWnd, belka);
                }
            }
            return 0;
        case WM_MOUSEMOVE:
            if (wParam & MK_LBUTTON)
            {
                RysujTlo(hWnd);
                for(int j = 0; j < i; j++)
                    RysujElipse(hWnd, pt[j][0].x, pt[j][0].y, pt[j][1].x, pt[j][1].y);
                RysujElipse(hWnd, pt[i][0].x, pt[i][0].y, LOWORD(lParam), HIWORD(lParam));
            }
        case WM_CHAR:
            {
                if (wParam == 'z' && i > 0)
                {
                    i--;
                    RysujTlo(hWnd);
                    for (int j = 0; j < i; j++)
                        RysujElipse(hWnd, pt[j][0].x, pt[j][0].y, pt[j][1].x, pt[j][1].y);
                    sprintf(belka, "Program \"Rysowanie elips v. 1.0\" - Elipsy: %d", i);
                    SetWindowText(hWnd, belka);
                }

                if (wParam == 'x' && i < ostatni)
                {
                    i++;
                    RysujTlo(hWnd);
                    for (int j = 0; j < i; j++)
                        RysujElipse(hWnd, pt[j][0].x, pt[j][0].y, pt[j][1].x, pt[j][1].y);
                    sprintf(belka, "Program \"Rysowanie elips v. 1.0\" - Elipsy: %d", i);
                    SetWindowText(hWnd, belka);
                }
                return 0;
            }
        case WM_PAINT:
            for (int j = 0; j < i; j++)
                RysujElipse(hWnd, pt[j][0].x, pt[j][0].y, pt[j][1].x, pt[j][1].y);
            return 0;
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
    }
    return DefWindowProc(hWnd, message, wParam, lParam);
}

void RysujElipse(HWND uO, INT lewo, INT gora, INT prawo, INT dol)
{
    HDC hdc = 0;
    hdc = GetDC(uO);

    SelectObject(hdc, CreatePen(PS_SOLID, 1, RGB(255, 0, 0)));
    SelectObject(hdc, GetStockObject(NULL_BRUSH));

    Ellipse(hdc, lewo, gora, prawo, dol);

    ReleaseDC(uO, hdc);
    ValidateRect(uO, 0);

    DeleteObject(SelectObject(hdc, GetStockObject(BLACK_PEN)));
}

void RysujTlo(HWND u0)
{
    HDC hdc = 0;
    hdc = GetDC(u0);

    SelectObject(hdc, GetStockObject(NULL_PEN));
    SelectObject(hdc, GetStockObject(GRAY_BRUSH));

    Rectangle(hdc, 0, 0, 2000, 2000);

    ReleaseDC(u0, hdc);
    ValidateRect(u0, 0);
}
 
0

W obsłudze WM_PAINT na samym początku musisz wywołać BeginPaint, a następnie, po tym jak skończysz malowanie, funkcję EndPaint. Pierwsza funkcja zwraca kontekst, po którym masz malować, żadne tam GetDC. Po szczegóły - jak zwykle - odsyłam do dokumentacji.

0

Ale dlaczego, skoro wywołuję funkcję ValidateRect, która zatwierdza unieważniony obszar, oraz funkcję ReleaseDC, która zwalnia kontekst urządzenia?

0

Przerobiłem program i teraz używa BeginPaint i EndPaint, ale nadal jest to samo. Nie wiem, czy nie można wywołać funkcji GetDC więcej niż x razy?

0
#include <windows.h>
#include <stdio.h>

POINT pt[1000][2] = {{{0, 0}, {0, 0}}};
HWND uchwytOkna = 0;
int i = 0;
int ostatni = 0;
char belka[100];

LRESULT CALLBACK ProceduraOkna(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
void RysujElipse(HWND uO, INT lewo, INT gora, INT prawo, INT dol);
void RysujElipsePaint(HWND uO, HDC hdc, INT lewo, INT gora, INT prawo, INT dol);
void RysujTlo(HWND u0);
void RysujTloPaint(HWND u0, HDC hdc);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
    WNDCLASSEX wndclassex;

    wndclassex.cbSize = sizeof (WNDCLASSEX);
    wndclassex.style = CS_VREDRAW | CS_HREDRAW;
    wndclassex.lpfnWndProc = ProceduraOkna;
    wndclassex.cbClsExtra = 0;
    wndclassex.cbWndExtra = 0;
    wndclassex.hInstance = hInstance;
    wndclassex.hIcon = LoadIcon(0, (LPCTSTR)IDI_APPLICATION);
    wndclassex.hCursor = LoadCursor(0, (LPCTSTR)IDC_ARROW);
    wndclassex.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
    wndclassex.lpszMenuName = 0;
    wndclassex.lpszClassName = "Standardowe Okno";
    wndclassex.hIconSm = LoadIcon(0, (LPCTSTR)IDI_APPLICATION);

    RegisterClassEx(&wndclassex);

    uchwytOkna = CreateWindowEx(
                0,
                "Standardowe Okno",
                "Program \"Rysowanie Elips v. 1.0\" - Elipsy: 0",
                WS_OVERLAPPEDWINDOW,
                0, 0,
                800, 500,
                0,
                0,
                hInstance,
                0);

    ShowWindow(uchwytOkna, SW_NORMAL);
    UpdateWindow(uchwytOkna);

    MSG msg;

    for (;;)
    {
        if (0 != GetMessage(&msg, 0, 0, 0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        if (msg.message == WM_QUIT) break;
    }

    return (int)msg.wParam;
}

LRESULT CALLBACK ProceduraOkna(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    HDC hdc = 0;
    PAINTSTRUCT ps = {0};

    switch (message)
    {
        case WM_CREATE:
            MessageBox(hWnd, "Program do rysowania elips.\nMozna narysowac max. 1000 elips.\nz - cofanie\nx - ponawianie\n\nAutor: Michal Pawlowski", "informacja", MB_OK | MB_ICONINFORMATION);
            return 0;
        case WM_LBUTTONDOWN:
            {
                pt[i][0].x = LOWORD(lParam);
                pt[i][0].y = HIWORD(lParam);
            }
            return 0;
        case WM_LBUTTONUP:
            {
                if (pt[i][0].x != LOWORD(lParam) && pt[i][0].y != HIWORD(lParam) && i < 1000)
                {
                    pt[i][1].x = LOWORD(lParam);
                    pt[i][1].y = HIWORD(lParam);
                    i++;
                    ostatni = i;
                    InvalidateRect(hWnd, 0, TRUE);
                }
            }
            return 0;
        case WM_MOUSEMOVE:
            if (wParam & MK_LBUTTON)
            {
                RysujTlo(hWnd);
                for (int j = 0; j < i; j++)
                    RysujElipse(hWnd, pt[j][0].x, pt[j][0].y, pt[j][1].x, pt[j][1].y);
                RysujElipse(hWnd, pt[i][0].x, pt[i][0].y, LOWORD(lParam), HIWORD(lParam));
            }
        case WM_CHAR:
            {
                if (wParam == 'z' && i > 0)
                {
                    i--;
                    InvalidateRect(hWnd, 0, TRUE);
                }

                if (wParam == 'x' && i < ostatni)
                {
                    i++;
                    InvalidateRect(hWnd, 0, TRUE);
                }
                return 0;
            }
        case WM_PAINT:
            InvalidateRect(hWnd, 0, TRUE);
            hdc = BeginPaint(hWnd, &ps);
            for (int j = 0; j < i; j++)
                RysujElipsePaint(hWnd, hdc, pt[j][0].x, pt[j][0].y, pt[j][1].x, pt[j][1].y);
            sprintf(belka, "Program \"Rysowanie elips v. 1.0\" - Elipsy: %d", i);
            SetWindowText(hWnd, belka);
            EndPaint(hWnd, &ps);
            return 0;
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
    }
    return DefWindowProc(hWnd, message, wParam, lParam);
}

void RysujElipse(HWND uO, INT lewo, INT gora, INT prawo, INT dol)
{
    HDC hdc = 0;
    hdc = GetDC(uO);

    SelectObject(hdc, CreatePen(PS_SOLID, 1, RGB(255, 0, 0)));
    SelectObject(hdc, GetStockObject(NULL_BRUSH));

    Ellipse(hdc, lewo, gora, prawo, dol);

    ReleaseDC(uO, hdc);

    DeleteObject(SelectObject(hdc, GetStockObject(BLACK_PEN)));
}

void RysujElipsePaint(HWND uO, HDC hdc, INT lewo, INT gora, INT prawo, INT dol)
{
    SelectObject(hdc, CreatePen(PS_SOLID, 1, RGB(255, 0, 0)));
    SelectObject(hdc, GetStockObject(NULL_BRUSH));

    Ellipse(hdc, lewo, gora, prawo, dol);

    DeleteObject(SelectObject(hdc, GetStockObject(BLACK_PEN)));
}

void RysujTlo(HWND u0)
{
    HDC hdc = 0;
    hdc = GetDC(u0);

    SelectObject(hdc, GetStockObject(NULL_PEN));
    SelectObject(hdc, GetStockObject(GRAY_BRUSH));

    Rectangle(hdc, 0, 0, 2000, 2000);

    ReleaseDC(u0, hdc);
    ValidateRect(u0, 0);
}

void RysujTloPaint(HWND u0, HDC hdc)
{
    SelectObject(hdc, GetStockObject(NULL_PEN));
    SelectObject(hdc, GetStockObject(GRAY_BRUSH));

    Rectangle(hdc, 0, 0, 2000, 2000);
}
 
0

Znowu zmieniłem program i wywaliłem wszystkie funkcje korzystające z GetDC i ReleaseDC i teraz działa dobrze. Oto kod:

#include <windows.h>
#include <stdio.h>

POINT pt[1000][2] = {{{0, 0}, {0, 0}}};
HWND uchwytOkna = 0;
int i = 0;
int ostatni = 0;
char belka[100];

LRESULT CALLBACK ProceduraOkna(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
void RysujElipse(HWND uO, INT lewo, INT gora, INT prawo, INT dol);
void RysujElipsePaint(HWND uO, HDC hdc, INT lewo, INT gora, INT prawo, INT dol);
void RysujTlo(HWND u0);
void RysujTloPaint(HWND u0, HDC hdc);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
    WNDCLASSEX wndclassex;

    wndclassex.cbSize = sizeof (WNDCLASSEX);
    wndclassex.style = CS_VREDRAW | CS_HREDRAW;
    wndclassex.lpfnWndProc = ProceduraOkna;
    wndclassex.cbClsExtra = 0;
    wndclassex.cbWndExtra = 0;
    wndclassex.hInstance = hInstance;
    wndclassex.hIcon = LoadIcon(0, (LPCTSTR)IDI_APPLICATION);
    wndclassex.hCursor = LoadCursor(0, (LPCTSTR)IDC_ARROW);
    wndclassex.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
    wndclassex.lpszMenuName = 0;
    wndclassex.lpszClassName = "Standardowe Okno";
    wndclassex.hIconSm = LoadIcon(0, (LPCTSTR)IDI_APPLICATION);

    RegisterClassEx(&wndclassex);

    uchwytOkna = CreateWindowEx(
                0,
                "Standardowe Okno",
                "Program \"Rysowanie Elips v. 1.0\" - Elipsy: 0",
                WS_OVERLAPPEDWINDOW,
                0, 0,
                800, 500,
                0,
                0,
                hInstance,
                0);

    ShowWindow(uchwytOkna, SW_NORMAL);
    UpdateWindow(uchwytOkna);

    MSG msg;

    for (;;)
    {
        if (0 != GetMessage(&msg, 0, 0, 0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        if (msg.message == WM_QUIT) break;
    }

    return (int)msg.wParam;
}

LRESULT CALLBACK ProceduraOkna(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    HDC hdc = 0;
    PAINTSTRUCT ps = {0};

    switch (message)
    {
        case WM_CREATE:
            MessageBox(hWnd, "Program do rysowania elips.\nMozna narysowac max. 1000 elips.\nz - cofanie\nx - ponawianie\n\nAutor: Michal Pawlowski", "informacja", MB_OK | MB_ICONINFORMATION);
            return 0;
        case WM_LBUTTONDOWN:
            {
                pt[i][0].x = LOWORD(lParam);
                pt[i][0].y = HIWORD(lParam);
            }
            return 0;
        case WM_LBUTTONUP:
            {
                if (pt[i][0].x != LOWORD(lParam) && pt[i][0].y != HIWORD(lParam) && i < 1000)
                {
                    pt[i][1].x = LOWORD(lParam);
                    pt[i][1].y = HIWORD(lParam);
                    i++;
                    ostatni = i;
                    InvalidateRect(hWnd, 0, TRUE);
                }
            }
            return 0;
        case WM_MOUSEMOVE:
            if (wParam & MK_LBUTTON)
            {
                InvalidateRect(hWnd, 0, TRUE);
                hdc = BeginPaint(hWnd, &ps);
                for (int j = 0; j < i; j++)
                    RysujElipsePaint(hWnd, hdc, pt[j][0].x, pt[j][0].y, pt[j][1].x, pt[j][1].y);
                RysujElipse(hWnd, pt[i][0].x, pt[i][0].y, LOWORD(lParam), HIWORD(lParam));
                EndPaint(hWnd, &ps);
            }
        case WM_CHAR:
            {
                if (wParam == 'z' && i > 0)
                {
                    i--;
                    InvalidateRect(hWnd, 0, TRUE);
                }

                if (wParam == 'x' && i < ostatni)
                {
                    i++;
                    InvalidateRect(hWnd, 0, TRUE);
                }
                return 0;
            }
        case WM_PAINT:
            InvalidateRect(hWnd, 0, TRUE);
            hdc = BeginPaint(hWnd, &ps);
            for (int j = 0; j < i; j++)
                RysujElipsePaint(hWnd, hdc, pt[j][0].x, pt[j][0].y, pt[j][1].x, pt[j][1].y);
            sprintf(belka, "Program \"Rysowanie elips v. 1.0\" - Elipsy: %d", i);
            SetWindowText(hWnd, belka);
            EndPaint(hWnd, &ps);
            return 0;
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
    }
    return DefWindowProc(hWnd, message, wParam, lParam);
}

void RysujElipse(HWND uO, INT lewo, INT gora, INT prawo, INT dol)
{
    HDC hdc = 0;
    hdc = GetDC(uO);

    SelectObject(hdc, CreatePen(PS_SOLID, 1, RGB(255, 0, 0)));
    SelectObject(hdc, GetStockObject(NULL_BRUSH));

    Ellipse(hdc, lewo, gora, prawo, dol);

    ReleaseDC(uO, hdc);

    DeleteObject(SelectObject(hdc, GetStockObject(BLACK_PEN)));
}

void RysujElipsePaint(HWND uO, HDC hdc, INT lewo, INT gora, INT prawo, INT dol)
{
    SelectObject(hdc, CreatePen(PS_SOLID, 1, RGB(255, 0, 0)));
    SelectObject(hdc, GetStockObject(NULL_BRUSH));

    Ellipse(hdc, lewo, gora, prawo, dol);

    DeleteObject(SelectObject(hdc, GetStockObject(BLACK_PEN)));
}

void RysujTlo(HWND u0)
{
    HDC hdc = 0;
    hdc = GetDC(u0);

    SelectObject(hdc, GetStockObject(NULL_PEN));
    SelectObject(hdc, GetStockObject(GRAY_BRUSH));

    Rectangle(hdc, 0, 0, 2000, 2000);

    ReleaseDC(u0, hdc);
    ValidateRect(u0, 0);
}

void RysujTloPaint(HWND u0, HDC hdc)
{
    SelectObject(hdc, GetStockObject(NULL_PEN));
    SelectObject(hdc, GetStockObject(GRAY_BRUSH));

    Rectangle(hdc, 0, 0, 2000, 2000);
}
 
0

Wspomniane przeze mnie funkcje możesz użyć tylko w obsłudze WM_PAINT. Jak chcesz wymusić odmalowanie okna, użyj RedrawWindow (**InvalidateRect **nie daje gwarancji natychmiastowego odmalowania). Wywal tę pętlę z obsługi WM_MOUSEMOVE, bo to samo masz w WM_PAINT. Wywal także InvalidateRect z WM_PAINT.

0

InvalidateRect w WM_PAINT jest po to, żeby nie było ograniczającego regionu po wywołaniu BeginPaint. Ale nie miałeś racji co do GetDC etc. Te funkcje działają dobrze. Błąd w programie polegał na tym, że w funkcji RysujElipse kasowałem pióro po zwolnieniu kontekstu urządzenia, a nie po.

A wszystkie polecenia do WinApi znam z książki: Visual Studio 2005. Programowanie z Windows API w języku C++. Piotr Besta. W tej książce są przykłady z GetDC itp. Polecam.

Pozdrawiam
Michał

0

InvalidateRect w WM_PAINT jest po to, żeby nie było ograniczającego regionu po wywołaniu BeginPaint.

No dobra, ale po co Ci to, jeśli i tak unieważniasz cały obszar w WM_MOUSEMOVE, WM_LBUTTONUP i WM_CHAR?

1 użytkowników online, w tym zalogowanych: 0, gości: 1