-3

I am trying to load a bitmap in a Win32 application, but for some strange reason the bitmap does not load. Here is what I have so far:

HANDLE hImg = LoadImageW(
    NULL,
    L"img.bmp",
    IMAGE_BITMAP,
    0,
    0,
    LR_LOADFROMFILE
);
if (hImg == NULL) {
    std::cout << GetLastError();
}

Compiled on GCC 8.1.0 with -Wall -municode.

Nothing is output to the console, so there are no errors. However, the image never shows up. These questions seem to address a similar issue, but I have had a look at them and couldn't find a solution:

Where could the problem be?

Full code:

#ifndef UNICODE
#define UNICODE
#endif

#include <windows.h>
#include <iostream>

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) {
    const wchar_t CLASS_NAME[] = L"Window Class";

    WNDCLASS wc = {};

    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = CLASS_NAME;

    RegisterClass(&wc);

    HWND hwnd = CreateWindowEx(
        0,
        CLASS_NAME,
        L"My Application",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        NULL,
        NULL,
        hInstance,
        NULL
    );

    if (hwnd == NULL) {
        return 0;
    }

    ShowWindow(hwnd, nCmdShow);

    HANDLE hImg = LoadImageW(
        NULL,
        L"img.bmp",
        IMAGE_BITMAP,
        0,
        0,
        LR_LOADFROMFILE
    );
    if (hImg == NULL) {
        std::cout << GetLastError();
    }

    MSG msg = {};
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return 0;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;

        case WM_PAINT: {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hwnd, &ps);

            FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));

            EndPaint(hwnd, &ps);
            break;
        }
    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
Wais Kamal
  • 5,858
  • 2
  • 17
  • 36

2 Answers2

4

You are merely loading the bitmap into memory, but you are not actually displaying it anywhere. You need to either:

  • create a child STATIC control on your window, giving it the SS_BITMAP window style, and then send it a STM_SETIMAGE message.

  • have your window's WM_PAINT message handler select the bitmap into an in-memory HDC using CreateCompatibleDC() and SelectObject(), and then copy that HDC onto your window's HDC (returned by BeginPaint()) using BitBlt() or StretchBlt().

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
3

You just need to change the window procedure to do all the drawing in WM_PAINT. Once the image is loaded successfully, create a memory DC, select the bitmap into the memory DC, and draw the memory DC onto the target window DC.

When the bitmap handle from LoadImage is no longer needed, it should be deleted DeleteObject (or DestroyCursor/DestroyIcon depending on what resource was loaded)

HANDLE hImg = NULL;
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 
{
    switch (uMsg) 
    {
    case WM_CREATE:
        if (!hImg)
            hImg = LoadImage(NULL, L"img.bmp", IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
        if (!hImg) { DWORD err = GetLastError(); std::cout << err; }
        return 0;

    case WM_PAINT: 
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hwnd, &ps);
        FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));

        if (hImg) {
            BITMAP bm;
            GetObject(hImg, sizeof(bm), &bm);//get bitmap dimension
            auto memdc = CreateCompatibleDC(hdc);
            auto oldbmp = SelectObject(memdc, (HBITMAP)hImg);
            BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, memdc, 0, 0, SRCCOPY);
            SelectObject(memdc, oldbmp);//restore memdc
            DeleteDC(memdc);//delete memdc, we don't need it anymore
        }

        EndPaint(hwnd, &ps);
        return 0;
    }

    case WM_DESTROY:
        if (hImg) DeleteObject(hImg);//release resource
        hImg = NULL;
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

When using GetLastError, make sure the function is called immediately after failure. Example:

if (hImg == NULL) 
{
    DWORD err = GetLastError();
    std::cout << err << '\n';
}

If UNICODE is already defined, we can use LoadImage instead of LoadImageW

Barmak Shemirani
  • 30,904
  • 6
  • 40
  • 77