С++. Я получаю нарушение прав доступа в CreateWindow с геттером для получения строки имени класса

(коды ошибок скопированы и вставлены внизу)

Я следую онлайн-учебнику, но также имею 3 книги и закончил продвинутый курс C++ от Microsoft @ edX, но понимаю, что я все еще новичок. Я упоминаю об этом, чтобы люди не думали, что я учусь в первую очередь с помощью плохих руководств на YouTube, это не так.

Эта ошибка исходит из созданного мной класса окна С++ «Окно», который содержит другой класс, «Windowclass». Они используются для создания окон, а внутренний одноэлементный класс скрывает детали.

Я скопирую код, который, по моему мнению, имеет отношение к конструктору.

Кажется, проблема может быть в конструкторе окна @ hWnd = CreateWindow(Здесь, имя и т.д. и т.п.:

Window::Window(int width, int height, const char* name) noexcept

{
    RECT wr;
    wr.left = 100;
    wr.right = width + wr.left;
    wr.top = 100;
    wr.bottom = height + wr.top;
    AdjustWindowRect(&wr, WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU, false);
    //create window & get hWnd
    hWnd = CreateWindow(WindowClass::GetName(), name, WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | CW_USEDEFAULT | CW_USEDEFAULT, wr.right, wr.left, wr.bottom, wr.top, nullptr, nullptr, WindowClass::GetInstance(), this);

    ShowWindow(hWnd, SW_SHOWDEFAULT);
}

Если я изменю первый аргумент с CreateWindow на то, что, как я думаю, называется строковым литералом, то есть «Window» вместо геттера GetName(), тогда программа, похоже, работает нормально.

вот код геттера, обратите внимание, что геттер работает нормально в других местах. Например, когда он вызывается при создании имени класса окна

wc.lpszClassName = GetName();

Геттер:

const char* Window::WindowClass::GetName() noexcept {

    return wndClassName;
}

хотя я не хочу копировать слишком много кода, ниже приведен WindowClass, который находится внутри своего родительского класса «Window».

Также обратите внимание, что мое психическое здоровье не очень хорошее, и кодирование действительно помогает (с большим перерывом между ними) в восстановлении моего психического здоровья, а споры и оскорбления - нет. Но рад конструктивному отзыву.

class WindowClass
{
public:
    static const char* GetName() noexcept;
    static HINSTANCE GetInstance() noexcept;

private:
    WindowClass() noexcept;
    ~WindowClass();
    WindowClass(const WindowClass&) = delete;
    WindowClass& operator=(const WindowClass&) = delete;
    static constexpr const char* wndClassName = "Ryan Direct3d";
    static WindowClass wndClass;
    HINSTANCE hInst;
};

Я ценю все отзывы по этому поводу, которые относятся к делу. Почему я получаю: Исключение по адресу 0x00000000 в WindowsApp1.exe: 0xC0000005: Нарушение прав доступа, место выполнения 0x00000000. Необработанное исключение по адресу 0x7155CCE0 в WindowsApp1.exe: 0xC000041D: во время обратного вызова пользователя обнаружено необработанное исключение.

Код для компиляции ниже этой строки


#include <Windows.h>
#include <string>

class Window
{
private:
    //singleton class - manages registration and cleanup / of window  class (unregistered)
    class WindowClass
    {
    public:
        static const char* GetName() noexcept;
        static HINSTANCE GetInstance() noexcept;

    private:
        WindowClass() noexcept;
        ~WindowClass();
        WindowClass(const WindowClass&) = delete;
        WindowClass& operator=(const WindowClass&) = delete;
        //wndClassName called by getter
        static constexpr const char* wndClassName = "Ryan";
        static WindowClass wndClass;
        HINSTANCE hInst;
    };
public:

    Window(int width, int height, const char* name) noexcept;
    ~Window();
    Window(const Window&) = delete;
    Window& operator= (const Window&) = delete;

private:
    int width = 0;
    int height = 0;
    HWND hWnd;
};


//Window Class stuff
Window::WindowClass Window::WindowClass::wndClass; //
Window::WindowClass::WindowClass() noexcept : hInst(GetModuleHandle(nullptr))
{

    WNDCLASSEX wc = { 0 };
    wc.cbSize = sizeof(wc);
    wc.style = CS_OWNDC;
    //wc.lpfnWndProc = WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = GetInstance();
    wc.hIcon = nullptr;
    wc.hCursor = nullptr;
    wc.hbrBackground = nullptr;
    wc.lpszMenuName = nullptr;
    wc.lpszClassName = GetName();
    wc.hIconSm = nullptr;
    RegisterClassEx(&wc);

}


const char* Window::WindowClass::GetName() noexcept {

    return wndClassName;
}

HINSTANCE Window::WindowClass::GetInstance() noexcept {

    return wndClass.hInst;

}
Window::WindowClass::~WindowClass()
{
    UnregisterClass(wndClassName, GetInstance());
}


//Window Creation
Window::Window(int width, int height, const char* name) noexcept
{
    RECT wr;
    wr.left = 100;
    wr.right = width + wr.left;
    wr.top = 100;
    wr.bottom = height + wr.top;
    AdjustWindowRect(&wr, WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU, false);
    //create window & get hWnd
    hWnd = CreateWindow("Ryan", name, WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | CW_USEDEFAULT | CW_USEDEFAULT, wr.right, wr.left, wr.bottom, wr.top, nullptr, nullptr, WindowClass::GetInstance(), nullptr);
        ShowWindow(hWnd, SW_SHOW);
}


Window::~Window() {

    DestroyWindow(hWnd);
}



//wndprocs
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{

    switch (msg) //use a switch because in future we will add / process many kinds of messages
    {
    case WM_KEYUP:
        if (wParam == 'F')
        {
            SetWindowText(hWnd, "Dogmah");
        }
        break;

    case WM_CLOSE:
        PostQuitMessage(69);
        break;
    case WM_KEYDOWN:
        if (wParam == 'F')
        {
            SetWindowText(hWnd, "New Name");
        }
        break;
    case WM_CHAR:
    {
        static std::string title;
        title.push_back((char)wParam);
        SetWindowText(hWnd, title.c_str());
    }
    case WM_LBUTTONDOWN:
    {

    }

    }

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

int CALLBACK WinMain(
    HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR   lpCmdLine,
    int nCmdShow)
{

    Window Ryan(600, 500, "Hey");


    //message pump
    MSG msg;
    BOOL gResult;
    //register custom class for outputting EXTRA msg details

    while ((gResult = GetMessage(&msg, nullptr, 0, 0)) > 0)
    {
        TranslateMessage(&msg);

        DispatchMessage(&msg);
    }

    if (gResult == -1) { return -1; }

    else { return msg.wParam; }

    return 0;
}

person Ryan Birks    schedule 19.03.2020    source источник
comment
Можете ли вы опубликовать минимально воспроизводимый пример. Что-то, что мы можем напрямую скопировать в проект визуальной студии, скомпилировать и запустить?   -  person selbie    schedule 19.03.2020
comment
Да, но это 4 файла. Хотя они маленькие. Я должен скопировать текст здесь.   -  person Ryan Birks    schedule 19.03.2020
comment
Вместо того, чтобы публиковать 4 файла, опубликуйте один файл, который воспроизводит проблему. Это может заставить вас провести рефакторинг кода, но также может позволить вам найти ошибку самостоятельно.   -  person selbie    schedule 19.03.2020
comment
Я попробую, будет ли место, чтобы опубликовать это здесь, как правило?   -  person Ryan Birks    schedule 19.03.2020
comment
Вы можете скопировать весь код сюда, но это не то, что является минимальным воспроизводимым примером, вы должны создать фрагмент кода, который воспроизводит вашу проблему, удалив весь код, который, по вашему мнению, не связан, если вы перейдете по ссылке, если у вас есть подробное описание того, как вы должны это сделать.   -  person anastaciu    schedule 19.03.2020
comment
В настоящее время работаю над предложениями и пытаюсь сделать его меньше, но, как предложил Селби, это помогло мне отладить. Тем временем я еще больше сузил ошибку, что может дать вам необходимую информацию. Кажется, что если первым параметром функции CreateWindow является getter() или строка, которая точно совпадает с именем строки, содержащейся в переменной, это вызовет ошибку. То есть GetName() вызовет сбой, так как заменит его на Ryan Direct3d (имя строки, хранящееся в wndClassName), но если я изменю его на Anything else, он будет работать нормально. (доступ к альту)   -  person Ryan Birks    schedule 19.03.2020
comment
Я скопировал код в исходный пост.   -  person Ryan Birks    schedule 19.03.2020
comment
Ладно, вроде починил, но теперь окно не показывается. Проблема заключалась в том, что lpfnWndProc = WndProc; не был определен.   -  person Ryan Birks    schedule 19.03.2020


Ответы (1)


Вы закомментировали назначение WndProc в WNDCLASSEX:

Window::WindowClass::WindowClass() noexcept : hInst(GetModuleHandle(nullptr))
{

    WNDCLASSEX wc = { 0 };
    wc.cbSize = sizeof(wc);
    wc.style = CS_OWNDC;
    //wc.lpfnWndProc = WndProc;
    ...

Чтобы исправить это, просто объявите свою функцию WndProc (которая определена ниже). И раскомментируйте.

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

//Window Class stuff
Window::WindowClass Window::WindowClass::wndClass; //
Window::WindowClass::WindowClass() noexcept : hInst(GetModuleHandle(nullptr))
{

    WNDCLASSEX wc = { 0 };
    wc.cbSize = sizeof(wc);
    wc.style = CS_OWNDC;
    wc.lpfnWndProc = WndProc;
    ...

После того, как я внес это изменение, сбой исчезает, и появляется ваше окно:

введите здесь описание изображения

person selbie    schedule 19.03.2020
comment
Где бы вы поместили это в код, так как у меня есть 4 файла. и большое спасибо. Мне удалось увидеть, что это была проблема, но это помогло. - person Ryan Birks; 20.03.2020