@kos_dev

Как исправить AccessViolation при чтении указателя, считанного при помощь CArchive?

Есть два проекта: DLL MFC для обозначения функционала и .NET'овский для чтения функционала библиотеки.
Почти все функции, определенные в библиотеке работают, кроме одной, которая считывает объекты из файла с помощью CArchive (данные объекты были предварительно записаны в файл, ошибок не возникло, метод сериализации определен, DECLATE/IMPLEMENT определен, пустой конструктор есть)
Объявления функции в C#
[DllImport("testlib.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern void ReadFromFile([MarshalAs(UnmanagedType.LPStr)] string path);


Реализация метода в C++, в котором вылетает исключение
std::vector<Fruit*> plate; // Вектор, в котором будем хранить считанные объекты
//....
extern "C" _declspec(dllexport) void __stdcall ReadFromFile(char* path)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	CFile file(CString(path), CFile::modeRead);
	CArchive ar(&file, CArchive::load);
	
	int n;
	ar >> n;
	for (int i = 0; i < n; i++)
	{
		Fruit* fruit;
		ar >> fruit;
		plate.push_back(fruit);
		auto cstrName = fruit->name; // Для наглядности. Именно в этом моменте возникает исключение AccessVIolation
	}

	ar.Close();
	file.Close();
}


Если нужен - метод записи файла с помощью CArchive
extern "C" _declspec(dllexport) void __stdcall WriteIntoFile(char* path)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	CFile file(CString(path), CFile::modeCreate | CFile::modeWrite);
	CArchive ar(&file, CArchive::store);
	
	ar << plate.size();
	for (auto o : plate)
	{
		ar << o;
	}

	ar.Close();
	file.Close();
}


На всякий случай реализация класса Fruit.cpp и заголовочный файл Fruit.h

Fruit.h
#ifndef FRUIT_H
#define FRUIT_H
#include "pch.h"

class Fruit : public CObject
{
	DECLARE_SERIAL(Fruit);
public:

	CString name;

	Fruit();
	Fruit(CString cname);
	~Fruit();

	virtual void Serialize(CArchive& ar);
};

#endif

Fruit.cpp
#include "pch.h"
#include "Fruit.h"

IMPLEMENT_SERIAL(Fruit, CObject, 1);
void Fruit::Serialize(CArchive& ar)
{
	CObject::Serialize(ar);
	if (ar.IsStoring())
	{
		ar << name;
	}
	else
	{
		ar >> name;
	}
}

Fruit::Fruit(CString cname)
{
	name = cname;
}

Fruit::Fruit() {}
Fruit::~Fruit() {}



Я так понимаю, что не удается нормально записать объект, который получил CArchive в указатель Fruit*.
Подскажите, как это исправить.
  • Вопрос задан
  • 68 просмотров
Решения вопроса 1
@kos_dev Автор вопроса
Мало ли кому-то понадобится решение подобной проблемы.
Суть в том, что сперва CArchive при записи пишет размер коллекции, а после саму коллекцию. А при чтении из файла мы с помощью CArchive считываем сперва размер коллекции, который в файле, а после каждый объект.
Так вот, ошибка была довольно простая - записывали мы размер как vectorObject.size() - который возвращает объект size_t размером 8 байт, а считывали размер в INT, размер которого 4 байта, тем самым продолжая считывание уже объектов из файла - порождались NULL объекты.
Для решения достаточно либо записывать размер как (INT) vectorObject.size() и считывать размер так же в INT.
Либо записывать vectorObject.size() -> size_t значение, ну и считывать соответственно тоже:
size_t count;
ar >> count;
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
@res2001
Developer, ex-admin
Не заню, что там в CArchive и вообще MFC давно не брал в руки, но предполагаю, что нужно использовать такой вариант:
Fruit fruit;
ar >> fruit;

Для сериализации/десериализации вызывается метод Serialize вашего же класса, т.е. сам CArchive не создает сериализуемые классы. Следовательно вы должны передать операции >> ссылку на существующий класс, а не указатель. ar просто перезапишет содержимое класса, точнее не ar перезапишет, а ваш же метод Serialize.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы