Search code examples
c++qtqdomdocument

Read access violation creating a QDomElement the second time


I wrote a class to handle an XML file. The class loads the file on construction and will save it on destruction. While the class is active I have several getter and setter functions to change the values. One of them is a function to log some file renames. On each call, it will create a new child element for elem_renames.

void DataElementHandle::renamed(QString new_name, QString old_name)
{
    QDomElement elem_ren = xml_doc.createElement("renamed");
    QDomAttr att = xml_doc.createAttribute("time");
    att.setValue(QDateTime::currentDateTime().toString(Qt::ISODate));
    elem_ren.setAttributeNode(att);

    QDomText t = xml_doc.createTextNode(old_name + " -> " + new_name);
    elem_ren.appendChild(t);

    elem_renames.appendChild(elem_ren);
}

Problem: Now, I create the DataElementHandle class and call the rename function for every change to the files. But every second time I call the function my program crashes with this error message:

Exception thrown: read access violation. The error is thrown in the first line of the function.

I don't understand why this happens. I think I can not override the QDomElement because there is still a link to the element created at the first call. But how? It should be gone at the end of the function.

I use Qt 5.8 with Visual Studio 2015 and Visual Leak Detector.

The header file:

// Version
const quint32 version = 1;

// Doc file path
QString file_path;
bool load_file_ok;

// Doc
QDomDocument xml_doc;
QDomElement root;

// First root elements
QDomElement elem_renames;

If the XML file doesn't exist a new template is created like this and will be saved to file in the destructor.

xml_doc = QDomDocument("data_xml");
root = xml_doc.createElement("root");
root.setAttribute("version", QString::number(version));
xml_doc.appendChild(root);

elem_renames= xml_doc.createElement("renames");

root.appendChild(elem_renames);

Edit 1: I've set up a test project and there it works fine. I have to investigate the problem in more detail.


Solution

  • Solved! I'm so stupid. I had a setup like this. And I declared and initialized the pointer to the XML class in the for loop. And because I delete the old and create a new class in an if statement below, I initialized pointer in the if statement the first time but then deleted it on the second.

    #include <QCoreApplication>
    #include <QDomDocument>
    #include <QFile>
    #include <QDateTime>
    #include <QString>
    #include <QDebug>
    
    class TestDomClass
    {
    public:
        TestDomClass(QString file_path) :
            path(file_path)
        {
            xml_doc = QDomDocument("data_xml");
            root = xml_doc.createElement("root");
            root.setAttribute("version", QString::number(version));
            xml_doc.appendChild(root);
    
            elem_renames= xml_doc.createElement("renames");
    
            root.appendChild(elem_renames);
        }
        ~TestDomClass()
        {
            QFile file(path);
            if(!file.open(QIODevice::WriteOnly | QIODevice::Text))
            {
                // TODO
                qDebug() << "Failed to open file for writing!";
            }
            else
            {
                QTextStream stream(&file);
                stream << xml_doc.toString();
            }
    
            if(file.isOpen())
            {
                file.close();
            }
        }
    
        void renamed(QString new_name, QString old_name)
        {
            QDomElement elem_ren = xml_doc.createElement("renamed");
            QDomAttr att = xml_doc.createAttribute("time");
            att.setValue(QDateTime::currentDateTime().toString(Qt::ISODate));
            elem_ren.setAttributeNode(att);
    
            QDomText t = xml_doc.createTextNode(old_name + " -> " + new_name);
            elem_ren.appendChild(t);
    
            elem_renames.appendChild(elem_ren);
        }
    
    private:
        QString path;
    
        // Version
        const quint32 version = 1;
    
        // Doc
        QDomDocument xml_doc;
        QDomElement root;
    
        // First root elements
        QDomElement elem_renames;
    };
    
    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
    
        TestDomClass *t = Q_NULLPTR; // <-- This line was in the for loop
    
        for(int a = 0; a < 10; ++a)
        {
            delete t;
            t = new TestDomClass("test_" + QString::number(a) + ".xml");
            for(int i = 0; i < 10; ++i)
            {
                qDebug() << "Round: " << i;
                t->renamed(QString::number(10*a + i), QString::number(10*a + 10-i));
            }
        }
    
        delete t;
    
        return a.exec();
    }