Ошибка C++ Directx: нарушение прав доступа, цепочка обмена была nullptr

Я пытаюсь изучить 3D-рисование с помощью DirectX, и я получил этот код в учебном пособии, но каждый раз, когда я пытаюсь его скомпилировать, мой компилятор выдает ошибку «Нарушение прав доступа».

заранее спасибо

   // include the basic windows header files and the Direct3D header files
    #include <windows.h>
    #include <windowsx.h>
    #include <d3d11.h>
    #include <d3dx11.h>
    #include <d3dx10.h>

    // include the Direct3D Library file
    #pragma comment (lib, "d3d11.lib")
    #pragma comment (lib, "d3dx11.lib")
    #pragma comment (lib, "d3dx10.lib")

    // global declarations
    IDXGISwapChain *swapchain;             // the pointer to the swap chain interface
    ID3D11Device *dev;                     // the pointer to our Direct3D device interface
    ID3D11DeviceContext *devcon;           // the pointer to our Direct3D device context
    ID3D11RenderTargetView *backbuffer;    // the pointer to our back buffer

    // function prototypes
    void InitD3D(HWND hWnd);    // sets up and initializes Direct3D
    void RenderFrame(void);     // renders a single frame
    void CleanD3D(void);        // closes Direct3D and releases memory

    // the WindowProc function prototype
    LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);


    // the entry point for any Windows program
    int WINAPI WinMain(HINSTANCE hInstance,
                       HINSTANCE hPrevInstance,
                       LPSTR lpCmdLine,
                       int nCmdShow)
    {
        HWND hWnd;
        WNDCLASSEX wc;

        ZeroMemory(&wc, sizeof(WNDCLASSEX));

        wc.cbSize = sizeof(WNDCLASSEX);
        wc.style = CS_HREDRAW | CS_VREDRAW;
        wc.lpfnWndProc = WindowProc;
        wc.hInstance = hInstance;
        wc.hCursor = LoadCursor(NULL, IDC_ARROW);
        wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
        wc.lpszClassName = L"WindowClass";

        RegisterClassEx(&wc);

        RECT wr = {0, 0, 800, 600};
        AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE);

        hWnd = CreateWindowEx(NULL,
                              L"WindowClass",
                              L"Our First Direct3D Program",
                              WS_OVERLAPPEDWINDOW,
                              300,
                              300,
                              wr.right - wr.left,
                              wr.bottom - wr.top,
                              NULL,
                              NULL,
                              hInstance,
                              NULL);

        ShowWindow(hWnd, nCmdShow);

        // set up and initialize Direct3D
        InitD3D(hWnd);

        // enter the main loop:

        MSG msg;

        while(TRUE)
        {
            if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);

                if(msg.message == WM_QUIT)
                    break;
            }

            RenderFrame();
        }

        // clean up DirectX and COM
        CleanD3D();

        return msg.wParam;
    }


    // this is the main message handler for the program
    LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
        switch(message)
        {
            case WM_DESTROY:
                {
                    PostQuitMessage(0);
                    return 0;
                } break;
        }

        return DefWindowProc (hWnd, message, wParam, lParam);
    }


    // this function initializes and prepares Direct3D for use
    void InitD3D(HWND hWnd)
    {
        // create a struct to hold information about the swap chain
        DXGI_SWAP_CHAIN_DESC scd;

        // clear out the struct for use
        ZeroMemory(&scd, sizeof(DXGI_SWAP_CHAIN_DESC));

        // fill the swap chain description struct
        scd.BufferCount = 1;                                    // one back buffer
        scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;     // use 32-bit color
        scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;      // how swap chain is to be used
        scd.OutputWindow = hWnd;                                // the window to be used
        scd.SampleDesc.Count = 1;                               // how many multisamples
        scd.SampleDesc.Quality = 0;                             // multisample quality level
        scd.Windowed = TRUE;                                    // windowed/full-screen mode

        // create a device, device context and swap chain using the information in the scd struct
        D3D11CreateDeviceAndSwapChain(NULL,
                                      D3D_DRIVER_TYPE_HARDWARE,
                                      NULL,
                                      NULL,
                                      NULL,
                                      NULL,
                                      D3D11_SDK_VERSION,
                                      &scd,
                                      &swapchain,
                                      &dev,
                                      NULL,
                                      &devcon);


        // get the address of the back buffer
        ID3D11Texture2D *pBackBuffer;
        swapchain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer);

        // use the back buffer address to create the render target
        dev->CreateRenderTargetView(pBackBuffer, NULL, &backbuffer);
        pBackBuffer->Release();

        // set the render target as the back buffer
        devcon->OMSetRenderTargets(1, &backbuffer, NULL);


        // Set the viewport
        D3D11_VIEWPORT viewport;
        ZeroMemory(&viewport, sizeof(D3D11_VIEWPORT));

        viewport.TopLeftX = 0;
        viewport.TopLeftY = 0;
        viewport.Width = 800;
        viewport.Height = 600;

        devcon->RSSetViewports(1, &viewport);
    }


    // this is the function used to render a single frame
    void RenderFrame(void)
    {
        // clear the back buffer to a deep blue
        devcon->ClearRenderTargetView(backbuffer, D3DXCOLOR(0.0f, 0.2f, 0.4f, 1.0f));

        // do 3D rendering on the back buffer here

        // switch the back buffer and the front buffer
        swapchain->Present(0, 0);
    }


// this is the function that cleans up Direct3D and COM
void CleanD3D(void)
{
    // close and release all existing COM objects
    swapchain->Release();
    backbuffer->Release();
    dev->Release();
    devcon->Release();
}

person Erik Schulze    schedule 08.03.2017    source источник
comment
Правильный инструмент для решения таких проблем — ваш отладчик. Вы должны выполнить свой код построчно, прежде чем задать вопрос о переполнении стека. Для получения дополнительной помощи прочитайте Как отлаживать небольшие программы (по Эрик Липперт). Как минимум, вы должны [отредактировать] свой вопрос, включив в него минимальный, полный и проверяемый пример, который воспроизводит вашу проблему, а также наблюдения, которые вы сделали в отладчике.   -  person πάντα ῥεῖ    schedule 08.03.2017
comment
Вы никогда не проверяете какие-либо значения в своем коде.   -  person πάντα ῥεῖ    schedule 08.03.2017
comment
Если вам лень проверять результаты самостоятельно (либо в коде, либо с помощью отладчика), вы можете установить флаги на D3D11_CREATE_DEVICE_DEBUG в вызове создания устройства, и он предупредит вас, где вы впервые ошибетесь.   -  person Mike Vine    schedule 08.03.2017


Ответы (1)


При программировании COM AP, таких как Direct3D, очень важно проверять возвращаемое значение всех функций, которые возвращают HRESULT. Если безопасно игнорировать возвращаемое значение, оно вернет void.

Это означает использование макросов SUCCEEDED или FAILED для каждого HRESULT. Вам следует избегать выполнения == S_OK или != S_OK, так как есть другие успешные результаты, кроме S_OK. В современном C++ еще лучше использовать ThrowIfFailed для фатального быстрого сбоя. сценарии, в которых ваша программа не может восстановиться после сбоя.

Ваш код также не может проверить наличие ошибки в вызове RegisterClassEx или CreateWindowEx, что может привести к тому, что hWnd будет нулевым, прежде чем вы действительно начнете.

Вам также следует рассмотреть возможность использования nullptr вместо NULL. Вы передаете NULL в местах, которые на самом деле принимают UINT, который компилируется только потому, что вы используете определение препроцессора старой школы для 0.

После проверки возвращаемых значений следующим шагом будет включение устройства отладки. См. Анатомия Direct3D 11. Создайте устройство и Приемы отладки слоя Direct3D SDK. Уровень отладки будет предоставлять дополнительную информацию в окне вывода отладки при возврате распространенных ошибок, таких как E_INVALIDARG.

Обратите внимание, что BufferCount должно быть не менее 2. Все, что ниже 1, в любом случае рассматривается как 2.

Поскольку вы новичок в программировании Direct3D 11, я рекомендую вам ознакомиться с набором инструментов DirectX. учебники.

Вы также должны инициализировать все переменные.

IDXGISwapChain *swapchain = nullptr;
ID3D11Device *dev = nullptr;
ID3D11DeviceContext *devcon = nullptr;
ID3D11RenderTargetView *backbuffer = nullptr;

Вам действительно следует избегать использования «сырых» указателей для COM-объектов. Лучше использовать умный указатель COM, например ComPtr.

person Chuck Walbourn    schedule 09.03.2017