0

I am extending an old console application. Trying to add a window( for an animation ). Are able to display first image, but unable to update the window with later images. Have created a minimum application to demonstrate the problem. In this app I have a red window that should change at each update of the window( it is not ). What am I doing wrong ?

#include "pch.h"
#include <iostream>
#include <Windows.h>

using namespace std;

HWND hWindow;   

LRESULT __stdcall MyWindowProcedure(HWND hWnd, unsigned int msg, WPARAM wp, LPARAM lp)
{
    PAINTSTRUCT ps;
    HDC hdc;
    HBITMAP hBitmap;
    VOID * pvBits;
    RGBQUAD  *bmiColors;
    static int j = 128;
    HBITMAP hOldBmp;
    BITMAPINFO MyBitmap;
    BOOL qRetBlit;


    switch (msg)
    {

    case WM_DESTROY:
        std::cout << "\ndestroying window\n";
        PostQuitMessage(0);
        return 0L;

    case WM_LBUTTONDOWN:
        std::cout << "\nmouse left button down at (" << LOWORD(lp)
            << ',' << HIWORD(lp) << ")\n";
        // fall thru

    case WM_PAINT:

        hdc = BeginPaint(hWnd, &ps);
#if 1
        // Create a device context that is compatible with the window
        HDC hLocalDC;
        hLocalDC = ::CreateCompatibleDC(hdc);
        // Verify that the device context was created
        if (hLocalDC == NULL) {
            printf("CreateCompatibleDC Failed\n");
            //          return false;
            break;
        }

        MyBitmap.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
        MyBitmap.bmiHeader.biWidth = 256;
        MyBitmap.bmiHeader.biHeight = -256;
        MyBitmap.bmiHeader.biPlanes = 1;
        MyBitmap.bmiHeader.biBitCount = 32;
        MyBitmap.bmiHeader.biCompression = BI_RGB;
        MyBitmap.bmiHeader.biSizeImage = 256 * 256 * 4;
        MyBitmap.bmiHeader.biYPelsPerMeter = 1000;
        MyBitmap.bmiHeader.biXPelsPerMeter = 1000;
        MyBitmap.bmiHeader.biClrUsed = 0;
        MyBitmap.bmiHeader.biClrImportant = 0;

        hBitmap = CreateDIBSection(NULL, &MyBitmap, DIB_RGB_COLORS, &pvBits, NULL, NULL);
        bmiColors = (RGBQUAD*)pvBits;

        // note: BGR
        for (int i = 0; i < 256 * 256; i++)
        {
            bmiColors[i].rgbBlue = 0;
            bmiColors[i].rgbGreen = 0;
            bmiColors[i].rgbRed = j % 256;
            bmiColors[i].rgbReserved = 255;
        }
        j += 10;

        bmiColors[1000].rgbRed;

        // Select the bitmap into the device context
        hOldBmp = (HBITMAP)::SelectObject(hLocalDC, hBitmap);
        if (hOldBmp == NULL) {
            printf("SelectObject Failed");
            //          return false;
            break;
        }

        // Blit the dc which holds the bitmap onto the window's dc
        qRetBlit = ::BitBlt(hdc, 0, 0, 256, 256, hLocalDC, 0, 0, SRCCOPY);
        if (!qRetBlit) {
            printf("Blit Failed");
            //          return false;
            break;
        }

        // Unitialize and deallocate resources
        SelectObject(hLocalDC, hOldBmp);
        DeleteDC(hLocalDC);
        DeleteObject(hBitmap);
#endif
        EndPaint(hWnd, &ps);
        break;

    default:

        std::cout << '.';
        break;
    }
    return DefWindowProc(hWnd, msg, wp, lp);
}

int main()

{
    WNDCLASSEX wndclass = { sizeof(WNDCLASSEX), CS_DBLCLKS | CS_HREDRAW, MyWindowProcedure,
                                0,  0, GetModuleHandle(0), LoadIcon(0,IDI_APPLICATION),
                                LoadCursor(0,IDC_ARROW), HBRUSH(COLOR_WINDOW + 1),
                                0, L"myclass", LoadIcon(0,IDI_APPLICATION) };

    if (RegisterClassEx(&wndclass))
    {
        hWindow = CreateWindowEx(0, L"myclass", L"myclass", WS_OVERLAPPEDWINDOW, 1200, 600, 256 + 15, 256 + 38, 0, 0, GetModuleHandle(0), 0);

        ShowWindow(hWindow, SW_SHOWDEFAULT);
        UpdateWindow(hWindow);

        MSG msg;
        byte bRet;

        while ((bRet = GetMessage(&msg, hWindow, 0, 0)) != 0)
        {
            if (bRet == -1)
            {
                return 1;
            }
            else
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
        }
    }
}
drescherjm
  • 10,365
  • 5
  • 44
  • 64
enocknitti
  • 31
  • 8
  • You should not call [BeginPaint](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-beginpaint) except in response to a WM_PAINT message, and every call to `BeginPaint` must be matched with a call to `EndPaint`. – 1201ProgramAlarm Dec 03 '19 at 19:21
  • So the error is in the "missing" break in the case WM_LBUTTONDOWN: std::cout << "\nmouse left button down at (" << LOWORD(lp) << ',' << HIWORD(lp) << ")\n"; // fall thru – enocknitti Dec 03 '19 at 19:35
  • So BeginPaint() and EndPaint(); can only be used as a responce to WM_PAINT: Is that correct ? – enocknitti Dec 03 '19 at 19:40
  • That's what the documentation for `BeginPaint` says. – 1201ProgramAlarm Dec 03 '19 at 19:54
  • Ok that seems to be the problem. Then I have to update the image by som othe way than "fall thru" on the WM_LBUTTONDOWN btw: I found that code in some example on the net. I will try to find that example again and add a commet about this. – enocknitti Dec 03 '19 at 19:59
  • You can use one of the `Invalidate` functions. – 1201ProgramAlarm Dec 03 '19 at 19:59
  • Thank you, problem solved....... Time for bed :) – enocknitti Dec 03 '19 at 20:08
  • On a side note, get rid of the check for `if (hOldBmp == NULL)` after the call to `::SelectObject(hLocalDC, hBitmap)`. And since the `HBITMAP` is not dependent on anything that is not known until `WM_PAINT` is processed, you should just create the `HBITMAP` when the window is first created (in `WM_CREATE`), re-use it every time `WM_PAINT` is received, and then destroy it in `WM_DESTROY`. – Remy Lebeau Dec 03 '19 at 22:03

1 Answers1

1

As pointed out. You do not want to call WM_PAINT your self. Let the system handle it. So in WM_LBUTTONDOWN do:

case WM_LBUTTONDOWN:
        std::cout << "\nmouse left button down at (" << LOWORD(lp)
            << ',' << HIWORD(lp) << ")\n";
        InvalidateRect(hWnd, nullptr, FALSE);
        return 0;

Do not fall through. Wnen you call RedrawWindow the window is invalidated and then needs to be repainted. I just tested this, the window lightens up on each click.

UPDATE: Thanks to Remy Lebeau

Difference between InvalidateRect and RedrawWindow

And yes, it is better to let windows use its own schedule for this kind of stuff.

lakeweb
  • 1,859
  • 2
  • 16
  • 21
  • Unless you need an immediate repaint, you should generally use `InvalidateRect()` instead and let the OS generate `WM_PAINT` on its own time when appropriate – Remy Lebeau Dec 03 '19 at 22:05
  • @Remy Lebeau, thanks. I work with MFC and have rarely used a console method. I did not find `Invalidate()` for this. I'll update my post. – lakeweb Dec 03 '19 at 22:13
  • [`InvalidateRect()`](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-invalidaterect) – Remy Lebeau Dec 03 '19 at 22:19