Joymaker
Joymaker

Reputation: 1428

Incorrect error report from VC++ debugger

I'm debugging C++ in Visual Studio on Windows 11 on an ARM architecture.

I try using LoadImageA() to get an HBITMAP, and the debugger tells me that I've got an erroneous pointer that can't be dereferenced. Indeed, I do it twice and get two very different looking pointers, both of which display as erroneous. But it isn't true! When the last line displayed below actually executes, the green triangle appears! Obviously it loaded correctly and was there all along.

So, what is this report of "Unable to read memory"? Has something been lost in transition between 32-bit and 64-bit architectures?

To prevent confusion, let me explain something: my function LoadImage(), which my library presents to its user, is kept distinct by an #undef from LoadImageA(), which is in fact the Win32 API's function.

struct Image {
    ImageType type; 
    void *data;         // type- and system-dependent information
    int width, height;  // natural dimensions, in pixels
    Rect bounds;

    Image() : type(nullImage), data(NULL), width(0), height(0),
              bounds(nullRect) {};
    Image(ImageType t, int w, int h) 
        : type(t), data(NULL), width(w), height(h),
              bounds(0, 0, w, h) {};

    virtual void draw(int x, int y);
        // draw at normal size with UL corner at x, y
};

struct BitmapImage : Image {
    HBITMAP bits;
    BitmapImage() { bits = NULL; } 
    virtual void draw(int x, int y);
};

// load an image from a file or resource.  Returns an image with type==nullImage if failed.
Image *LoadImage(apstring name) {
    HBITMAP hh = (HBITMAP)LoadImageA(theInstance, "GreenTriangle.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
    // . . .
        HBITMAP h = (HBITMAP) LoadImageA(theInstance, name.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
        BitmapImage* b = new BitmapImage;
        b->bits = h;
        return b;
}

// in the caller:
    Image* im = LoadImage("GreenTriangle.bmp");
    im->draw(200, 200);

Here is the debugger state just before executing return b;:

image

And here is the final outcome: a green triangle!

image

So, what are these red errors all about?

Upvotes: 0

Views: 32

Answers (1)

Remy Lebeau
Remy Lebeau

Reputation: 597146

You are compiling with STRICT Type Checking enabled, so the compiler and debugger see HBITMAP as a pointer to an HBITMAP__ struct type that has an unused data member.

In windef.h, HBITMAP is declared like this:

DECLARE_HANDLE(HBITMAP);

In winnt.h, DECLARE_HANDLE() is declared like this:

#ifdef STRICT
...
#define DECLARE_HANDLE(name) struct name##__{int unused;}; typedef struct name##__ *name
...
#else
...
#define DECLARE_HANDLE(name) typedef HANDLE name
...
#endif

This means that under STRICT, the compiler and debugger see HBITMAP declared as this:

struct HBITMAP__{int unused;};
typedef struct HBITMAP__ *HBITMAP;

Many other Win32 API handle types are similar declared with DECLARE_HANDLE(). This system ensures that each handle type is a distinct type, thus allowing the compiler to ensure at compile-time that you don't accidentally mix up incompatible handle types in APIs, for example you don't use an HBITMAP where an HWND is expected, etc.

But, LoadImageA() returns an opaque void* pointer, which is NOT actually pointing at a valid instance of the HBITMAP__ struct. So, the debugger is right to complain that it can't read the unused data member, because it is not actually real!

However, the code is perfectly valid, and will perform correctly at runtime.

Upvotes: 1

Related Questions