遵循官方 direct2d 示例,但出现访问冲突错误 [重复]

Posted

技术标签:

【中文标题】遵循官方 direct2d 示例,但出现访问冲突错误 [重复]【英文标题】:follow official direct2d sample but got access violation error [duplicate] 【发布时间】:2019-08-16 09:44:43 【问题描述】:

按照 Direct2D (https://docs.microsoft.com/en-us/windows/win32/direct2d/direct2d-quickstart) 的官方教程使用 Visual Studio 2019 创建示例项目。在 x86 中运行代码时,在将平台更改为 x64 时一切正常,我收到一条错误消息:“异常抛出:读取访问冲突。在 SampleD2D.cpp 中。 (该行在下面的代码中被注释)

错误是:

Exception thrown: read access violation.
this was 0xBB18F6E8.
    LRESULT CALLBACK DemoApp::WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)

    LRESULT result = 0;

    if (message == WM_CREATE)
    
        LPCREATESTRUCT pcs = (LPCREATESTRUCT)lParam;
        DemoApp* pDemoApp = (DemoApp*)pcs->lpCreateParams;

        ::SetWindowLongPtrW(
            hwnd,
            GWLP_USERDATA,
            PtrToUlong(pDemoApp)
        );

        result = 1;
    
    else
    
        DemoApp* pDemoApp = reinterpret_cast<DemoApp*>(static_cast<LONG_PTR>(
            ::GetWindowLongPtrW(
                hwnd,
                GWLP_USERDATA
            )));

        bool wasHandled = false;

        if (pDemoApp)
        
            switch (message)
            
            case WM_SIZE:
            
                UINT width = LOWORD(lParam);
                UINT height = HIWORD(lParam);
                pDemoApp->OnResize(width, height); // throw the error!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            
            result = 0;
            wasHandled = true;
            break;

            case WM_DISPLAYCHANGE:
            
                InvalidateRect(hwnd, NULL, FALSE);
            
            result = 0;
            wasHandled = true;
            break;

            case WM_PAINT:
            
                pDemoApp->OnRender();
                ValidateRect(hwnd, NULL);
            
            result = 0;
            wasHandled = true;
            break;

            case WM_DESTROY:
            
                PostQuitMessage(0);
            
            result = 1;
            wasHandled = true;
            break;
            
        

        if (!wasHandled)
        
            result = DefWindowProc(hwnd, message, wParam, lParam);
        
    

    return result;

【问题讨论】:

您是否更改了其他内容?您的系统规格是什么? 嘿,我没有改变任何东西。这是带有 i7-8700 CPU 的 windows 10 pro 顺便说一句。我查看了您的链接并在第 4 部分中发现了错误的代码(如您所声称的)。您可以制作一个示例错误报告(并向我致以问候)。 ;-) MS 在示例中有一个错误,不想修复。这是一个更好的:github.com/Const-me/Direct2D-demo @Sonts 我没想到会出现这样的完全相同的副本。 (我想知道为什么我在谷歌搜索时没有找到它......)另一方面,OP 并不是第一个进入这个陷阱的人。很遗憾(对于MS)。 ;-)(或者,磨砺你的眼睛真的很有价值吗?这里和那里的小错误迫使你(真的)了解发生了什么......) 【参考方案1】:

不幸的是,我不是 WinAPI 专家,但出于好奇,我在 Google 上搜索了一下。现在,我很确定 OPs 问题:

        ::SetWindowLongPtrW(
            hwnd,
            GWLP_USERDATA,
            PtrToUlong(pDemoApp)
        );

特别是PtrToUlong(pDemoApp)

这可能适用于 32 位应用程序,但不适用于 64 位。

long 在 MS VC++ 32 位中 - 适用于 x86 和 x64 平台。

因此,将指针转换为 longunsigned long 有助于在 x64 上出错(只要高 32 位不为 0,这可能很难预测)。

谷歌搜索我发现的这个方向,例如PtrToUlong Q/A on gamedev.net 这个(旧)答案:

msdn,尽量避免使用这些,因为您将指针转换为无符号长整数。这可能在 32 位可执行文件上正常工作,但如果您在 64 位中编译,您可能会遇到问题。

这支持了我的怀疑。

根据 MS 文档。 SetWindowLongPtrW function,签名为:

LONG_PTR SetWindowLongPtrW(
  HWND     hWnd,
  int      nIndex,
  LONG_PTR dwNewLong
);

所以,这应该可以解决它:

        ::SetWindowLongPtrW(
            hwnd,
            GWLP_USERDATA,
            reinterpret_cast<LONG_PTR>(pDemoApp)
        );

请注意 MS 文档。关于LONG_PTR

LONG_PTR

指针精度的有符号长类型。在将指针转换为 long 以执行指针运算时使用。

此类型在 BaseTsd.h 中声明如下:

C++

#if defined(_WIN64)
 typedef __int64 LONG_PTR; 
#else
 typedef long LONG_PTR;
#endif

顺便说一句。我也没看懂

        DemoApp* pDemoApp = reinterpret_cast<DemoApp*>(static_cast<LONG_PTR>(
            ::GetWindowLongPtrW(
                hwnd,
                GWLP_USERDATA
            )));

根据文档。 GetWindowLongPtrW function,函数返回LONG_PTR。那么,为什么是static_cast&lt;LONG_PTR&gt;?如果绝对必要,类型转换应该始终是最后的手段。 (虽然,我承认没有 WinAPI 可能无法使用。)

【讨论】:

以上是关于遵循官方 direct2d 示例,但出现访问冲突错误 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

检测超出数组访问冲突

遍历数组和线程时访问冲突

Laravel 5.8:总是给出多个主键语法或访问冲突错误

rocketmq官方示例超时报错处理

使用 model_main.py 进行训练对象检测失败并出现 Windows 致命异常:访问冲突

运行spark官方的graphx 示例 ComprehensiveExample.scala报错解决