Search code examples
c++cfile-handlingunhandled-exception

Unhandled exception after reading from a binary file, when control is returned to calling function


I am new to your forum, so please forgive any missteps. I am working on a c++ project that reads and writes to a binary file. I first tried doing this using full on c++ but when an error popped up, my instructor told me to use c style file manipulation. Low and behold, I get the same error:

Unhandled exception at 0x6087CCC8 (msvcp110d.dll) in CSI_FinalProj_EmployeeDB.exe: 0xC0000005: Access violation reading location 0x00CDDAEC.

This occurs after successfully completing the read and print, and successfully closing the file. It always occurs when the program exits the function and attempts to return to the calling function. If I put it in the main, it blows up after the return, when the program ends.

The function is a simple print function:

void fileClerkType::printRecord(int id)const
{

    FILE* spRead;
    employeeType record;
    long location;
    long size;

    location = id - 1;                  
    size = sizeof(employeeType);

    spRead = fopen("companyFile.dat", "r");

    fseek(spRead, location*size, SEEK_SET);
    fread(&record, sizeof(employeeType), 1, spRead);

    // If a record has been deleted, the id will be 0
    // In that case, don't print
    if (record.getEmployeeID() != 0)
    {

        cout << record << endl;
        fread(&record, sizeof(employeeType), 1, spRead);
    }

    fclose(spRead);

}//Unhandled exception at 0x5065CCC8 (msvcp110d.dll) in
//CSI_FinalProj_EmployeeDB.exe: 0xC0000005: Access violation
//reading location 0x00CDDAEC.

As I said, the function works perfectly. employeeType is a class that has:

2 ints, three strings, and a float

Here is the original c++ version with the same problem. The only difference is that this prints all of the records. It also works perfectly.:

void administratorType::showAllRecords()
{

    long test;
    long position = 0;
    long recordSize = sizeof(employeeType);

    ifstream inFile("EmployeesNew.dat", ios::in | ios::binary);
    employeeType buffer; // empty employeeType

    if(inFile.is_open())
    {

        inFile.seekg((position * recordSize), ios::beg);
        test = inFile.peek(); // Debug
        inFile.read(reinterpret_cast<char*>(&buffer), recordSize);

        position = 0;

        while(position < getRecordCount())
        {

            inFile.seekg((position * recordSize), ios::beg);
            test = inFile.peek();
            inFile.read(reinterpret_cast<char*>(&buffer), recordSize);
            outputRecord(cout, buffer);
            position++;

        }

        inFile.close();

    }

}// Runs fine to here, but throws error when leaving the function
// Unhandled exception at 0x5408CCC8 (msvcp110d.dll) in
// ProjectName.exe: 0xC0000005: Access violation
// reading location 0x0137D3B4.

It has to be an implementation issue. But I cannot see it. Is there something in the implementation that is causing the pointers keeping track of function calls and returns to be corrupted? Thank you in advance for your help.

Sorry, here is the list of member variables for the Employee class. They are not fixed length strings:

int age;
int employeeID; // Auto-generated
float salary;
string lastName;
string firstName;
string ssn;

Solution

  • I created a struct to hold the data being read to the file, then converted all strings to char arrays. Doing each of those did not work, but the combination did. The following is the test program with a main() and a test class (with a struct. This is what I used to find the solution. This is a working program for those of you seeking a way to read/write binary files randomly (unless I screwed it up while formatting it in here).

    #include <iostream>
    #include <fstream>
    #include <string>
    #include <iomanip>
    
    using namespace std;
    
    struct STUDENT
    {
    
        char lName[21];
        int id;
        float sal;
    
    }; 
    
    class Person
    {
    public:
    
        struct STUDENT student;
    
        string getlName() const
        {
    
            return student.lName;
    
        }
    
        int getID() const
        {
    
            return student.id;
    
        }
        float getSal() const
        {
    
            return student.sal;
    
        }
    
        // Insertion operator
        friend std::ostream& operator<<(std::ostream& os, const Person& p)
        {
    
            // write out individual members of the struct with
            // an end of line between each one
            os << p.student.id << ' ' << p.student.lName
                << ' ' << p.student.sal << '\n';
    
            return os;
    
        }
    
        // Extraction operator
        friend std::istream& operator>>(std::istream& is, Person& p)
        {
    
            // read in individual members of struct
            is >> p.student.id >> p.student.lName >> p.student.sal;
    
            return is;
        }
    
        Person()
        {
    
        }
    
    };
    
    
    void outputLine( ostream&, const STUDENT&);
    
    
    int main()
    {
    
        char lName[21] = {}; // Extra char for null
        int id;
        float sal;
        int size = sizeof(STUDENT);
        string more;
        bool exit_now = false;
    
        STUDENT buffer;
        Person person;
    
        // In order to randomly access data without destroying the file,
        //  you must use in and out (read/write mode).
        fstream outFile("testFile.dat", ios::in | ios::out | ios::binary);
    
        // Ensure file is opened
        if(!outFile)
        {
    
            cerr << "Error: Out File could not be opened" << endl;
            exit(1);
    
        }
    
        // ************* Random access inserting *************
        do
        {
    
            cout << "Enter last Name\n?";
    
            cin.getline(lName, 21);
            int test;
            test = strlen(lName); // FYI: this works to get char count 
            cout << "Enter salary\n?";
            cin >> sal;
    
            cout << "Enter ID\n?";
            cin >> id;
    
            strcpy_s(person.student.lName, lName); // copy input to struct
            person.student.sal = sal;
            person.student.id = id;
            cout << person; // object being printed
    
            outFile.seekp((person.student.id - 1) * size);
            outFile.write(reinterpret_cast<const char* >(&person.student), size);
    
            // Need this to get the next name
            cin.clear();
            cin.ignore();
    
            cout << "Do you want to add another record? (yes or no)\n?"
                 << endl;
            cin >> more;
    
            if (more == "no")
                exit_now = true;
    
            // Need this to get the next name properly
            cin.clear();
            cin.ignore();
    
    
        }while(exit_now == false);
    
        outFile.close();
    
    
        // ************* Display Data *************
    
        fstream inFile("testFile.dat", ios::in);
    
        if(inFile) // Is there a connection
        {
    
            int target = 0;
            int index = 0;
            int position;
    
            cout << "All records:" << endl;
    
            while(inFile)
            {
    
                inFile.read(reinterpret_cast<char*>(&buffer), size);
                if (buffer.id > 0)
                {
    
                    target = inFile.tellg(); // Debug
                    cout << buffer.lName << endl;
    
                }
    
                //cout << buffer << endl; // This works
                //cout << buffer.id << endl; // This works
    
            }
    
            cout << endl << "Search for a record by id" << endl << endl;
            cout << "Enter an id: (0 to exit)" << endl;
    
            cin >> target;
    
            while(target > 0)
            {
    
                index = target - 1;
    
                inFile.clear(); // Clear the flags. If the fail flags are
                                // are set, seekg() will not work.
    
    
                // Position the file pointer
                inFile.seekg(sizeof(Person)*index, ios::beg);
    
                // Read information into the buffer (Person object)
                //  starting at the file pointer
                inFile.read(reinterpret_cast<char*>(&buffer), size);
    
                cout << buffer.lName << endl;
                outputLine(cout, buffer);
    
                cout << "Enter an id: (0 to exit)" << endl;
                cin.clear();
                cin >> target;
    
            }
    
            inFile.close();
            cin.clear();
            cin.get();
    
        }else
            cerr << endl << "Error: Could not complet the file connection."
                 << "\nData could not be read."<< endl;
    
        return 0;
    
    }
    
    void outputLine( ostream& output, const STUDENT& record)
    {
    
        //output << record << endl; // This works also
    
        output << left << setw(20) << record.lName
                << setw(5) << record.id << setprecision(2)
                << right << fixed << showpoint
                << record.sal << endl;
    
    }