Reputation: 1428
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;
:
And here is the final outcome: a green triangle!
So, what are these red errors all about?
Upvotes: 0
Views: 32
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