i put an image on form with WM_PAINT message.
but the image is, only, showed after resize(for example) the form. why is that? did i forget any style\extended style?

Dani AI

Generated

Short answer: the window was getting a WM_PAINT before your paint callback and cached bitmap were ready, so the first paint did nothing. As pointed out, loading the image inside every paint is costly and leaking GDI objects; as discovered, the callback was still NULL at first and forcing a repaint after initialization made the image appear.

Practical guidance:

  • Load the bitmap once (for example in your initialization or WM_CREATE) and keep the HBITMAP around. On WM_PAINT just blit from a prepared memory DC — do not call LoadImage on every paint. Always restore the previously selected object and free your DC/bitmap when the window is destroyed to avoid leaks.
  • Use GetObject on the HBITMAP to obtain width/height so BitBlt uses the correct size. Prefer CreateCompatibleDC(hdc) (or create your compatible DC once) and avoid repeatedly calling GetDC(0) for each paint.
  • Don’t call BeginPaint/EndPaint except inside WM_PAINT, and don’t call SendMessage to synchronously re-enter WM_PAINT from inside WndProc — that can cause ordering and reentrancy issues.

Initialization and forcing the first paint:

  • Set up your instance pointer during WM_NCCREATE/WM_CREATE so the paint callback exists before the window becomes visible.
  • After you create and show the window, request a redraw (invalidate the client area and let the system issue WM_PAINT, or call UpdateWindow to process the update immediately). That’s what cured the symptom you saw, but the robust fix is to ensure resources and callbacks are ready before the window is shown.

Quick checklist if it still fails: verify LoadImage returned non-NULL and check GetLastError, confirm the window is visible, check bitmap dimensions from GetObject, ensure you restore/select GDI objects correctly, and handle WM_ERASEBKGND or use double-buffering to avoid flicker.

Recommended Answers

All 5 Replies

I cannot replicate this. What does your WndProc look like?

in form class:

case WM_PAINT:
                {
                    if (inst->Paint==NULL) break;
                    PAINTSTRUCT  ps;
                    HDC hdc = BeginPaint(inst->hwnd, &ps);
                    inst->Paint(inst->hwnd,hdc);
                    EndPaint(inst->hwnd, &ps);
                }
                break;

(inst is the instance class pointer, but the problem is other)
the inst->Paint() is a lambda function.
heres the form instance code:

form a;
button c,g,b;

HBITMAP bmpSource = NULL;
HDC hdcSource = NULL;

int WinMain()
{


    a.Paint=[](HWND hwndWindow, HDC hdcWindow)
    {
        bmpSource = (HBITMAP)LoadImage(NULL, "C:\\Users\\Public\\Pictures\\Sample Pictures\\Chrysanthemum.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
        hdcSource = CreateCompatibleDC(GetDC(0));
        SelectObject(hdcSource, bmpSource);
        BitBlt(hdcWindow, 0, 0, 500, 500, hdcSource, 0, 0, SRCCOPY);

    };
//............

i can't see any problem with code...
but for see the paint image, i need resize(for exemple) the form... why?

The problem with the above code is actually that you LOAD the image every single time the window is painted. This can take time to do. Not only that, you never delete the compatible dc or bitmap that you created and so you are leaking in the above code. It could be that you are also doing other resource intensive code in the paint or resize messages.

Is your image not drawing at all? Is it flickering?

Other than that, I cannot replicate it.

This works just fine for me:

class Form2 : public BaseForm
{
    private:
        std::function<void(HWND, HDC)> onPaint;

    public:
        Form2(const char *Title, POINT Location, unsigned short Width, unsigned short Height, HWND Parent = NULL);
        LRESULT WinProc(HWND Hwnd, UINT Msg, WPARAM wParam, LPARAM lParam);
        int Show();
        HWND GetHWND() {return BaseForm::GetHWND();}

        void setPaintListener(const std::function<void(HWND, HDC)>& onPaint) {this->onPaint = onPaint;}
};

Form2::Form2(const char *Title, POINT Location, unsigned short Width, unsigned short Height, HWND Parent)
{
    BaseForm::Create(0, Title, WS_OVERLAPPEDWINDOW | WS_TABSTOP, Location, Width, Height, Parent);
}

LRESULT Form2::WinProc(HWND Hwnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
    switch(Msg)
    {
        case WM_PAINT:
        {
            PAINTSTRUCT ps = {0};
            HDC hdc = BeginPaint(Hwnd, &ps);

            if (onPaint)
            {
                onPaint(Hwnd, hdc);
            }

            EndPaint(Hwnd, &ps);
        }
        break;

        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;

        default:
            return DefWindowProc(Hwnd, Msg, wParam, lParam);
    }
    return 0;
}

int Form2::Show()
{
    MSG Messages = {0};
    ShowWindow(this->GetHWND(), SW_SHOW);

    while(GetMessage(&Messages, NULL, 0, 0))
    {
        TranslateMessage(&Messages);
        DispatchMessage(&Messages);
    }
    return Messages.wParam;
}

int main()
{
    Form2 f("Hello", {CW_USEDEFAULT, CW_USEDEFAULT}, 500, 500);

    //Load the image once.. you can do this in WM_CREATE if you want.
    HBITMAP hbmp = (HBITMAP)LoadImage(GetModuleHandle(NULL), "C:/Users/Brandon/Desktop/test.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);


    f.setPaintListener([&](HWND hwnd, HDC hdc){
        BITMAP bitmap;
        HDC hdcMem = CreateCompatibleDC(hdc);
        HGDIOBJ old = SelectObject(hdcMem, hbmp);

        GetObject(hbmp, sizeof(bitmap), &bitmap);
        BitBlt(hdc, 0, 0, bitmap.bmWidth, bitmap.bmHeight, hdcMem, 0, 0, SRCCOPY);
        SelectObject(hdcMem, old);
        DeleteDC(hdcMem);
    });

    int res = f.Show();
    DeleteObject(hbmp); //DELETE the loaded image.. You can do this in WM_DESTROY if you want.
    return res;
}
commented: thanks anyway +3

i, now, understand why don't works ;)
see my form window procedure:

static LRESULT CALLBACK WndProcForm(HWND HandleWindow, UINT msg, WPARAM wParam, LPARAM lParam)
        {
            static POINT PreviousLocation, Location;
            static bool Tracking = false;
            static MouseButtons MBButtons;
            static bool blControl = false;
            static bool blShift = false;
            static bool blResize = false;
            static int xPos = 0;
            static int yPos = 0;
            static UINT_PTR timerid;
            static bool blnDrag = false;
            static bool KeyPressed=false;
            static int KeyDownCount=0;
            form *inst = (form *)GetWindowLongPtr(HandleWindow, GWLP_USERDATA);

            if(inst!=NULL && inst->Create!=NULL && IsWindowVisible(inst->hWnd())==true)
            {
                static bool i=true;
                 if(i==true)
                 {
                    i=false;
                    inst->Create(inst->getLeft(), inst->getTop());
                    int a=GetLastError();
                    if (a!=0)
                        MessageBox(NULL,to_string(a).c_str(),"error",MB_OK);
                 }
            }

            if(inst!=NULL && inst->Paint!=NULL)
            {
                 static bool i=true;
                 if(i==true)
                 {
                     i=false;
                     SendMessage(inst->hwnd,WM_PAINT,wParam,lParam);
                 }
            }

            HBRUSH g_hbrBackground = NULL;

            //Working with messages
            switch(msg)
            {
                case WM_NCCREATE:
                {
                    CREATESTRUCT *p = (CREATESTRUCT *)lParam;
                    inst = (form *)p->lpCreateParams;
                    SetWindowLongPtr(HandleWindow, GWLP_USERDATA, (LONG_PTR)inst);
                    inst->hwnd = HandleWindow;
                }
                break;

the WM_PAINT message don't call my Paint() lambda, because, when i create the lambda, the Paint() is NULL. only works fine, because when we resize the form, the Paint() is called.
so i did that 2 'if': 1 for the Create() and another for the Paint()... but needs more work :(
anotherthing that i had tested too: if i do a constructor for recive the Paint(), it will works fine ;)

i resolve the problem adding these line:

ShowWindow(hwnd, SW_NORMAL);
UpdateWindow(hwnd);
InvalidateRect(hwnd,NULL,true);//i add these line for fix that

after create the form.
thanks for all

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.