VS Compiler: QTableView::spans korrupt nach removeRow()
VS Compiler: QTableView::spans korrupt nach removeRow()
Hallo,
meine Anwendung stürzt reproduzierbar ab ohne das ich den Grund dafür kenne.
Der Ablauf im Groben:
- DataTable erbt von QTableWidget
- ein paar Zeilen werden hinzugefügt
- sämtliche Zeilen werden per removeRow() wieder entfernt
- eine Zeile wird hinzugefügt
- Schutzverletzung inQMapData::Node *QMap<Key, T>::mutableFindNode()
(genauer: das Attribut d enthält den unsinnigen Wert 0xfdfdfdfd
Ein paar Aufrufe höher habe ich herausgefunden, das in QTableView::paintEvent() die Liste d->spans korrupt ist (Debugger zeigt -2 Einträge). Siehe auch hierzu den Screenshot.
Daraufhin wird in QTableView Zeile 1330 "if (d->hasSpans())" aktiv und ruft d->drawAndClipSpans() auf. Das endet dann in der Schutzverletzung weiter unten.
Das Seltsame daran ist: Ich benutze überhaupt keine Spans. Auch ein clearSpans(); in DataTable::paintEvent() direkt vor QTableView::paintEvent() kann den Absturz nicht aufhalten.
Die Umgebung:
OS: Windows XP 32-bit
Compiler: Visual Studio 2008 (mit Qt-Plugin)
Debuger: CDB
Runtime: Multithreaded Debug DLL
SubSystem: Console
Qt: 4.6.2 Open Source
Weis jemand Rat?
meine Anwendung stürzt reproduzierbar ab ohne das ich den Grund dafür kenne.
Der Ablauf im Groben:
- DataTable erbt von QTableWidget
- ein paar Zeilen werden hinzugefügt
- sämtliche Zeilen werden per removeRow() wieder entfernt
- eine Zeile wird hinzugefügt
- Schutzverletzung inQMapData::Node *QMap<Key, T>::mutableFindNode()
(genauer: das Attribut d enthält den unsinnigen Wert 0xfdfdfdfd
Ein paar Aufrufe höher habe ich herausgefunden, das in QTableView::paintEvent() die Liste d->spans korrupt ist (Debugger zeigt -2 Einträge). Siehe auch hierzu den Screenshot.
Daraufhin wird in QTableView Zeile 1330 "if (d->hasSpans())" aktiv und ruft d->drawAndClipSpans() auf. Das endet dann in der Schutzverletzung weiter unten.
Das Seltsame daran ist: Ich benutze überhaupt keine Spans. Auch ein clearSpans(); in DataTable::paintEvent() direkt vor QTableView::paintEvent() kann den Absturz nicht aufhalten.
Die Umgebung:
OS: Windows XP 32-bit
Compiler: Visual Studio 2008 (mit Qt-Plugin)
Debuger: CDB
Runtime: Multithreaded Debug DLL
SubSystem: Console
Qt: 4.6.2 Open Source
Weis jemand Rat?
- Dateianhänge
-
- d->spans[-2]
- spans_korrupt.PNG (37.19 KiB) 3099 mal betrachtet
-
- CallStack.txt
- CallStack
- (8.87 KiB) 82-mal heruntergeladen
-
Christian81
- Beiträge: 7319
- Registriert: 26. August 2004 14:11
- Wohnort: Bremen
- Kontaktdaten:
Einen kompletten Beispielcode zu erstellen wird etwas Zeit in Anspruch nehmen.
Ich habe paintEvent() überschrieben um herauszufinden welche Widgets überhaupt betroffen sind. Erst nachdem ich in meiner, von QTableWidget abgeleiteten Klasse, die Methode überschrieben hatte, tauchte diese auch im CallStack auf. Das try unten hilft leider nicht:
void DataTable::paintEvent(QPaintEvent *event) {
try { QTableView::paintEvent(event); }
catch (...) {
qWarning("DataTable::paintEvent() - Exception raised");
}
}
NB: Wenn ich die Anwendung mit MinGW unter Windows kompiliere, dann tritt der Effekt nicht auf.
Ich habe paintEvent() überschrieben um herauszufinden welche Widgets überhaupt betroffen sind. Erst nachdem ich in meiner, von QTableWidget abgeleiteten Klasse, die Methode überschrieben hatte, tauchte diese auch im CallStack auf. Das try unten hilft leider nicht:
void DataTable::paintEvent(QPaintEvent *event) {
try { QTableView::paintEvent(event); }
catch (...) {
qWarning("DataTable::paintEvent() - Exception raised");
}
}
NB: Wenn ich die Anwendung mit MinGW unter Windows kompiliere, dann tritt der Effekt nicht auf.
-
Christian81
- Beiträge: 7319
- Registriert: 26. August 2004 14:11
- Wohnort: Bremen
- Kontaktdaten:
Das ist ein Auszug aus meiner Klasse:
class ComparableTableWidgetItem : QTableWidgetItem {
// speichert einen IntegerWert anhand dessen Elemente verglichen & sortiert werden können
}
class DataTable : public QTableWidget {
...
void paintEvent(QPaintEvent *event) {
try { QTableView::paintEvent(event); }
catch (...) {
qWarning("DataTable::paintEvent() - Exception raised");
} }
void DataTable::removeRow(int Row) {
for (int Col = columnCount() - 1; Col >= 0; Col--) {
ComparableTableWidgetItem* DeleteItem = (ComparableTableWidgetItem*) takeItem(Row, Col);
if (DeleteItem) delete DeleteItem;
}
int RowCount = getRowCount();
if (Row >= 1) QTableWidget::removeRow(Row);
else { // (http://www.qtforum.org/article/32644/qt ... post105376)
clearContents();
QTableWidget::removeRow(0);
} } }
Hier ein Auszug aus beiliegender CallStack.txt:
...
3) QtGuid4.dll!QTableView::paintEvent(QPaintEvent * event=0x0012c800)
2) BeaconManager.exe!DataTable::paintEvent(QPaintEvent * event=0x0012c800)
1) QtGuid4.dll!QWidget::event(QEvent * event=0x0012c800)
...
Das Event gelangt erst zu DataTable::paintEvent() und dann weiter zu QTableView::paintEvent(). Das ist auch der normale Ablauf. Wenn ich DataTable::paintEvent() entferne, dann wird von 1) direkt 3) aufgerufen.
Durch das Einfügen von 2) kann ich im Debugger erkennen um welche Instanz es sich konkret handelt und ihre Attribute abfragen.
removeRow() habe ich überschrieben aufgrund eines Forumartikels. Allerdings brachte auch das keine Besserung.
class ComparableTableWidgetItem : QTableWidgetItem {
// speichert einen IntegerWert anhand dessen Elemente verglichen & sortiert werden können
}
class DataTable : public QTableWidget {
...
void paintEvent(QPaintEvent *event) {
try { QTableView::paintEvent(event); }
catch (...) {
qWarning("DataTable::paintEvent() - Exception raised");
} }
void DataTable::removeRow(int Row) {
for (int Col = columnCount() - 1; Col >= 0; Col--) {
ComparableTableWidgetItem* DeleteItem = (ComparableTableWidgetItem*) takeItem(Row, Col);
if (DeleteItem) delete DeleteItem;
}
int RowCount = getRowCount();
if (Row >= 1) QTableWidget::removeRow(Row);
else { // (http://www.qtforum.org/article/32644/qt ... post105376)
clearContents();
QTableWidget::removeRow(0);
} } }
Hier ein Auszug aus beiliegender CallStack.txt:
...
3) QtGuid4.dll!QTableView::paintEvent(QPaintEvent * event=0x0012c800)
2) BeaconManager.exe!DataTable::paintEvent(QPaintEvent * event=0x0012c800)
1) QtGuid4.dll!QWidget::event(QEvent * event=0x0012c800)
...
Das Event gelangt erst zu DataTable::paintEvent() und dann weiter zu QTableView::paintEvent(). Das ist auch der normale Ablauf. Wenn ich DataTable::paintEvent() entferne, dann wird von 1) direkt 3) aufgerufen.
Durch das Einfügen von 2) kann ich im Debugger erkennen um welche Instanz es sich konkret handelt und ihre Attribute abfragen.
removeRow() habe ich überschrieben aufgrund eines Forumartikels. Allerdings brachte auch das keine Besserung.
Fällt dir was auf? Sind unterschiedliche Klassen! Klar geht das schief...Gregor hat geschrieben:class DataTable : public QTableWidget {
...
try { QTableView::paintEvent(event); }
Und code bitte in Zukunft in Code-Tags packen, schaut dann gleich besser aus:
Code: Alles auswählen
class DataTable : public QTableWidget {
void paintEvent(QPaintEvent *event) {
try { QTableView::paintEvent(event); }
catch (...) {
qWarning("DataTable::paintEvent() - Exception raised");
} }Jedes QTableWidget ist auch ein QTableView.
Auszug aus der Qt-Doku:
Und wie gesagt, wenn ich DataTable::paintEvent() aus dem Quelltext entferne, dann ist der Programmfluss ansonsten derselbe. Die Methode habe ich eingebaut, nachdem ich den CallStack im Fehlerfall untersucht habe. DataTable::paintEvent() wird genau die Methode aufgerufen, die ohnehin aufgerufen worden wäre.
Hier ist der Aufruf der virtuellen Methode paintEvent() (C:\Qt\2010.02.1\qt\src\gui\kernel\qwidget.cpp Zeile 8144):
Auszug aus der Qt-Doku:
Code: Alles auswählen
The QTableWidget class provides an item-based table view with a default model. More...
#include <QTableWidget>
Inherits QTableView.
Hier ist der Aufruf der virtuellen Methode paintEvent() (C:\Qt\2010.02.1\qt\src\gui\kernel\qwidget.cpp Zeile 8144):
Code: Alles auswählen
QWidget::event() {
...
switch (event->type()) {
...
case QEvent::Paint:
// At this point the event has to be delivered, regardless
// whether the widget isVisible() or not because it
// already went through the filters
paintEvent((QPaintEvent*)event);
break;
...
}Man überspringt aber trotzdem keine Klasse bei solchen Aufrufen, das stiftet nur Verwirrung. Es ist durchaus möglich, dass QTableWidget auch das paintEvent() implementiert, dann gäbe es Probleme. Ich hab jetzt nachgeschaut - tut es nicht.
Dann brauchen wir ein minimales Beispiel. Eine Erklärung, was du Stück für Stück machst, bringt es da nicht. Das Beispiel sollte dann den Fehler aufweisen. Und du solltest die Operationen verwenden, die auch in dem Originalcode verwendet werden.
Dann brauchen wir ein minimales Beispiel. Eine Erklärung, was du Stück für Stück machst, bringt es da nicht. Das Beispiel sollte dann den Fehler aufweisen. Und du solltest die Operationen verwenden, die auch in dem Originalcode verwendet werden.
Nach langer Suche mit CDB und mit Hardwarebreakpoints habe ich den Fehler endlich gefunden.
Falls es jemanden interessiert:
Ein QHash wurde dynamisch erzeugt und später als QMap gecastet wieder gelöscht.
Stark vereinfacht:
Dadurch wurde im Speicher das size Attribut in QLinkedListData der QLinkedList QTableView::spans.spans mehrfach dekrementiert (auf -2 in diesem Fall).
Später kam dann das PaintEvent seines Weges und prüfte in qtableview.cpp Zeile 1330:
Der konnte natürlich nicht an sich halten, weil -2 ist ja nicht 0!
Der Rest ist Geschichte.
Was lernen wir daraus?
Typecasten ist eine dreckige und fehlerträchtige Geschichte!
Trotzdem danke für Eure Hilfe!
Falls es jemanden interessiert:
Ein QHash wurde dynamisch erzeugt und später als QMap gecastet wieder gelöscht.
Stark vereinfacht:
Code: Alles auswählen
QHash<UnifiedMessageID_s , QString>* H = new QHash<UnifiedMessageID_s , QString>();
...
QMap<UnifiedMessageID_s , QString>* M = (QMap<UnifiedMessageID_s , QString>*) H;
delete M;
Später kam dann das PaintEvent seines Weges und prüfte in qtableview.cpp Zeile 1330:
Code: Alles auswählen
if (d->hasSpans()) {
d->drawAndClipSpans(region, &painter, option, &drawn,
firstVisualRow, lastVisualRow, firstVisualColumn, lastVisualColumn);
}
Der Rest ist Geschichte.
Was lernen wir daraus?
Typecasten ist eine dreckige und fehlerträchtige Geschichte!
Trotzdem danke für Eure Hilfe!