1

My project is C++ Windows Desktop Wizard AKA Win32 API project.

In function WinMain(...) I'm creating my window:

hWnd = CreateWindowEx(NULL, _T("DesktopApp"), _T("Hi, I'm window"), WS_OVERLAPPEDWINDOW,
    CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, NULL, NULL, hInstance, NULL);

And after that for some reason my window has status "unable to read memory" (therefore I can't create GUIs without this issue). I checked even 2nd page of Google to find a resolve of this problem. Microsoft's documentation didn't help: I checked the implementation and it fits with mine. I can't fix it for a long time, mb I'm blind and I wrote smth wrong before, despite I've created 2 working projects before (comparison didn't give any results).

Here goes the whole code:

#include <windows.h>
#include <stdlib.h>
#include <string>
#include <tchar.h>
#include <shellapi.h>
#include <ctime>

HWND hWnd;
HINSTANCE hInst;

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int CALLBACK WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow)
{
    WNDCLASSEX wcex;

    ZeroMemory(&wcex, sizeof(wcex));
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
    wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcex.lpszMenuName = NULL;
    wcex.lpszClassName = szWindowClass;
    wcex.hIconSm = LoadIcon(wcex.hInstance, IDI_APPLICATION);

    if (!RegisterClassEx(&wcex))
    {
        MessageBox(NULL,
            _T("Call to RegisterClassEx failed!"),
            _T("Windows Desktop Guided Tour"),
            NULL);
        return 1;
    }

    hInst = hInstance;

    hWnd = CreateWindowEx(NULL, _T("DesktopApp"), _T("Hi, I'm window"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, NULL, NULL, hInstance, NULL);
    if (!hWnd)
    {
        MessageBox(NULL,
            _T("Call to CreateWindow failed!"),
            _T("Windows Desktop Guided Tour"),
            NULL);
        return 1;
    }

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

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

    return (int)msg.wParam;
}

>how it looks in debugger<

Thanks in advance for help.

Vanes
  • 41
  • 3
  • 7
    What's *"the issue"*? A `HWND` is an opaque handle. Only its **value** is important. The fact that it's implemented as a pointer type in C is an implementation detail, that happens to surface in the debugger, which tries to treat it like a pointer (which it isn't). The attempt to read the pointed-to memory fails. That doesn't indicate any sort of issue with the handle itself. – IInspectable Jun 02 '20 at 15:02
  • The VS debugger will always show the `unused` as `???` because the `HWND` is just a `void*.` The debugger tries to treat it like one and attempts to read from the memory location, which is most likely invalid. Are you sure this is the problem that is not letting you create a window? – Arush Agarampur Jun 02 '20 at 18:11
  • 2
    @ArushAgarampur If the `HWND` were really a `void*` then no `unused` data member would appear in the debugger. `HWND` is a `void*` only when `STRICT` is not defined during compiling. Otherwise, it is an `HWND__*` instead, and the `HWND__` struct has an `unused` data member. That is why the debugger shows `unused` at all. – Remy Lebeau Jun 02 '20 at 20:32
  • @RemyLebeau Yep, you're right. I should have mentioned that it is a `void*` when `STRICT` is not defined. – Arush Agarampur Jun 02 '20 at 23:54

1 Answers1

3

When STRICT Type Checking is enabled during compiling, HWND is a typedef for HWND__*, where HWND__ is a struct with an unused data member (only because a struct can't legally be empty in C, but it can in C++), eg:

winnt.h:

#ifdef STRICT
typedef void *HANDLE;
#if 0 && (_MSC_VER > 1000)
#define DECLARE_HANDLE(name) struct name##__; typedef struct name##__ *name
#else
#define DECLARE_HANDLE(name) struct name##__{int unused;}; typedef struct name##__ *name
#endif
#else
typedef PVOID HANDLE;
#define DECLARE_HANDLE(name) typedef HANDLE name
#endif

windef:

DECLARE_HANDLE            (HWND);

So, when STRICT is defined, HWND is an alias for struct HWND__*, otherwise it is an alias for PVOID (void*). Same with MANY other handle types (HHOOK, HEVENT, HGDIOBJ, HBITMAP, etc).

For your debugger to be showing you the unused member when viewing an HWND, that means you are compiling with STRICT defined (which is a good thing, you should be). The debugger sees a pointer to a type, and tries to display the data that type contains.

However, an HWND is not REALLY a pointer to a struct HWND__ in memory. It is actually just an opaque value provided by the kernel. What an HWND actually refers to is private to the kernel, a user-mode debugger has no way of knowing what that really is.

STRICT handling is merely provided for compile-time type safety, so that user code can't accidentally pass other handle types where an HWND is expected, and vice versa.

In your case, your HWND has a value of 0x00170344, which means CreateWindowEx() is not failing, the HWND itself is valid. But 0x00170344 is not a valid memory address in your app's address space, so when the debugger tries to access the unused member at that address, it fails with "unable to read memory". The unused member will NEVER be valid in a user-mode debugger (that is why it is "unused"). The only thing that is important is whether the value of the HWND itself is 0 or not.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 1
    M-hm, thanks for explaining. The main problem is with `SetWindowText(label1, "any text")` , my bad that I didn't mention this before. Looks like my child-window **label1** (class "static") is unable to be read and after debugging I came to conclusion that my GUI window is implemented wrong cuz of wrong implementation of my `hWnd`. That's why I've shown only the code related to it. Creating a label is correct, I checked all the arguements. – Vanes Jun 03 '20 at 06:00
  • 1
    @Vanes that is a completely different issue, unrelated to what you asked about originally. So please post a new question about THAT issue, and be sure to show all of the relevant code that is not working for you (ie, the `WndProc` code that creates the label and tries to set its text) – Remy Lebeau Jun 03 '20 at 16:30