häufiger crash bei schließen eines panels mit fokussierter wxTextCtrl
-
die ausgabe zeigt:
Eine Ausnahme (erste Chance) bei 0x00517620 in mepOhneDll4.exe: 0xC0000005: Zugriffsverletzung beim Lesen an Position 0x01e2f034.
0x00517620 bleibt immer gleich, der letzte wert 0x01e2f034 ist immer anders. kann ich da irgendwie ansetzen?
danke!!
-
Also was du da so erzählst, da kann man auch nur raten...#
Zeigt doch mal deinen Code, und den Debugger im VS anzuwerfen wäre hier definitiv richtig.
Wenn du das noch nicht kannst, ist es Zeit es zu lernen, wird dir immer wieder eine gute Hilfe sein.
Ich bin auch einige Jahre ohne ausgekommen, aber mit ist es um einiges leichter.
-
das projekt ist wie gesagt leider recht groß, aber ich poste mal den teil, in dem der fehler geschieht:
/** \file defectmodepanel.cpp * * \brief Defect Mode Panel (Implementierung) */ #include "defectmodepanel.h" //#include "codeinputctrl.h" #include "eventproxy.h" #include "eventids.h" #include "mainframe.h" #include "indexcursor.h" #include "defectlist.h" #include "formscrolllistdefects.h" #include "wx/bookctrl.h" #include "othersconfig.h" #include "keypropagators.h" //#include <iostream> //using namespace std; BEGIN_EVENT_TABLE(DefectModePanel, wxPanel) EVT_CHAR(DefectModePanel::onQuickInputEnter) // TAB_EVT_DEFECTPANEL_SELECTION(ID_DEFECTPANEL, DefectModePanel::onDefectPanelSelection) // TAB_EVT_DEFECTPANEL_BEGINSELECTION(ID_DEFECTPANEL, DefectModePanel::onDefectPanelBeginSelection) EVT_TIMER(ID_TIMER, DefectModePanel::onTimer) END_EVENT_TABLE() DefectModePanel::DefectModePanel(ModePanelFrame* _mpf) : ModePanel(_mpf) { this->SetForegroundColour(*wxWHITE); mpf = _mpf; //wxBoxSizer *outer = new wxBoxSizer(wxVERTICAL); defNote = new notebookPropagator(this); Connect(defNote->GetId(), wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED, wxCommandEventHandler(DefectModePanel::onNotebookChanged)); defNote->SetOwnBackgroundColour(mpf->GetParent()->GetBackgroundColour()); tpw = new TubePicWindow(defNote, wxID_ANY, wxPoint(0, 0), wxDefaultSize); defNote->AddPage(tpw, _("Rohrbild"), true); wxBoxSizer *auxPlotterSizer = new wxBoxSizer(wxVERTICAL); wxPanel *auxPanel = new wxPanel(defNote); auxPanel->SetSizer(auxPlotterSizer); defNote->AddPage(auxPanel, _("Hilfsplotter"), false); //--- Auxplotter --- setAuxPlotter(new PlotterXY(auxPanel, mpf, false)); getAuxPlotter()->SetSize(100, 100); // quadratische Grundform festlegen auxPlotterSizer->Add(getAuxPlotter(), 1, wxSHAPED | wxALIGN_BOTTOM | wxALL, 8); //--- Endbewertungsanzeige --- wxBoxSizer *totalDefectSizer = new wxBoxSizer(wxHORIZONTAL); wxStaticText* endbew = new wxStaticText(this, wxID_ANY, _("Endbewertung: ")); endbew->SetFont(wxFont(25, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL)); totalDefectSizer->Add(endbew, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); wxStaticText *_totalDefectLabel = new wxStaticText(this, wxID_ANY, _("-")); _totalDefectLabel->SetFont(wxFont(30, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD)); setTotalDefectLabel(_totalDefectLabel); totalDefectSizer->Add(_totalDefectLabel, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT); //outer->Add(auxNote, 1, wxEXPAND); if(mpf->getNotebookPageSelected() != -1) defNote->SetSelection(mpf->getNotebookPageSelected()); //--- Befundtabelle --- defList = new FormScrollListDefects(this, _("Befundliste"), *wxWHITE); wxStaticText *_quickInputLabel = new wxStaticText(this, wxID_ANY, _("Schnelleingabe:")); quickDefectInput = new textCtrlPropagator(this); //--- Sizer "Hierarchie" --- wxStaticBox *defectBox = new wxStaticBox(this, -1, _("")); wxSizer *topSizer = new wxBoxSizer(wxVERTICAL); wxStaticBoxSizer *boxed = new wxStaticBoxSizer(defectBox, wxVERTICAL); wxBoxSizer *insideBox = new wxBoxSizer(wxHORIZONTAL); wxBoxSizer *defListContainer = new wxBoxSizer(wxVERTICAL); topSizer->Add(boxed, 1, wxEXPAND); boxed->Add(insideBox, 1, wxEXPAND); insideBox->Add(defNote, 1, wxEXPAND); insideBox->Add(defListContainer, 0, wxEXPAND); boxed->Add(totalDefectSizer, 0, wxALIGN_CENTER); defListContainer->Add(defList, 1, wxALIGN_CENTER | wxEXPAND | wxRIGHT | wxTOP, 5); defListContainer->Add(_quickInputLabel, 0, wxTOP | wxLEFT, 5); defListContainer->Add(quickDefectInput, 0, wxEXPAND | wxRIGHT | wxTOP, 5); SetSizer(topSizer); topSizer->SetSizeHints(this); setTimer(new wxTimer(this, ID_TIMER)); //SystemState::getInstance()->setActiveBuffer(SystemState::getInstance()->getContBuffer()); //--- Eventhandler setzen --- //keyHookManager = new KeyHookManager(this); //keyHookManager->hook(this); //getCodeInput()->SetFocus(); //SystemState::getInstance()->getMainFrame()->SendSizeEvent(); updateTotalDefect(); SystemState *ss = SystemState::getInstance(); ss->getIndexCursor()->setChoordsChangedByHand(false); ss->addObserver(this); defList->addObserver(this); ss->getOthersConfig()->addObserver(this); ss->getMainFrame()->addObserver(this); ss->getIndexCursor()->addObserver(this); ss->getIndexCursor()->getCurrentTube()->getDefectList()->addObserver(this); this->Show(); Layout(); /*wxWindow * test = FindFocus(); int test1 = 0;*/ } DefectModePanel::~DefectModePanel() { SystemState *ss = SystemState::getInstance(); //wxWindow * test = FindFocus(); //OutputDebugString("-------------------------- jetzt löschen der observer\n"); ss->deleteObserver(this); defList->deleteObserver(this); ss->getOthersConfig()->deleteObserver(this); ss->getMainFrame()->deleteObserver(this); ss->getIndexCursor()->deleteObserver(this); ss->getIndexCursor()->getCurrentTube()->getDefectList()->deleteObserver(this); //OutputDebugString("-------------------------- jetzt keyhook\n"); //delete keyHookManager; delete getTimer(); //OutputDebugString("-------------------------- ~defectModPan() ende\n"); } void DefectModePanel::onNotebookChanged(wxCommandEvent &evt){ wxNotebook *nb = (wxNotebook*) evt.GetEventObject(); setTpwVisible(nb->GetPageText(nb->GetSelection()) == _("Rohrbild")); } void DefectModePanel::onQuickInputEnter(wxKeyEvent &event) { if(event.GetKeyCode() == WXK_RETURN || event.GetKeyCode() == WXK_NUMPAD_ENTER){ wxString str = getQuickDefectInput()->GetValue(); str.Trim(); // vor buchstabe zahlen 0-100 optional ok wxString reText = _("^(100|[0-9]{1,2})?(["); // gültige buchstabe in groß- und kleinschreibung wxArrayString allowed = SystemState::getInstance()->validDefCodes; for(unsigned int i = 0; i < allowed.GetCount(); i++){ reText += allowed.Item(i); reText += allowed.Item(i).MakeLower(); } // nach buchstabe beliebig große zahlen optional ok // größe wird später mit der rohrlänge abgeglichen reText += _("]{1})([0-9]*)$"); wxRegEx reQuickInput = reText; // falls eingabe gültig in neuen befund umwandeln if(reQuickInput.Matches(str)){ TDefectCode code; // werte extrahieren int depth = wxAtoi(reQuickInput.GetMatch(str, 1)); wxString codeStr = reQuickInput.GetMatch(str, 2).MakeUpper(); int pos = wxAtoi(reQuickInput.GetMatch(str, 3)); // ------------- tiefe ------------------- // nur i und a speichern tiefe if(codeStr != _("I") && codeStr != _("A")) depth = 0; // aus i oder a ohne tiefe wird 1I bzw. 1A if(depth == 0 && (codeStr == _("A") || codeStr == _("I"))) depth = 10; // ------------- code ------------------- if(codeStr != _("I") && codeStr != _("A")) code = stringToDefectCode(codeStr); else code = stringToDefectCode(wxString::Format(_("%d"), DefectRecord::getDepthRoundedTenths(depth)) + codeStr); // ------------- position ------------------- // p, x und s speichern keine position if(codeStr == _("P") || codeStr == _("X") || codeStr == _("S")) pos = 0; // neuen DefectRecord in DefectList DefectRecord dr = DefectRecord(depth, code, pos); SystemState::getInstance()->getIndexCursor()->getCurrentTube()->getDefectList()->add(dr); defList->addRow(4, pos, code, depth, 0); // quickInput leeren getQuickDefectInput()->Clear(); getQuickDefectInput()->SetFocus(); } } event.Skip(); } void DefectModePanel::updateTotalDefect() { TDefectCode code = SystemState::getInstance()->getIndexCursor()->getCurrentTube()->getDefectList()->findTotalDefect(); if(code == DC_NONE) code = DC_P; getTotalDefectLabel()->SetLabel(defectCodeToString(code)); } void DefectModePanel::onTimer(wxTimerEvent &event) { int tmpStop = playStart + (150 * SystemState::getInstance()->getPlotterConfig()->getReplaySpeed()); if(tmpStop > playStop) tmpStop = playStop; getAuxPlotter()->doStep(playStart, tmpStop); mpf->getMainPlotter()->doStep(playStart, tmpStop); playStart = tmpStop; if(playStart == playStop){ getTimer()->Stop(); mpf->getPlayButton()->Enable(true); } } void DefectModePanel::replaySignal() { XytPanel* xytp = (XytPanel*) mpf->getXytPanel(); playStart = xytp->getPlayStartSmpl(); playStop = xytp->getPlayStopSmpl(); clearPlotters(); mpf->getPlayButton()->Enable(false); mpf->getPauseButton()->Enable(true); getTimer()->Start(1); } void DefectModePanel::clearPlotters() { mpf->getMainPlotter()->doClear(); getAuxPlotter()->doClear(); } void DefectModePanel::update(EventObject *eo) { DefectList *list = SystemState::getInstance()->getIndexCursor()->getCurrentTube()->getDefectList(); switch(eo->getEventId()) { case EI_SYS_PREP_TUBE_CHANGE: case EI_MF_COMMIT_CHANGES: //list->sort(); //ic->getCurrentTube()->getDefectList()->copyFrom(this->getDefectList()); break; //case EI_SYS_BUNDLE_TUBE_CHANGED: case EI_SYS_TUBE_CHANGED: //setDefectList(ic->isSingleTubeSelected() || ic->getBundleTube() == NULL ? new DefectList() : new DefectList(ic->getBundleTube()->getDefectList())); list->addObserver(this); this->getAuxPlotter()->doClear(); mpf->getXytPanel()->doClear(); updateTotalDefect(); break; case EI_DEFECTLIST_CHANGED: // vorerst keinen Eintrag auswählen! Andernfalls wird kein Auswahl-Event // mehr generiert, wenn in der neuen Liste der gleiche Eintrag vorgewählt // wird, wie in der aktuellen bzw. alten Version //getDefectPanel()->setSelectedDefect(-1); updateTotalDefect(); Layout(); break; case EI_OC_CONFIG_CHANGED: Layout(); break; } }
wie im ersten post beschrieben passiert der crash wenn überhaupt nur, wenn die wxTextCtrl namen quickDefectInput beim schließen des panel den focus hat.
was meinst du mit "debugger im vs anwerfen"? mit f5 das "debugging starten" hat die oben geposteten fehlermeldungen generiert.
ich würde sehr gerne lernen, wie ich diese fehlermeldung selber debugge, deswegen auch die frage in einem vorherigen post nach einem tutorial. nur weiß ich leider gar nicht, wo ich da ansetzen soll, weil die fehlermeldungen sich nicht auf meinen code beziehen. wie im ersten post zu sehen ist, stehen in der aufrufliste ausschließlich wx funktionen oder user32.dll. das programm bricht mit verweis auf IMPLEMENT_APP(App) ab, liefert also auch keine infos, oder?ich habe auch schon versucht, den fehler in einem minimalen testprogramm zu isolieren, dort trat er aber leider nicht auf.
könnt ihr mir vielleicht irgendeinen ansatzpunkt sagen, wie ich im debugging weiter komme? ich drehe mich hier im kreis und bin schon seit tagen erfolglos dabei, irgendwelche infos im netz zu suchen.
vielen dank!!!
-
Es ist schwer Dir bei einem Projekt einer solchen Größe zu helfen wie Du es andeutest.
Die obigen Meldungen bringt Dir der Debugger zwar, aber das hilft nicht auf einem fremden Rechner. Das ist nur der Callstack. Der hilft uns nicht weiter, wenn wir den Code nicht kennen :). Du musst mit dem Debugger dein Programm analysieren und herausfinden wodurch die Zugriffsverletzung ausgelöst wird. Wie man ihn benutzt?
Ja, das ist schwer zu erklären. Wichtig wären zum einen Breakpoints, die dein Programm stoppen in dem Moment wo Du es beendest. Am besten direkt am Deconstrutor der geposteten Klasse. Und dann Schrittweise durchgehen. Findet der Crash vorher statt, dann könnte es in wxWidgets passieren und wir reden von einem wxWidgets Bug.
Zudem gefällt mirdelete getTimer();
nicht. Ich weiss nicht wie der Standard hier definiert ist, ob das funktioniert, aber mir gefällts nicht. Losch die Variable direkt die von getTimer() zurückgegeben wird. Und das nächste mal bitte nicht [ code ] sondern [ cpp ]verwenden danke :D.
Wenn Dein Programm nur wxWidgets als Abhängigkeit hat, könnte ich Dir eventuell beim Debuggen helfen.. weiss ja nicht ob das Programm weitergabefähig ist.
Ist es vllt OpenSource? Kann man es sich irgendwo vom SVN ziehen?Die beste Lösung:
Versuche ein minimales Sample zusammenzustellen, in dem Du NUR das Interface genauso nachbaust wie der Crash auftritt. Sprich das Panel mit deinen Controls etc Funktioniert das, weisst Du dass es nicht an wxWidgets liegt. So würde ich es angehen wenn ich es partout nicht finden würde.
Bei Youtube hab ich übrigens einige Videos zu Visual Studio gefunden.
http://www.youtube.com/watch?v=kr4TkcNTdHQ
rya.
-
vielen dank für deine ausführliche antwort!
die grundlegenden debugger funktionen wie z.b. breakpoints, die in dem youtube video erklärt werden, sind mir gut vertraut. bisher (die letzten 1,5 jahre vollzeit programmieren) konnte ich auch alle fehler damit finden. falls du dich fragst "wie konnte der typ 1,5 jahre ohne fundiertes debugging wissen vollzeit programmieren?!": ich bin einziger programmierer in einer kleinen firma, die firmenintern diese software benutzt.
leider hat mein programm nicht nur wxWidgets als abhängigkeit, sondern auch noch eine umfangreiche national instruments bibliothek mit einem simulierten hardware gerät. das ist sehr aufwändig einzurichten. aber vielen dank für dein angebot!wie man am call stack im ersten post sehen kann, sind leider keine meiner programmzeilen zum zeitpunkt des crashs noch direkt aktiv. wenn ich einen breakpoint setze und mit F10 immer einen schritt weiter gehe, dann ende ich nach sehr viel klicken an einer stelle, an der der code nicht angezeigt werden kann.
das
delete getTimer()
löscht den im konstruktor erzeugten
setTimer(new wxTimer(this, ID_TIMER));
es macht hier keinen unterschied, ob ich timer direkt lösche, oder das über getTimer() mache.
das mit dem minimalen sample habe ich schon versucht, nur leider trat der fehler nicht auf. ich starte nochmal einen neuen versuch, der hoffentlich noch näher an das problem im original programm ran kommt.
ich poste dann das ergebnis.danke!!
-
Nimm das mal raus, könnte sein das wxWidgets den Timer schon selber löscht.
-
@phlox81: danke für die idee, das habe ich schon getestet, der timer wird nicht automatisch gelöscht.
ich bin durch ein minimales projekt schon näher an der lösung, der crash scheint mit events zu tun zu haben. zumindest bekomme ich im minimalen projekt eine extrem ähnliche aufrufliste bei crash an derselben stelle. ein hinweis darauf scheint auch wxEventLoop in der aufrufliste in meinem ersten post zu sein.
ich melde mich, sobald ich mehr infos habe oder das problem gelöst ist.danke!
-
ich komme nicht mehr weiter.
hier ist ein minimales projekt, in dem zunächst das austauschen der panels funktioniert:
noname.h
#ifndef __noname__ #define __noname__ /////////////////////////////////////////////////////////////////////////// class textCtrlPropagator; class MyFrame1 : public wxFrame { private: DECLARE_EVENT_TABLE() wxBoxSizer *changeContainer; wxPanel *shownPanel; protected: void onButtonClick(wxCommandEvent &event); public: MyFrame1( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxEmptyString, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 500,300 ), long style = wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL ); void changePanel(bool toMyPanel3); void onMyKeyDown(wxKeyEvent &event); }; ///////////////////////////// class textCtrlPropagator : public wxTextCtrl { public: textCtrlPropagator(wxWindow *parent) : wxTextCtrl(parent, wxID_ANY){ Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(textCtrlPropagator::OnKeyDown)); levelsFromMyFrame1 = 1; while(dynamic_cast<MyFrame1*>(parent) == NULL){ parent = parent->GetParent(); levelsFromMyFrame1++; } } protected: int levelsFromMyFrame1; private: void OnKeyDown(wxKeyEvent& event){ event.ResumePropagation(levelsFromMyFrame1); event.Skip(); } }; ///////////////////////////////////////// class MyPanel2 : public wxPanel { public: MyPanel2::MyPanel2(wxWindow* par) : wxPanel(par) { wxTextCtrl *quickDefectInput = new textCtrlPropagator(this); quickDefectInput->SetFocus(); } }; /////////////////////////////////////////////////////////////////////////////// class MyPanel1 : public wxPanel { private: MyFrame1 *par; protected: wxStaticText* m_staticText1; public: MyPanel1( MyFrame1* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 500,300 ), long style = wxTAB_TRAVERSAL ) : wxPanel( parent, id, pos, size, style ) { this->SetSizer( new wxBoxSizer( wxVERTICAL ) ); Layout(); }; ~MyPanel1(){}; }; ///////////////////////////////////////////////////// class App : public wxApp { bool OnInit() { // Hauptfenster initialisieren wxFrame *mainFrame = new MyFrame1(NULL); mainFrame->Show(); SetTopWindow(mainFrame); return TRUE; } }; IMPLEMENT_APP(App) #endif //__noname__
noname.cpp:
#include "noname.h" void MyFrame1::onMyKeyDown(wxKeyEvent &event){ if(event.GetKeyCode() == WXK_ESCAPE){ changePanel(dynamic_cast<MyPanel2*>(shownPanel) != NULL); Refresh(); } //event.StopPropagation(); //event.Skip(); } void MyFrame1::changePanel(bool toMyPanel1){ wxPanel *newPanel; if(toMyPanel1) newPanel = new MyPanel1(this); else newPanel = new MyPanel2(this); changeContainer->Replace(shownPanel, newPanel, true); changeContainer->Layout(); delete shownPanel; shownPanel = newPanel; } MyFrame1::MyFrame1( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxFrame( parent, id, title, pos, size, wxFULL_REPAINT_ON_RESIZE | wxDEFAULT_FRAME_STYLE ) { changeContainer = new wxBoxSizer(wxVERTICAL); shownPanel = new MyPanel2(this); changeContainer->Add(shownPanel); wxStaticText *m_staticText1 = new wxStaticText( this, wxID_ANY, wxT("press esc"), wxDefaultPosition, wxDefaultSize, 0 ); changeContainer->Add(m_staticText1); SetSizer(changeContainer); } BEGIN_EVENT_TABLE(MyFrame1, wxFrame) EVT_KEY_DOWN(MyFrame1::onMyKeyDown) END_EVENT_TABLE()
entfernt man nun den kommentar in der cpp in zeile 9 von event.skip(), so kommt eine fehlermeldung, die der in meinem programm sehr nahe kommt (siehe erstes posting).
hat jemand eine idee, wie ich hier weiter komme?!?! hat es eventuell mit dem resumePropagation() in textCtrlPropagator::OnKeyDown zu tun? ich hielt das bisher für notwendig, damit das event bis zu MyFrame1 durch kommt.
maximalsten dank!
-
dazu sollte ich vielleicht noch erwähnen, dass die offensichtliche maßnahme, einfach auf event.skip() zu verzichten, in meinem programm nicht greift. dort ist gar kein event.skip in der fraglichen funktion enthalten.
was könnte dieses problem, das im minimalen test durch event.skip() ausgelöst wird, noch auslösen?
die frage ist natürlich auch, ob es wirklich derselbe fehler wie im ersten post ist, oder nur ein sehr ähnlicher..... seufz....
-
ok, das problem scheint gelöst.
falsch war, dass das panel mit dem wxTextCtrl per key down sein eigenes löschen getriggert hat. wenn ich im minimaltest in changePanel statt delete shownpanel einfach shownpanel->hide() einsetze, dann läuft alles.weiß vielleicht jemand, warum das bei wxTextCtrl der fall ist? das löschen war von anderen controls aus nie ein problem, auch wenn die auslösenden controls selber dabei gelöscht wurden. kann ich mir das so erklären, dass das event, wenn es von einem wxTextCtrl ausgelöst wurde, nochmal zum wxTextCtrl zurück muss?!?
danke für eure mühe.