-1

I've been trying to load a simple bitmap and draw a button using that bitmap. So far, so good. I've found that you're supposed to be able to create said button with the BS_OWNERDRAW flag and intercept the WM_DRAWITEM message in WndProc. Which is exactly what I've done. If I draw simple shapes such as FillRect, it works very well. GetLastError() also returns 0. The bitmap is loaded from a resource file, compiled with windres and linked along with the rest with gcc. If I extract the .bmp using 7zip from the .exe, I have no trouble opening it with anything. I've also tried with more than one, even drawn one using paint, the bitmap itself seems fine. Here's the code:

void on_WM_DRAWITEM(DRAWITEMSTRUCT* drawItem, HWND hwnd)
{
    HRSRC bmRes = FindResourceW(NULL, (LPCWSTR)MAKEINTRESOURCE(MY_BITMAP), (LPCWSTR)RT_BITMAP);
    HBITMAP hBm = LoadResource(NULL, bmRes);
    BITMAP bm;
    HDC memDC = CreateCompatibleDC(drawItem->hDC);
    if(hBm != NULL)
    {
        printf("HRSRC: %p\n", bmRes);
        printf("HBITMAP: %p\n", hBm);
        printf("HDC: %p\n", memDC);

        if(GetObject(hBm, sizeof(bm), &bm) != 0)
        {
            printf("Not printing\n");
            WINBOOL b = DrawStateW(memDC, NULL, NULL, (LPARAM)hBm, 0, 0, 0, 0, 0, DST_BITMAP);
            //BitBlt(drawItem->hDC, 0, 0, 50, 50, memDC, 0, 0, SRCCOPY);
        }
        printf("BITMAP: %p\n", bm);
        printf("BITMAP BITS: %p\n", bm.bmBits);
    }
    DeleteDC(memDC);
    DeleteObject(bmRes);
    DeleteObject(hBm);
}

The output of these prints:

HRSRC: 00007ff665ce70a0
HBITMAP: 00007ff665d484c0
HDC: 000000002d010e19
BITMAP: 000000d387bfef70
BITMAP BITS: 000014e0000014e0

Everything seems valid, nothing is null. Despite failing, the bits seems like they're getting set. I've tried to check the memory at this address, but it doesn't seem like I have access to this address range. I'm not sure how windows handles are supposed to work vs pointers, am I supposed to be able to print them like that, as pointers?

Even if I put the BitBlt or DrawStateW, both of which are supposed to work, outside the if, they both fail as well.

Compiled using gcc with MinGW.

Etienne Poulin
  • 178
  • 1
  • 4
  • 15
  • Re: handles: https://stackoverflow.com/questions/902967/what-is-a-windows-handle – Dai Oct 25 '22 at 01:28
  • @Dai Yes, I've read that any many other posts on the subject, the general explanation seems to be that it's context specific and may or may not be a pointer. In the case of my HRSRC and HBITMAP, it seems to be pointers, given the address. As for the HDC, I'd assume so, even though it's probably not memory from my module. Same goes for the bitmap bits. – Etienne Poulin Oct 25 '22 at 01:34
  • Real Win32 Handles are never pointers. They’re opaque integers. Nothing more. – Dai Oct 25 '22 at 01:34
  • @Dai What? What else can they be? The first two addresses in my post are clearly pointers, no? And what do you mean by real? Are there fake handles? If they're returned by the api, I can only assume they're real. – Etienne Poulin Oct 25 '22 at 01:37
  • @Dai My bad, didn't see the edit. But regardless of whether they're pointers or not, they're non zero values returned by the api, which means that windows considers them to be valid, therefore passing them to other windows functions should work, no? – Etienne Poulin Oct 25 '22 at 01:39
  • What is the **actual** erroneous return-value from GetObject that you’re seeing? And where are calling GetLastError? – Dai Oct 25 '22 at 02:13
  • @Dai It's zero, which is why I have a if != 0. According to the documentation, a return value of zero indicates the function failed. I've ran a few more tests, SizeOfResource returns the correct size, but if I get a pointer with LockResource and examine memory at that address, I clearly don't get my bitmap (starting with ASCII BM6) and get something like that: https://imgur.com/a/W2zgFf8 – Etienne Poulin Oct 25 '22 at 02:16
  • @Dai Nevermind, I was wrong, it really is my bitmap, but it skips the file header... I guess I could just do it that way, load it in memory with LockResource as a binary file. Would that be an okay thing to do? Also could the fact that the header isn't saved be why GetObject fails? – Etienne Poulin Oct 25 '22 at 02:20
  • Can you upload a `.zip` containing your entire project (and a simple build command....)? I'd like to see what's going-on myself. – Dai Oct 25 '22 at 02:47
  • https://ufile.io/zfyrqjto Can be build with a simple make (mingw's make), but might need to edit the MinGW path. I'll be back tomorrow. I know there's tons of unsafe stuff, such as potentially calling null function pointers, but this will be taken care of eventually. – Etienne Poulin Oct 25 '22 at 02:58
  • MinGW? Uhhhh... that might be the problem right there... Why aren't you building with VisualC++? Does it work if you use a different compiler+linker+toolchain? – Dai Oct 25 '22 at 03:36
  • 1
    @Dai `GetObject()` doesn't report an error code via `GetLastError()` on failure. Most GDI functions don't. – Remy Lebeau Oct 25 '22 at 04:04

1 Answers1

3

LoadResource() returns an HGLOBAL handle to a block of memory, not an HBITMAP handle to a bitmap image. They do not represent the same thing, so GetObject() fails due to you giving it the wrong type of handle.

You should enable STRICT Type Checking in your project so your code will fail to compile when mismatches like this occur.

The correct way to get an HBITMAP handle for a bitmap in a resource is to use LoadBitmap() or LoadImage() instead, eg:

void on_WM_DRAWITEM(DRAWITEMSTRUCT* drawItem, HWND hwnd)
{
    HBITMAP hBm = (HBITMAP) LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(MY_BITMAP), IMAGE_BITMAP, 0, 0, 0);
    if (hBm != NULL)
    {
        ...
        DeleteObject(hBm);
    }
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • You're right, it seems to work with LoadImage. I was casting with LoadResource because I read about people doing this, but the threads were some 15 years old. LoadBitmap doesn't work, but casting LoadImage works well, thanks! As for STRICT, I believe it may already be enabled with MinGW? I'm getting a ton of warnings about it already being defined. – Etienne Poulin Oct 25 '22 at 15:51