Russian Qt Forum

Qt => Вопросы новичков => Тема начата: qwyllum от Май 15, 2013, 20:06



Название: Qt unhanded win32 exception
Отправлено: qwyllum от Май 15, 2013, 20:06
Доброго времени суток, уважаемые форумчане! С-но проблема: имеется класс, отвечающий за работу с БД, называемый DataBase. В нем есть переменная QSqlDatabase body. В классе диалога есть переменная  DataBase db. Долгое время не мог понять, почему при закрытии программы выдается ошибка о неизвестном исключении с предложением открыть проект в VS 2010. Ассемблер я понимаю плохо, поэтому копаться в отладке вряд ли получится. Я методично удалял из проги все лишнее, чтобы докопаться, какая строка вызвала исключение. И вот что накопал:
При создании диалога автоматически вызывается конструктор DataBase, т.к. диалог содержит переменную этого класса. В конструкторе есть строчка
Код:
  body = QSqlDatabase::addDatabase("QMYSQL","Connect2");
которая и вызывает это исключение. если убрать строку, то закрытие программы исключений не вызывает. я подумал, что скорее всего дело в том, что не закрыл соединение с БД, поэтому создал деструктор класса:
Код:
DataBase::~DataBase()
{
    body.close();
}
Однако это не помогло. Подскажите пожалуйста, в чем может быть еще ошибка?


Название: Re: Qt unhanded win32 exception
Отправлено: Majestio от Май 15, 2013, 20:41
Вот тут в третей позиции (http://g.zeos.in/?q=addDataBase конструктор) есть полезная инфа.
PS. Попробуй вынести инициализацию в метод или слот.


Название: Re: Qt unhanded win32 exception
Отправлено: mta88 от Май 15, 2013, 20:54
метод addDatabase закрывает все предыдущие соединения с тем же драйвером и именем

это вполне может быть причиной исключения
при закрытии программа пытается что-то записать в закрытое соединение и выдает ошибку

возможными решениями будут
  • поменять имя "Connect2" на что нибудь другое
  • использовать одно соединение на всю программу


-------------------------------------------------

второй вариант:

из диалога наружу каким-то образом копируется объект data или указатель на него или объект, использующий его (QSqlQuery например)

при закрытии диалога этими объектами нельзя будет пользоваться (соединение закроется, указатель станет невалидным и т.д.)


Название: Re: Qt unhanded win32 exception
Отправлено: qwyllum от Май 15, 2013, 20:56
Вот тут в третей позиции (http://g.zeos.in/?q=addDataBase конструктор) есть полезная инфа.
PS. Попробуй вынести инициализацию в метод или слот.
Спасибо за ответ!
Для информации Вам: выдача результатов в поисковике на разных компьютерах может быть разной и зависит от предыдущих запросов. Поэтому я вряд ли смогу почерпнуть что-то полезное.

Я вынес в отдельный метод init() подключение к БД. Результат не изменился.


Название: Re: Qt unhanded win32 exception
Отправлено: Majestio от Май 15, 2013, 21:03
Конец дня, телепатические способности практически на нуле! :( Давайте ваши исходники, поразбираемся.


Название: Re: Qt unhanded win32 exception
Отправлено: qwyllum от Май 15, 2013, 21:04
метод addDatabase закрывает все предыдущие соединения с тем же драйвером и именем

это вполне может быть причиной исключения
при закрытии программа пытается что-то записать в закрытое соединение и выдает ошибку
Спасибо огромное за ответ! Да, я подумал в этом направлении, но у меня всего одно соединение в БД. Тем не менее я пробовал изменить имя соединения, а потом и вовсе убрал его(в методе addDataBase предусмотрено подключение по умолчанию)
второй вариант:
из диалога наружу каким-то образом копируется объект data или указатель на него или объект, использующий его (QSqlQuery например)

при закрытии диалога этими объектами нельзя будет пользоваться (соединение закроется, указатель станет невалидным и т.д.)
Такого не происходит. С-но, вот все, что происходит вне диалога:
Код:
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Dialog w;
    w.show();
    return a.exec();
}
кроме того, я закомментировал все, что только можно. Единственное, что происходит в диалоге  ui->setupUi(this);
В классе DataBase, который создается при создании диалога прописано:
Код:
   if (!QSqlDatabase::drivers().contains("QMYSQL"))
    {
        QMessageBox::critical(0, "Driver not foung", "This program needs the MySQL driver");
    }
    init();
в свою очередь, в init() прописана та злосчастная строчка. Причем, раньше такой ошибки не было, что вдвойне странно.


Название: Re: Qt unhanded win32 exception
Отправлено: Majestio от Май 15, 2013, 21:06
метод addDatabase закрывает все предыдущие соединения с тем же драйвером и именем
это вполне может быть причиной исключения
Не может. Соединение просто переоткрывается. Это видно в отладочных сообщениях. Если нужно два соединения - делаем так:
Код:
    DataBase = QSqlDatabase::addDatabase("QPSQL","First");
    TechBase = QSqlDatabase::addDatabase("QPSQL","Second");


Название: Re: Qt unhanded win32 exception
Отправлено: qwyllum от Май 15, 2013, 21:11
Конец дня, телепатические способности практически на нуле! :( Давайте ваши исходники, поразбираемся.
Код:
//database.h
#include <QObject>
#include <QtSql>
#include <QDebug>// библиотека для вывода системных сообщений
#include <QString>// библиотека для работы со строками
#include <QMessageBox>// библиотека для вывода сообщений MessageBox

class DataBase : public QObject
{
    Q_OBJECT
public:
    explicit DataBase(QObject *parent = 0);
     DataBase(QObject *parent, QString name);// конструктор класса. Передается имя БД, к которой нужно осуществить подключение
         ~DataBase();
     QSqlDatabase body;// переменная для работы с базой данных
     void init();
  
};

#endif // DATABASE_H


//database.cpp
#include "database.h"
#include <QStringList>

DataBase::DataBase(QObject *parent) :
    QObject(parent)
{
    // проверка на драйвер в системе:

    if (!QSqlDatabase::drivers().contains("QMYSQL"))
    {
        QMessageBox::critical(0, "Driver not foung", "This program needs the MySQL driver");
    }



    init();

}

void DataBase::init()
{
    body = QSqlDatabase::addDatabase("QMYSQL");

//    body.setHostName("localhost");
//    // имя базы
//    body.setDatabaseName("system");


//    body.setUserName("root");
//    body.setPassword("");
}



}


DataBase::~DataBase()
{
    body.close();
}


//dialog.h

#include "database.h"

namespace Ui {
class Dialog;
}

class Dialog : public QDialog
{
    Q_OBJECT


     DataBase db;


public:
    explicit Dialog(QWidget *parent = 0);

  
    ~Dialog();

private:
    Ui::Dialog *ui;
};

#endif // DIALOG_H


//dialog.cpp

#include "dialog.h"
#include "ui_dialog.h"
#include <QStringList>
#include <QList>



Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);

}

Dialog::~Dialog()
{

       delete ui;


}

main создает сам QT, так что его нет смысла выкладывать :)


Название: Re: Qt unhanded win32 exception
Отправлено: thechicho от Май 15, 2013, 21:16
<QDebug>// библиотека для вывода системных сообщений

комменты жгут) это же просто класс :)


Название: Re: Qt unhanded win32 exception
Отправлено: Majestio от Май 15, 2013, 21:18
попробуйте вызывать init() не напрямую, а вот так:

QTimer::singleShot(2000,this,SLOT(init()));

предварительно объявите его как public slots


Название: Re: Qt unhanded win32 exception
Отправлено: qwyllum от Май 15, 2013, 21:24
попробуйте вызывать init() не напрямую, а вот так:

QTimer::singleShot(2000,this,SLOT(init()));

предварительно объявите его как public slots
Спасибо за ответ! Я попробовал так, но безрезультатно. Также я создал слот close() и в деструкторе DataBase вызвал аналогичным образом его. Ошибка все равно выдается, но что странно - раньше он мне предлагал отловить исключение в Visual Studio, а теперь просто выдает окно ошибки. Ну хоть какие-то изменения :-)


Название: Re: Qt unhanded win32 exception
Отправлено: thechicho от Май 15, 2013, 21:28
я не силен в плюсах, но
class Dialog : public QDialog
{
    Q_OBJECT


     DataBase db;


public:

может стоить написать так
class Dialog : public QDialog
{
    Q_OBJECT
   
public:
    explicit Dialog(QWidget *parent = 0);
    ~Dialog();
    DataBase db;

//private:
    //DataBase db;


Название: Re: Qt unhanded win32 exception
Отправлено: qwyllum от Май 15, 2013, 21:33
UPD: решил поэксперементировать и в конструкторе написал так:
Код:
init();
     body.close();
Выдает исключение. Теперь так:
Код:
QTimer::singleShot(2000,this,SLOT(init()));
     body.close();;

Не выдает ошибок и все корректно. Отсюда вывод - получается при уничтожении экземпляра диалога как-то соединение некорректно закрывается?


Название: Re: Qt unhanded win32 exception
Отправлено: Majestio от Май 15, 2013, 21:40
Проверено электроникой!

Testo.pro
Код:
#-------------------------------------------------
#
# Project created by QtCreator 2013-05-12T08:01:55
#
#-------------------------------------------------
QT       += core gui sql
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = Testo
TEMPLATE = app
SOURCES += main.cpp\
        dialog.cpp
HEADERS  += dialog.h

main.cpp
Код:
#include "dialog.h"
#include <QApplication>

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);
    Dialog w;
    w.show();
    return a.exec();
}

dialog.h
Код:
#ifndef DIALOG_H
#define DIALOG_H

#include <QtWidgets>
#include <QtSql>

class Dialog : public QDialog
{
    Q_OBJECT

public:
    QSqlDatabase DataBase;
    Dialog(QWidget *parent = 0);
    ~Dialog();
public slots:
    void SlotClick();
};

#endif // DIALOG_H

dialog.cpp
Код:
#include "dialog.h"

Dialog::Dialog(QWidget *parent)
    : QDialog(parent)
{
    QPushButton *B = new QPushButton("Click!");
    QHBoxLayout *L = new QHBoxLayout();
    L->addWidget(B);
    setLayout(L);
    connect(B,SIGNAL(clicked()),this,SLOT(SlotClick()));
}

Dialog::~Dialog()
{
   
}

void Dialog::SlotClick()
{
    DataBase = QSqlDatabase::addDatabase("QPSQL","First");
    DataBase.setHostName("192.168.1.47");
    DataBase.setDatabaseName("testo");
    DataBase.setUserName("pgsql");
    DataBase.setPassword("gfhjkm1");

    if (DataBase.open()) {
        QMessageBox::information(0,"Open","Ok");
        DataBase.close();
    } else {
        QMessageBox::information(0,"Open",tr("Error: ")+DataBase.lastError().text());
    }

}


Название: Re: Qt unhanded win32 exception
Отправлено: qwyllum от Май 15, 2013, 21:41
может стоить написать так
class Dialog : public QDialog
{
    Q_OBJECT
   
public:
    explicit Dialog(QWidget *parent = 0);
    ~Dialog();
    DataBase db;

//private:
    //DataBase db;
Спасибо за ответ! К сожалению, не помогло


Название: Re: Qt unhanded win32 exception
Отправлено: Majestio от Май 15, 2013, 21:43
А теперь напишу как правильно делать) Только схожу выкурю сигару  8)


Название: Re: Qt unhanded win32 exception
Отправлено: thechicho от Май 15, 2013, 21:49
Dialog::~Dialog()
{
    delete ui;
    db.close();
}

а так


Название: Re: Qt unhanded win32 exception
Отправлено: qwyllum от Май 15, 2013, 21:52
Проверено электроникой!
Спасибо! Ваша программа работает без ошибок. Но в диалоге содержится объект QSqlDatabase, тогда как у меня это отдельный класс. В перспективе там будет куча методов обращения к БД.
Поэтому остался главный вопрос - что в моем коде не так? Эх, пойду заварю чай и продолжу копать)


Название: Re: Qt unhanded win32 exception
Отправлено: qwyllum от Май 15, 2013, 21:59
Dialog::~Dialog()
{
    delete ui;
    db.close();
}

а так
Спасибо! Реально, огромное спасибо) программа больше не выдает ошибок. Одно странно, я думал, что Qt должен сам завершать соединение при уничтожении переменной. Но век живи, век учись :)


Название: Re: Qt unhanded win32 exception
Отправлено: Majestio от Май 15, 2013, 22:06
1) Из проекта убираете все неработающее, связанное с БД.

2) В проект добавляете это myglobal.h и myglobal.cpp

myglobal.h
Код:
#ifndef MYGLOBAL_H
#define MYGLOBAL_H

#include <QtGui>
#include <QtSql>

class MyGlobal {

public:
        QString  DbHost;
        QString  DbName;
        QString  DbUser;
        QString  DbPass;

    QSqlDatabase DataBase;

    MyGlobal();
    bool OpenDB();
};

#endif // MYGLOBAL_H

myglobal.cpp
Код:
#include "myglobal.h"

MyGlobal Global;

MyGlobal::MyGlobal()
{
}

bool MyGlobal::OpenDB()
{
    DataBase = QSqlDatabase::addDatabase("QPSQL","First");
    DataBase.setHostName(DbHost);
    DataBase.setDatabaseName(DbName);
    DataBase.setUserName(DbUser);
    DataBase.setPassword(DbPass);
    if (DataBase.open()) return true;
    return false;
}

3) Во всех модулях, где нужно обращение к БД прописываете #include "myglobal.h".

Это даст возможность обращаться с статическому экземпляру класса вот так:

Код:
Global.DbHost = "192.168.1.123";
Global.DbName = "mybase";
Global.DbUser = "im_super";
Global.DbPass = "secret";

if (!Global.OpenDB()) {
  qDebug() << "Shit happyness!";
}
...
Сделал урезанный вариант этого класса, у меня там все глобальные переменные приложения и еще куча методов для работы с транзакциями и блокировками.

Помните - обращение к этому всему делать строго из любого слота.
Чтобы соединение пошло автоматом после открытия диалога - слот по синглшоту вы уже знаете как вызывать.


Название: Re: Qt unhanded win32 exception
Отправлено: thechicho от Май 15, 2013, 22:10
чтобы Qt удалял, надо парента указывать.
при удалении парента, вызывается деструктор объекта.

DataBase db(this);

а в деструкторе уже

DataBase::~DataBase()
{
    body.close();
}

хотя я в теории не особо силен, поэтому не уверен, что сработает. попробуйте


Название: Re: Qt unhanded win32 exception
Отправлено: Majestio от Май 15, 2013, 22:31
А вообще в духе расовой чистоты считаю что QSqlDatabase обязательно нужно выносить в глобальную статическую переменную. Это даст:

1) возможность обращения к "хэндлу" БД из любого виджета приложения
2) мотивацию не заниматься созданиями/уничтожениями "хэндлов" БД без очевидной необходимости
3) возможность управления множеством БД (если такое требуется) из любого виджета приложения

Правда в таком случае нужно подпилить напильником типа этого:

QMap <QString, QSqlDatabase*> MyClaster;

Вот тогда имеет смысл динамически управлять экземплярами QSqlDatabase.


Название: Re: Qt unhanded win32 exception
Отправлено: thechicho от Май 15, 2013, 22:45
//QSqlDatabase обязательно нужно выносить в глобальную статическую переменную

имеется в виду этот объект? MyGlobal Global;
почему он статический?

по поводу глобального класса для доступа к бд, хз.
у тс отдельный класс для работы с бд. создает объект бд, когда надо. так удобнее по идее.
хотя мой опыт работы с бд заканчивается на единичном подключении к бд стандартными методами кьют, без вспомогательных классов.


Название: Re: Qt unhanded win32 exception
Отправлено: Old от Май 15, 2013, 23:00
А вообще в духе расовой чистоты считаю что QSqlDatabase обязательно нужно выносить в глобальную статическую переменную. Это даст:
Да почитайте вы уже документацию по этому классу. :)
Не нужно ничего никуда выносить. Он сам предоставляет кучу методов для доступа к соединению из любого места программы.


Название: Re: Qt unhanded win32 exception
Отправлено: Majestio от Май 15, 2013, 23:09
имеется в виду этот объект? MyGlobal Global;
почему он статический?

Статические экземпляры класса создаются сразу же после старта приложения (не дожидаясь, к примеру создания экземпляра главного окна). Это гарантирует то, что к моменту создания любого виджета есть набор общих переменных, видимость которых должна быть глобальной.

по поводу глобального класса для доступа к бд, хз.
у тс отдельный класс для работы с бд. создает объект бд, когда надо. так удобнее по идее.

Представим сценарий многопользовательской работы с БД.

1) С "моим" подходом - подключился к БД в начале "сеанса" работы, отключился в конце. Как минимум это дает возможность "спросить" БД, а сколько и кто именно сейчас в онлайне.
2) С динамическими подключениями, отключениями ... а смысл создавать и уничтожать "хэндлы"? На это же уходит  дополнительное время, как минимум.
3) С неглобальным хранением "хэндлов" БД ... а кому отдать владение? И главное зачем его потом делать глобально видимым ... а еще не забыть его проинициализировать вовремя...

Ну вот как-то так.

ЗЫ: Сейчас я свое приложение оформляю в виде DLL с отложенной загрузкой. В dll-ке вообще несколько "главных" окон (инициализируемых интерфейсов), кто из них главнее?  :P


Название: Re: Qt unhanded win32 exception
Отправлено: Majestio от Май 15, 2013, 23:14
Да почитайте вы уже документацию по этому классу. :)
Не нужно ничего никуда выносить. Он сам предоставляет кучу методов для доступа к соединению из любого места программы.

Милейший, у меня приложение поднимает два соединения. Одно работает с транзакциями и блокировками. Второе - регистрирует операции в базе (кто залочил, когда, кто удалил, и пр ...). Второе нужно для того, чтобы эти операции можно было проводить во время открытой и еще незавершенной транзакции (первого соединения). Поэтому я остаюсь при свое точке зрения, со всем уважением  ;)


Название: Re: Qt unhanded win32 exception
Отправлено: Old от Май 15, 2013, 23:20
Милейший, у меня приложение поднимает два соединения. Одно работает с транзакциями и блокировками. Второе - регистрирует операции в базе (кто залочил, когда, кто удалил, и пр ...). Второе нужно для того, чтобы эти операции можно было проводить во время открытой и еще незавершенной транзакции (первого соединения). Поэтому я остаюсь при свое точке зрения, со всем уважением  ;)
Это нужно исключительно вам исключительно для этого приложения, не находите?
Так почему вы рекомендуете это для всех и всегда? В большинстве случаев это избыточно и хватает функционала предоставляемого самим QSqlDatabase.

А про статические объекты/переменные не писал только ленивый. Чем меньше их будет у вас программе, тем проще ее будет сопровождать в дальнейшем.


Название: Re: Qt unhanded win32 exception
Отправлено: Majestio от Май 15, 2013, 23:34
Это нужно исключительно вам исключительно для этого приложения, не находите?
Так почему вы рекомендуете это для всех и всегда? В большинстве случаев это избыточно и хватает функционала предоставляемого самим QSqlDatabase.

1) Ваш вариант - QSqlDatabase::database("MyDB")
2) Мой вариант - Global.MyDB

Я, как минимум, экономлю электричество на написание лишних на 19 символов, как максимум - на отсутствии необходимости Qt бессмысленного поиска "MyDB" в списке зарегистрированных соединений.

А про статические объекты/переменные не писал только ленивый. Чем меньше их будет у вас программе, тем проще ее будет сопровождать в дальнейшем.

Мало ли кто чего писал  ;D ... Лично у меня только одна - MyGlobal. И в ней все глобальной видимости. Не понимаю, зачем изобретать овальные колеса - если круглое и так ниче.


Название: Re: Qt unhanded win32 exception
Отправлено: Old от Май 15, 2013, 23:36
Не понимаю, зачем изобретать овальные колеса - если круглое и так ниче.
А это дело времени и масштаба проекта... Поймете. ;)


Название: Re: Qt unhanded win32 exception
Отправлено: thechicho от Май 15, 2013, 23:37
//Статические экземпляры класса создаются сразу же после старта приложения (не дожидаясь, к примеру создания экземпляра главного окна). Это гарантирует то, что к моменту создания любого виджета есть набор общих переменных, видимость которых должна быть глобальной.

может под статической переменной вы имеете в виду переменную, созданную в стеке?
согласно http://ru.wikipedia.org/wiki/%D0%A1%D1%82%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%81

переменную (объект класса) MyGlobal Global; нельзя назвать статической, т.к. не каждая функция класса статическая (их вообще нет).

то есть глобальная переменная - да
глобальная статическая переменная - нет

или я что-то не так понимаю? если не трудно, скиньте ссылку, где вы прочитали, что статические экземпляры класса создаются сразу же после старта приложения


Название: Re: Qt unhanded win32 exception
Отправлено: thechicho от Май 15, 2013, 23:44
//2) Мой вариант - Global.MyDB

вы привели не полный класс? нет же поля MyDB

class MyGlobal {

public:
        QString  DbHost;
        QString  DbName;
        QString  DbUser;
        QString  DbPass;

    QSqlDatabase DataBase;

    MyGlobal();
    bool OpenDB();
};



Название: Re: Qt unhanded win32 exception
Отправлено: Majestio от Май 15, 2013, 23:52
В моем варианте (в том, который приводил) - Global.DataBase;


Название: Re: Qt unhanded win32 exception
Отправлено: Majestio от Май 16, 2013, 00:11
переменную (объект класса) MyGlobal Global; нельзя назвать статической, т.к. не каждая функция класса статическая (их вообще нет).
Увы, в приведенном мною выше примере я чутка ошибся, в черновики лазил) Правильнее так:

-- myglobal.h ---
extern MyGlobal Global;

и

-- myapp.cpp ---
int main(int argc, char *argv[])
{
   MyGlobal Global;

тогда если посмотрим вызовы конструкторов/деструкторов:

Код:
int main(int argc, char *argv[])
{
    MyGlobal Global;
    qDebug() << "main1";
    QApplication a(argc, argv);
    qDebug() << "main2";
    Dialog w;
    qDebug() << "main3";
    w.show();
    qDebug() << "main4";
    return a.exec();
}

Это будет выглядеть так:

MyGlobal::MyGlobal
main1
main2
Dialog::Dialog
main3
main4
Dialog::~Dialog
MyGlobal::~MyGlobal


Название: Re: Qt unhanded win32 exception
Отправлено: Majestio от Май 16, 2013, 00:28
переменную (объект класса) MyGlobal Global; нельзя назвать статической, т.к. не каждая функция класса статическая (их вообще нет).
то есть глобальная переменная - да
глобальная статическая переменная - нет
Все верно - я неверно выразился. В статическом классе нет конструкторов, деструкторов и есть описатели static.


Название: Re: Qt unhanded win32 exception
Отправлено: thechicho от Май 16, 2013, 13:34
ясно, спс.
а не думали синглтон для этой задачи сделать?


Название: Re: Qt unhanded win32 exception
Отправлено: Majestio от Май 16, 2013, 21:16
а не думали синглтон для этой задачи сделать?
Синглтон привнесет только дополнительный код, но чего-то существенного не улучшит в данном вопросе. По большому счету можно вообще обойтись без объявления класса, а завернуть это в namespace (для многопоточного доступа наверное лучше все ж как экземпляр класса). Цель же этого всего - только централизация общедоступных ресурсов.


Название: Re: Qt unhanded win32 exception
Отправлено: qwyllum от Май 17, 2013, 23:19
1) Из проекта убираете все неработающее, связанное с БД.

2) В проект добавляете это myglobal.h и myglobal.cpp

Огромное спасибо за пример. Ваш пример пригодился немного для другого - позволил уменьшить количество методов в диалоге) Что касается исключения - иногда программа генерировала исключения, иногда нет. Я переустановил винду и ошибка исчезла сама собой. Прошу прощения у всех, что отнял Ваше время.