Что вызывает здесь сбой fstream::open?

avatar
Nasrat Takoor
8 августа 2021 в 19:58
63
1
0

Проблема

Я работаю над простым приложением "Блокнот" на Win32 C++. Однако я столкнулся с проблемой открытия файла для сохранения. Там довольно много кода (который вы можете найти здесь), но строка, вызывающая ошибку, находится в файле Event Handlers.cpp.

В частности, строка 25

wnd.file.open(wnd.filePath, std::ios_base::out);

создает исключение. Вот вывод отладки:

Exception thrown: read access violation.
this->**_Ptr** was nullptr.

хотя _Ptr не всегда является значением nullptr.


Что я пробовал

В этом суть проблемы. Вот что я пробовал до сих пор.

  • проверка результата диалога "Сохранить как" (значение wnd.filePath) - это действительно правильная строка пути к файлу
  • очистка, очистка, закрытие файлового потока перед его открытием
  • изменение режима открытия
  • Я подумал, что, возможно, объекты fstream не могут быть глобальными, так как я заметил, что элемент std::filebuf всегда был NULL для глобальных потоков fstream, но не для локальных. На самом деле это не было проблемой.
  • и МНОГО других вещей за последние несколько дней, которые я, к сожалению, не могу вспомнить

Моя теория

Однако я считаю, что проблема заключается в структуре моего кода и класса std::fstream.

Строка 34 в Main.cpp:

mainWnd = TopLevelWnd(wr.left, wr.right, wr.left, wr.top, 1280, 720, wr.right - wr.left,
   wr.bottom - wr.top, WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,
   0, L"Main Window Class", L"Main Window", hInstance);

создает TopLevelWnd, а затем копирует его в созданный по умолчанию TopLevelWnd в глобальном векторе windows. Насколько я могу судить, класс std::fstream имеет удаленные элементы-копии и не может быть скопирован. Это должно вызвать ошибку компилятора, поскольку он является членом TopLevelWnd, но это не так. В любом случае, я решил удалить элементы-копии TopLevelWnd и написать функции конструктора перемещения и оператора присваивания перемещения и явно вызвать их с помощью std::move:

    // delete copy members because fstream cannot be copied
    TopLevelWnd& operator=(const TopLevelWnd&) = delete;
    TopLevelWnd(const TopLevelWnd&) = delete;
    
    // move members
    TopLevelWnd(TopLevelWnd&& source)
        : Wnd(std::move(source)),
        file(std::move(source.file))
    {}
    TopLevelWnd& operator=(TopLevelWnd&& right)
    {
        Wnd::operator=(std::move(right));
        file = std::move(right.file);
        return *this;
    }

Это тоже не решает проблему, так что я в полной растерянности. Кто-нибудь может помочь мне понять?

P.S. Кстати, чтобы воссоздать проблему, вы просто запускаете программу и в меню выбираете Файл->Сохранить и выбираете файл для сохранения.

Источник
G.M.
8 августа 2021 в 20:21
1

Что такое стек/обратная трассировка вызовов, когда отладчик ломается на Exception thrown: read access violation? Это должно предоставить некоторую полезную информацию о точке в вашем собственном коде, которая может быть проблемой.

Nasrat Takoor
8 августа 2021 в 20:29
0

@G.M. уже упомянутая строка - Строка 25 в Event Handlers.cpp - wnd.file.open(wnd.filePath, std::ios_base::out);

Remy Lebeau
8 августа 2021 в 21:23
0

Ошибка подразумевает, что либо wnd, либо wnd.file не является допустимым объектом в памяти, поэтому вызов open() для него будет неопределенным поведением. Приведите минимальный воспроизводимый пример, демонстрирующий ошибку в действии.

Алексей Неудачин
9 августа 2021 в 00:28
0

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

Nasrat Takoor
9 августа 2021 в 03:39
0

@RemyLebeau wnd подходит для многих других операций, а wnd.file работает для открытия файлов, когда пользователь щелкает пункт меню File->Open, поэтому я не думаю, что это проблема. Если бы кто-то мог запустить программу в отладчике и немного проверить ее, я думаю, это было бы большой помощью.

Remy Lebeau
9 августа 2021 в 05:54
0

@NasratTakoor "Я не думаю, что это проблема " - тогда нам придется согласиться не соглашаться. Ваш симптом очень характерен для обращения к недопустимому объекту. "Если бы кто-то мог запустить программу в отладчике и немного проверить ее, я думаю, это было бы большой помощью " - это ВАША работа. StackOverflow — это сайт вопросов и ответов, а не служба отладки. Опять же, я прошу вас для минимального воспроизводимого примера. Просить людей скачивать и запускать код с другого сайта здесь неуместно.

IInspectable
9 августа 2021 в 06:01
0

См. "Недействительность итератора" в документации для std::vector.

Ответы (1)

avatar
Nasrat Takoor
22 августа 2021 в 23:06
0

После долгих поисков и экспериментов я наконец нашел ответ. Проблема была в конструкторе TopLevelWnd.

TopLevelWnd(int x, int y, int ncX, int ncY, int width, int height, int ncWidth, int ncHeight, DWORD styles, DWORD exStyles, const wchar_t* className, const wchar_t* name, HINSTANCE hInstance = GetModuleHandleW(nullptr), HWND hWnd = nullptr, HWND hWndParent = nullptr, HMENU hMenu = nullptr)
        : Wnd(x, y, ncX, ncY, width, height, ncWidth, ncHeight, styles, exStyles, className, name, hInstance ,hWnd, hWndParent),
        hMenu(hMenu)

Он принимает два параметра с именами hWnd и hMenu. Класс также имеет два члена данных с именами hWnd и hMenu. В теле я писал строки типа:

hWnd = CreateWindowExW(...);
hMenu = CreateMenu();

когда мне нужно было написать

this->hWnd = CreateWindowExW(...)
this->hMenu = CreateMenu();