Loading files in C++... (crashing)

Started by
16 comments, last by MatsVed 13 years, 12 months ago
I'm trying to load a saved file in C++. The problem is that I'm calling the DLL from UnrealScript, and don't really have a good way to debugging it. I can only confirm that it makes UDK crash. So... can anyone spot any obvious flaws in my loading code (compiles fine)?
// SaveLib.cpp : Defines the exported functions for the DLL application.
//

#include "stdafx.h"
#include <fstream>
#include <iostream>
#include "OSFDlg.h"

using namespace std;

extern "C"
{
	__declspec(dllexport) unsigned int SaveGame(wchar_t* LevelName, float X, float Y, float Z, wchar_t* SavePath)
	{
		wofstream SaveStream(SavePath, ios::out);
		SaveStream << LevelName;
		SaveStream << X;
		SaveStream << Y;
		SaveStream << Z;
		SaveStream.close();

		return 1;
	}

	struct Save
	{
		wchar_t LevelName;
		float X, Y, Z;
	};

	__declspec(dllexport) Save* LoadGame(wchar_t* LoadPath)
	{
		wifstream LoadStream(LoadPath, ios::in);

		static Save *NewSave;
		NewSave = new Save;

		LoadStream >> NewSave->LevelName;
		LoadStream >> NewSave->X;
		LoadStream >> NewSave->Y;
		LoadStream >> NewSave->Z;
		LoadStream.close();

		return NewSave;
	}

	__declspec(dllexport) wchar_t SaveDiag()
	{
		//Filter
		TCHAR szFilter[] = TEXT("Save Files (*.SAV)\0*.sav\0");
		//Default extension
		TCHAR szDefExtension[] = TEXT("sav\0");

		COSFDialog SaveDiag;

		if(SaveDiag.FileSaveDlg(szFilter, szDefExtension, NULL))
		{
			return (wchar_t)SaveDiag.GetFileName();
		}
	}

	__declspec(dllexport) wchar_t LoadDiag()
	{
		//Filter
		TCHAR szFilter[] = TEXT("Save Files (*.SAV)\0*.sav\0");
		//Default Extension
		TCHAR szDefExtension[] = TEXT("sav\0");

		COSFDialog LoadDiag;

		if(LoadDiag.FileOpenDlg(szFilter, szDefExtension, TEXT("Load Game"), false))
			return (wchar_t)LoadDiag.GetFileName();
	}
}

Edit: It's the LoadGame() function that fails. LoadDiag seems to work fine... it just displays a File Open Dialog.
Advertisement
In terms of debugging you can attach to an already running process in Visual Studio. All you really need to do is:

-Make the program wait somewhere to give you a chance to attach, something like

static bool wait = true;
while(wait) {}

at the top of the crashing function should do it.

-Perform the steps to recreate the crash, you should find that the program then hangs at the while loops instead of crashing.

-In Visual Studio goto "Debug"->"Attach To Process" and choose your dll or UDK editor process (this can be a bit of trail and error :S ).

You can then change the wait to false and step through the code :)

This feature probably exists with other IDE's if you're not using Visual Studio.
Quote:Original post by BosskIn Soviet Russia, you STFU WITH THOSE LAME JOKES!
I don't have access to the Unreal sourcecode. :(
But I'm gonna do some MessageBox debugging to check the sanity of my variables etc
Quote:Original post by MatsVed
I don't have access to the Unreal sourcecode. :(
But I'm gonna do some MessageBox debugging to check the sanity of my variables etc


Why would you need it? you can still debug your DLL (as I understand it you are writing a DLL that unreal loads )
Quote:Original post by BosskIn Soviet Russia, you STFU WITH THOSE LAME JOKES!
You don't need to actually debug this using UDK, just create a basic project that contains your loading/saving code...

#include <iostream>#include <fstream>struct Save{	wchar_t LevelName;	float	X, Y, Z;};template <typename Stream>Stream& operator << (Stream& stream, const Save& save){	stream << save.LevelName;	stream << save.X;	stream << save.Y;	stream << save.Z;	return stream;}template <typename Stream>Stream& operator >> (Stream& stream, Save& save){	stream >> save.LevelName;	stream >> save.X;	stream >> save.Y;	stream >> save.Z;	return stream;}int main(int argc, char* argv[]){	{		Save test;		test.X = 2.0f;		test.Y = 4.0f;		test.Z = 8.0f;		test.LevelName = L'T';		std::wofstream os("test.txt");		os << test;	}	{		Save test;		std::wifstream is("test.txt");		is >> test;	}	return 0;}


If you take a look at the contents of test.txt that should give you some clue as to why this is not working:

T248


How should operator >> intepret this data into a wide char and three floats? You need some way to distinguish which part of the text belongs to which field, for example you could put each field on a seperate line, or use some other token to delimit the fields:

T248


template <typename Stream>Stream& operator << (Stream& stream, const Save& save){	stream << save.LevelName << '\n';	stream << save.X << '\n';	stream << save.Y << '\n';	stream << save.Z << '\n';	return stream;}template <typename Stream>Stream& operator >> (Stream& stream, Save& save){	std::wstring str;	std::getline(stream, str);	save.LevelName = boost::lexical_cast<wchar_t>(str);	std::getline(stream, str);	save.X = boost::lexical_cast<float>(str);	std::getline(stream, str);	save.Y = boost::lexical_cast<float>(str);	std::getline(stream, str);	save.Z = boost::lexical_cast<float>(str);	return stream;}


Although if you are willing to use boost there is a great serialisation library included which will handle this for you easily, as well as providing different archive types.
I actually did this earlier:

	__declspec(dllexport) unsigned int SaveGame(wchar_t* LevelName, float X, float Y, float Z, wchar_t* SavePath)	{		wofstream SaveStream(SavePath, ios::out);		SaveStream << LevelName;		SaveStream << std::endl;		SaveStream << X;		SaveStream << std::endl;		SaveStream << Y;		SaveStream << std::endl;		SaveStream << Z;		SaveStream << std::endl;		SaveStream.close();		return 1;	}


But it didn't seem to have any effect, so I removed it. Is there any standard equivalent to boost:lexical_cast?
Quote:Original post by MatsVed
I actually did this earlier:

*** Source Snippet Removed ***

But it didn't seem to have any effect, so I removed it.


It may well be that the crash is caused by something else, but your load/save code is definately broken without some delimeter between the fields. I imagine that one or more of those variables will remain uninitialised. Do you have any more information about the crash?

Quote:
Is there any standard equivalent to boost:lexical_cast?


I believe it is part of tr2 although you can simulate the effect quite simply (I'm pretty sure the boost::lexical_cast does some other trickery, including error conditions and such):

template <typename Target, typename Source>Target lexical_cast(Source source){	Target target;	std::ostringstream os;	os << source;	std::istringstream is(os.str());	is >> target;	return target;}
Ok, so I got the thing to work in UDK (turns out I was doing something wrong with the UnrealScript code).
But I still have a problem... no text is actually read from the file and into the struct I'm returning. :
I created a test program for myself, and a ReadLine function.

// LoadTest.cpp : Defines the entry point for the console application.//#include "stdafx.h"#include <fstream>#include <iostream>#include <sstream>#include "OSFDlg.h"#include <assert.h>using namespace std;template <typename Target, typename Source>Target lexical_cast(Source source){	Target target;	std::ostringstream os;	os << source;	std::istringstream is(os.str());	is >> target;	return target;}struct Save{	wchar_t *LevelName;	float X, Y, Z;};wchar_t LoadDiag(){	//Filter	TCHAR szFilter[] = TEXT("Save Files (*.SAV)\0*.sav\0");	//Default Extension	TCHAR szDefExtension[] = TEXT("sav\0");	COSFDialog LoadDiag;	if(LoadDiag.FileOpenDlg(szFilter, szDefExtension, TEXT("Load Game"), false))		return (wchar_t)LoadDiag.GetFileName();}wchar_t* ReadLine(wifstream *Stream){	wchar_t *Line = new wchar_t[256];	wchar_t ControlChar = '0';	int i = 0;	while(ControlChar != '\n')	{		ControlChar = Stream->get();		Line = ControlChar;		i++;		if(i == 256)			break;	}	return Line;}__declspec(dllexport) Save* LoadGame(wchar_t* LoadPath){	wifstream LoadStream(LoadPath, ios::in);	wchar_t X;	wchar_t Y;	wchar_t Z;	static Save NewSave;	NewSave.LevelName = new wchar_t[100];	NewSave.LevelName = ReadLine(&LoadStream);	LoadStream >> X;	NewSave.X = lexical_cast<float, wchar_t>(X);	LoadStream >> Y;	NewSave.Y = lexical_cast<float, wchar_t>(Y);	LoadStream >> Z;	NewSave.Z = lexical_cast<float, wchar_t>(Z);	/*LoadStream >> NewSave.X;	LoadStream >> NewSave.Y;	LoadStream >> NewSave.Z;*/		LoadStream.close();	MessageBox(NULL, (LPCWSTR)*NewSave.LevelName, TEXT("LoadGame()"), MB_OKCANCEL);	return &NewSave;}int _tmain(int argc, _TCHAR* argv[]){	wchar_t SavePath = LoadDiag();	Save *Sav = LoadGame(&SavePath);	assert(Sav);	return 0;}


When the MessageBox is displayed, it is empty! This makes me assume that no text is being read. So what is the problem here?
I see, you are doing something wrong with that wchar_t in your MessageBox:

This:
MessageBox(NULL, (LPCWSTR)*NewSave.LevelName, TEXT("LoadGame()"), MB_OKCANCEL);


should become this:

MessageBox(NULL, (LPCWSTR)NewSave.LevelName, TEXT("LoadGame()"), MB_OKCANCEL);


NewSave.LevelName is a *wchar_t type. And the message box takes a
LPCWSTR
which is not more than an
const wchar_t*
.

Also, you don't delete the memory you allocate after use, or do you?


Anyways, this would be better:

struct Save{	wchar_t LevelName[100];	float X, Y, Z;};


So you don't need to allocate new memory. But note that you have to cast that string to a LPWSTR or an LPCWSTR when you want to use it!

(LPWSTR)&LevelName should do that.


Hm.
I changed it to:

MessageBox(NULL, (LPCWSTR)NewSave.LevelName, TEXT("LoadGame()"), MB_OKCANCEL);


The messagebox is still empty. I'm worried that no data is getting read. How do I fix this? :\

This topic is closed to new replies.

Advertisement