【问题标题】:Win32 API - HWND "{unused = ???} Unable to read memory" errorWin32 API - HWND“{unused = ???} 无法读取内存”错误
【发布时间】:2020-06-02 14:53:56
【问题描述】:

我的项目是 C++ Windows Desktop Wizard AKA Win32 API 项目。

在函数 WinMain(...) 我正在创建我的窗口:

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

之后由于某种原因,我的窗口状态为“无法读取内存”(因此我无法创建没有此问题的 GUI)。我什至检查了谷歌的第二页以找到解决此问题的方法。微软的文档没有帮助:我检查了实现,它适合我的。我很长一段时间都无法修复它,我是盲人,我之前写错了,尽管我之前创建了 2 个工作项目(比较没有给出任何结果)。

整个代码如下:

#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<

提前感谢您的帮助。

【问题讨论】:

  • 什么是“问题”HWND 是一个不透明的句柄。只有它的是重要的。它在 C 中被实现为指针类型的事实是一个实现细节,它恰好出现在调试器中,它试图将它视为指针(它不是)。尝试读取指向的内存失败。这并不表示手柄本身有任何问题。
  • VS 调试器将始终将unused 显示为???,因为HWND 只是void*. 调试器尝试将其视为一个并尝试从内存位置读取,这很可能是无效的。您确定这是不允许您创建窗口的问题吗?
  • @ArushAgarampur 如果HWND 真的是void*,那么调试器中不会出现unused 数据成员。 HWNDvoid* 仅当在编译期间未定义 STRICT 时。否则,它是一个HWND__*,而HWND__ 结构有一个unused 数据成员。这就是调试器显示unused 的原因。
  • @RemyLebeau 是的,你是对的。我应该提到它是void*,而STRICT 未定义。

标签: c++ windows winapi createwindowex


【解决方案1】:

在编译期间启用STRICT Type Checking 时,HWNDHWND__* 的类型定义,其中HWND__ 是具有unused 数据成员的结构(仅因为struct 不能合法为空在 C 中,但在 C++ 中可以),例如:

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);

所以,当定义STRICT 时,HWNDstruct HWND__* 的别名,否则它是PVOID (void*) 的别名。与许多其他句柄类型相同(HHOOKHEVENTHGDIOBJHBITMAP 等)。

如果您的调试器在查看 HWND 时向您显示 unused 成员,这意味着您正在使用定义的 STRICT 进行编译(这是一件好事,您应该这样做)。调试器看到一个指向类型的指针,并尝试显示该类型包含的数据。

但是,HWND 并不是真正的指向内存中 struct HWND__ 的指针。它实际上只是内核提供的一个不透明值HWND 实际上指的是内核私有的,用户模式调试器无法知道它到底是什么。

STRICT 处理只是为了编译时类型安全而提供,因此用户代码不会意外传递其他需要 HWND 的句柄类型,反之亦然。

在您的情况下,您的HWND 的值为0x00170344,这意味着CreateWindowEx() 没有失败,HWND 本身是有效的。但是0x00170344 不是应用程序地址空间中的有效内存地址,因此当调试器尝试访问该地址的unused 成员时,它会失败并显示“无法读取内存”。 unused 成员在用户模式调试器中永远不会有效(这就是它“未使用”的原因)。唯一重要的是HWND 本身的 是否为0

【讨论】:

  • 嗯,谢谢你的解释。主要问题是 SetWindowText(label1, "any text") ,我之前没有提到这一点是我的错。看起来我的子窗口 label1 (类“静态”)无法读取,调试后我得出的结论是我的 GUI 窗口实现错误,因为我的hWnd 实现错误。这就是为什么我只展示了与之相关的代码。创建一个标签是正确的,我检查了所有的论点。
  • @Vanes 这是一个完全不同的问题,与您最初询问的内容无关。因此,请发布有关该问题的新问题,并确保显示所有不适合您的相关代码(即创建标签并尝试设置其文本的 WndProc 代码)
猜你喜欢
  • 2021-08-09
  • 2011-01-21
  • 1970-01-01
  • 1970-01-01
  • 2021-01-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多