Catch-Problem



  • 'strcpy' sollte mit ziemlicher Sicherheit *keine* Ausnahme werfen und Deine Berechnung da, '1 / Zahl', wirft auch definitiv keine Ausnahme.



  • sry, ich hab noch nicht soviel mit Exceptions gearbeitet,
    aber ich teile doch in dem Beispiel durch Null, für mich ist das eine Ausnahme, die ich mit catch abfangen kann. Und mit catch(...) konnte ich auch verhindern dass von windows der Nicht Senden Dialog kam, sondern das Programm normal beedet wurde.
    http://c.ittoolbox.com/groups/technical-functional/cpp-l/c-exception-handling-223506
    Hier steht das strcpy eine Exception wirft, wenn man eine Nullpointer verwendet, deshalb wollte ich mal schauen, ob das auch die Fehlerursache bei meinem Programm ist.

    Was habe ich denn nun falsch verstanden und wie muss ich es anders machen?

    Viele Grüße
    Andreas



  • Andreas_LL schrieb:

    aber ich teile doch in dem Beispiel durch Null, für mich ist das eine Ausnahme, die ich mit catch abfangen kann.

    Für C++ aber eben nicht. Ausnahmen sind eine ziemlich zeitintensive Sache, egal, ob sie tatsächlich geworfen werden oder nur die Möglichkeit dazu besteht. Und C++ ist nunmal enorm auf Geschwindigkeit getrimmt und eine so essenzielle Operation wie eine Division *muss* schnell sein.

    Daher bietet C++ hier keinerlei Ausnahmenbehandlung an.

    Und mit catch(...) konnte ich auch verhindern dass von windows der Nicht Senden Dialog kam, sondern das Programm normal beedet wurde.

    Bei mir klappt es nicht, und es ist definitiv auch nicht C++, sondern anscheinend eine Erweiterung Deines speziellen Compilers.

    http://c.ittoolbox.com/groups/technical-functional/cpp-l/c-exception-handling-223506
    Hier steht das strcpy eine Exception wirft, wenn man eine Nullpointer verwendet,

    Nein, da steht, dass 'strcpy' *keine* Ausnahme wirft, sondern dass nur der C++-Compiler in einer bestimmten Version mit einer bestimmten (Debug-) Konfiguration das tut. Generell ist das aber nicht der Fall. Microsoft will einem hier anscheinend nur Hilfe zum Debuggen bieten (gut so). Der Fehler ist auch keiner, der durch eine Ausnahme behandelt werden sollte, sondern ein grundsätzlicher Logikfehler, der in C++ einen Crash verdient: Nämlich anscheinend ein Nullzeiger an der falschen Stelle: Das gilt es zu verhindern, das ist nämlich ein Bug in Deinem Programm (und Bugs werden generell nicht mit Ausnahmen behandelt).



  • Null-Division wirft eine Exception ... Ist nur die Frage auf welchem Level ... und es ist bestimmt keine std::exception die geworfen wird 😉

    hmm

    printf("Drin");
        printf("%s", e.what());
    

    =>

    std::printf("Drin %s", e.what());
    

    Achja, bitte nicht C und C++ mischen ...

    const int number(0);
    
    try { double data(1.0 / number); std::cout << data << std::endl; }
    catch (...)
    {
        std::cerr << "FEHLER: Exception cought! Division by zero!" << std::endl;
    }
    

    ...



  • (D)Evil schrieb:

    Null-Division wirft eine Exception ... Ist nur die Frage auf welchem Level ... und es ist bestimmt keine std::exception die geworfen wird 😉

    Klar, auf CPU-Level vielleicht. Aber das ist (zumindest bei mir) keine fangbare Ausnahme. Auch 'catch (...)' fängt nichts, und sollte dies AFAIK auch gar nicht (wobei ich das jetzt nicht im C++-Standard nachgelesen habe).



  • Hmm jap auf Hardware-Level meinte ich. Und war mir eigtl. auch relativ sicher, dass du es durch catch(...) abfangen kannst.



  • (D)Evil schrieb:

    Hmm jap auf Hardware-Level meinte ich. Und war mir eigtl. auch relativ sicher, dass du es durch catch(...) abfangen kannst.

    Ah, eventuell mein Fehler. Es geht nur bei 'double', nicht bei 'int'. Trotzdem würde mich jetzt mal interessieren, was da der Standard sagt. Habe jetzt aber keine Zeit, das nachzulesen, muss ich bei Gelegenheit mal tun.



  • Hallo, erstmal vielen Dank für die Antworten!

    Ich habe auch nicht vor den Bug mit einem catch zu beseitigen, ich wollte eigentlich nur wie ich es in Java kenne die Exception abfangen und anschließend die Ursache der Exception ausgeben.

    Ist das im Fall von strcpy() denn möglich, da es sich ja dabei um eine spezifische vom Compiler erzeugte Meldung handelt?

    Noch eine Frage, ist es denn schlimm wenn man C und C++ verwechselt, weil ich das immer so gemacht habe und dein Beispiel mir schon recht gewöhnungsbedürftig vorkommt.

    Viele Grüße
    Andreas



  • (D)Evil schrieb:

    Hmm jap auf Hardware-Level meinte ich. Und war mir eigtl. auch relativ sicher, dass du es durch catch(...) abfangen kannst.

    So, ich habe jetzt doch mal nachgelesen und ich interpretiere das anders. Weder im Abschnitt über fundamentale Datentypen noch im Abschnitt über die Operatoren wird darauf eingegangen, dass diese eine Ausnahme erzeugen werden können und in [except] steht zu lesen:

    A handler will be invoked only by a throw-expression invoked in code executed in the handler's try block or in functions called from the handler's try block.

    – Ich lese das so, dass nur solche Ausnahmen gefangen werden können, die durch „throw“ geworfen werden, keine Hardware-Interrupts.



  • TB_INFO("DEBUGG3");
    	try
    	{
    		strcpy(m_cCode, pcCode);
    	}
    	catch(...)
    	{
    		TB_INFO("BIN IN CATCH");
    		if(m_cCode == NULL) TB_INFO("m_cCode ist NULL");
    		if(pcCode == NULL) TB_INFO("pcCode ist NULL");
    		TB_INFO("BIN HIER");
    
    		PostQuitMessage(0);
    	}
    	TB_INFO("DEBUG_SCHLECHT");
    

    Das ist der Quelltext bei dem der Fehler immer auftritt. Mein Problem ist jetzt,dass die Ausgabe "BIN IN CATCH" kommt und direkt danach "BIN HIER", d.h. doch das beide Parameter von strcpy nicht NUll sind. Wie kann ich denn jetzt herausfinden warum das Programm ca. alle 5 Mal genau an dieser Stelle abstürtzt?

    Viele Dank für eine Antwort, viele Grüße
    Andreas



  • Hast Du genügend Speicher reserviert? Der von Dir gepostete Code ist ziemlich uninteressant; interessant sind hingegen Deklaration und Befüllung von 'm_cCode' und 'pcCode'.



  • Übrigens: All diese Fehler würden garantiert nicht auftreten, wenn Du C++-Strings statt C-Strings verwenden würdest (also die Klasse 'std::string'). Wie (D)Evil schon sagte, es hat erhebliche Vorteile, in C++ die von C++ angebotenen Möglichkeiten zu nutzen und nicht auf veraltete C-Funktionen zurückzugreifen.



  • Hallo,
    das ganze ist Teil eines 3D-Spiels und der Fehler tritt in der Engine auf, die komplett mit char*-Strings aufgebaut ist. Ich müsste also sehr viele Teil umschreiben, welches ziemlich aufwendig wäre.

    Der geht doch in den Catch-Block rein, kann man da nicht herausfinden warum der Fehler aufgetreten ist?

    Viele Grüße
    Andreas



  • Das Ganze ist Teil der Init-Methode für Effekte. Der Aufruf kann somit von ziemlich vielen Seite kommen, da es z.B. für jedes Model oder für die GUI mehrere Effekte gibt. Wenn man da nicht weiß, warum strcpy versagt wird das sehr schwer den Fehler zu finden.



  • Vielleicht ist noch wichtig, dass an dieser Stelle das Spiel aus 2 Threads besteht. Die Render-Methode und dieser Absschnitt mit der Effekt-Initialisierung sind dabei aber in Critical Sections gepackt.



  • Hmm TriBase Engine von David Scherfgen? Also wenn duuns nicht die entsprechenden Ausschnitte posten willst, solltest du dich evtl. mal auf www.spieleprogrammierer.de/phpBB2/ melden ...



  • Das wird ein typischer Speicherüberlauf-Fehler beim strcpy sein, d.h. so wie K.R. schon geschrieben hat, ist der reservierte Puffer anscheinend zu klein (evtl. fehlt auch einfach das abschließende Null-Zeichen beim 2. String).
    Laß dir einfach mal die beiden Strings ausgeben...
    und am besten du setzt einen breakpoint und läßt dir den Call-Stack anzeigen (oder compiliert ihr nur im Release-Modus, aber selbst dort lassen sich Debug-Infos unterbringen).



  • Andreas_LL schrieb:

    ...ich wollte eigentlich nur wie ich es in Java kenne die Exception abfangen und anschließend die Ursache der Exception ausgeben....

    Da aber C++ nicht auf einer javaspezifischen VM, sondern einer (NICHT-C++-spezifischen) RM läuft, wirft diese auch keine C++-Exceptions.

    Gruß,

    Simon2.



  • Hallo erstmal vielen Dank für die Antwort,
    es handelt sich tatsächlich um die Tribase-Engine (Woher wussten Sie dass? 🙂 )
    Ich habe da aber schon sehr viel drin verändert, so dass ich denke, dass es sich dabei um ein C++-Problem handelt und ich die entsprechenden Passagen zeigen kann, da ja nur noch wenig original ist:
    Ein Effekt sieht so in etwa aus: (ist nur ein Auszug)

    // Materialeinstellungen
    		MaterialDiffuse		= {0.500f, 0.500f, 0.500f, 0.000f};
    		MaterialAmbient		= {0.000f, 0.000f, 0.000f, 0.000f};
    		MaterialEmissive	= {0.000f, 0.000f, 0.000f, 0.000f};
    		MaterialSpecular	= {0.200f, 0.200f, 0.200f, 0.000f};
    

    Dieser wird aus der Modeldatei geladen. Dabei steht Materialambient im Moment überall auf 0. Ich brauche aber noch einmal einen, in dem die ersten beiden Werte 1 sind, damit das Objekt gelb erscheint und einmal wo nur der erste Wert 1 ist, damit das Objekt auch mal rot erscheinen kann. (Die Werte stehen in Reihenfolge für Rot, Grün, Blau, Alpha).

    // Anzahl der Effekte lesen
    			if(pVFile->Read(sizeof(DWORD), &m_dwNumEffects)) TB_ERROR("Fehler beim Lesen der Effektanzahl!", TB_ERROR);
    			TB_INFO("DEBUG_M7");
    			// Genug Speicher für die Effekte reservieren
    			m_pEffects = (tbModelEffect*)(tbMemAlloc(3*m_dwNumEffects * sizeof(tbModelEffect)));
    			if(!m_pEffects) TB_ERROR_OUT_OF_MEMORY(TB_ERROR);
    
    			// Jeden Effekt durchgehen
    			for(dwEffect = 0; dwEffect < m_dwNumEffects; dwEffect++)
    			{
    				// Den Effekt-Header lesen
    				if(pVFile->Read(sizeof(tbModelEffectHeader), &m_pEffects[dwEffect].Header))
    				{
    					// Fehler!
    					TB_ERROR("Fehler beim Lesen des Effekt-Headers!", TB_ERROR);
    				}
    
    				memcpy((void*)&m_pEffects[(dwEffect+m_dwNumEffects)].Header, (void*)&m_pEffects[dwEffect].Header, sizeof(tbModelEffectHeader));
    				memcpy((void*)&m_pEffects[(dwEffect+m_dwNumEffects*2)].Header, (void*)&m_pEffects[dwEffect].Header, sizeof(tbModelEffectHeader));
    
    				// Speicher für den Effektcode reservieren
    				m_pEffects[dwEffect].pcCode = (char*)(tbMemAlloc(m_pEffects[dwEffect].Header.dwEffectCodeSize));
    				if(!m_pEffects[dwEffect].pcCode) TB_ERROR_OUT_OF_MEMORY(TB_ERROR);
    
    				m_pEffects[dwEffect+m_dwNumEffects].pcCode = (char*)(tbMemAlloc(m_pEffects[dwEffect].Header.dwEffectCodeSize));
    				if(!m_pEffects[dwEffect+m_dwNumEffects].pcCode) TB_ERROR_OUT_OF_MEMORY(TB_ERROR);
    
    				m_pEffects[dwEffect+m_dwNumEffects*2].pcCode = (char*)(tbMemAlloc(m_pEffects[dwEffect].Header.dwEffectCodeSize));
    				if(!m_pEffects[dwEffect+m_dwNumEffects*2].pcCode) TB_ERROR_OUT_OF_MEMORY(TB_ERROR);
    
    				// Effektcode lesen
    				if(pVFile->Read(m_pEffects[dwEffect].Header.dwEffectCodeSize,
    								m_pEffects[dwEffect].pcCode))
    				{
    					// Fehler!
    					TB_ERROR("Fehler beim Lesen des Effektcodes!", TB_ERROR);
    				}
    
    				m_pEffects[dwEffect+m_dwNumEffects].Header.dwEffectCodeSize = m_pEffects[dwEffect].Header.dwEffectCodeSize;
    				memcpy(m_pEffects[dwEffect+m_dwNumEffects].pcCode, m_pEffects[dwEffect].pcCode, m_pEffects[dwEffect].Header.dwEffectCodeSize);
    
    				m_pEffects[dwEffect+m_dwNumEffects*2].Header.dwEffectCodeSize = m_pEffects[dwEffect].Header.dwEffectCodeSize;
    				memcpy(m_pEffects[dwEffect+m_dwNumEffects*2].pcCode, m_pEffects[dwEffect].pcCode, m_pEffects[dwEffect].Header.dwEffectCodeSize);
    
    				char* rueck = strstr(m_pEffects[dwEffect+m_dwNumEffects].pcCode, "MaterialAmbient");
    //Effekt Gelb machen
    				if(rueck){
    					//rueck+=24;
    					while(*rueck != '{') rueck++;
    					rueck++;
    					*rueck = '1';
    					rueck+=2;
    					*rueck = '0';
    					rueck+=6;
    					*rueck = '1';
    					rueck+=2;
    					*rueck = '0';
    				}
    				else TB_INFO("nicht erfolgreich");
    
    				rueck = NULL;
    				rueck = strstr(m_pEffects[dwEffect+m_dwNumEffects*2].pcCode, "MaterialAmbient");
    //Effekt Rot machen
    				if(rueck){
    					//rueck+=24;
    					while(*rueck != '{') rueck++;
    					rueck++;
    					*rueck = '1';
    					rueck+=2;
    					*rueck = '0';
    				}
    				else TB_INFO("nicht erfolgreich");
    
    				// tbEffect-Klasseninstanz erstellen
    				m_pEffects[dwEffect].pEffect = new tbEffect;
    				if(!m_pEffects[dwEffect].pEffect)
    				{
    					// Fehler!
    					TB_ERROR_OUT_OF_MEMORY(TB_ERROR);
    				}
    
    				m_pEffects[dwEffect+m_dwNumEffects].pEffect = new tbEffect;
    				if(!m_pEffects[dwEffect+m_dwNumEffects].pEffect)
    				{
    					// Fehler!
    					TB_ERROR_OUT_OF_MEMORY(TB_ERROR);
    				}
    
    				m_pEffects[dwEffect+m_dwNumEffects*2].pEffect = new tbEffect;
    				if(!m_pEffects[dwEffect+m_dwNumEffects*2].pEffect)
    				{
    					// Fehler!
    					TB_ERROR_OUT_OF_MEMORY(TB_ERROR);
    				}
    
    				TB_INFO("DEBUG_BEGINNE EFFEKT INIT");
    				// Den Effekt initialisieren
    				if(m_pEffects[dwEffect].pEffect->Init(m_pEffects[dwEffect].pcCode,
    													  m_pEffects[dwEffect].Header.dwEffectCodeSize))
    				{
    					// Fehler beim Erstellen des Effekts!
    					TB_ERROR("Fehler beim Erstellen des Effekts!", TB_ERROR);
    				}
    
    				TB_INFO("DEBUG_BEGINNE EFFEKT INIT2");
    				if(m_pEffects[dwEffect+m_dwNumEffects].pEffect->Init(m_pEffects[dwEffect+m_dwNumEffects].pcCode,
    													  m_pEffects[dwEffect+m_dwNumEffects].Header.dwEffectCodeSize))
    				{
    					// Fehler beim Erstellen des Effekts!
    					TB_ERROR("Fehler beim Erstellen des Effekts! gelb", TB_ERROR);
    				}
    
    				TB_INFO("DEBUG_BEGINNE EFFEKT INIT3");
    				if(m_pEffects[dwEffect+m_dwNumEffects*2].pEffect->Init(m_pEffects[dwEffect+m_dwNumEffects*2].pcCode,
    													  m_pEffects[dwEffect+m_dwNumEffects*2].Header.dwEffectCodeSize))
    				{
    					// Fehler beim Erstellen des Effekts!
    					TB_ERROR("Fehler beim Erstellen des Effekts! rot", TB_ERROR);
    				}
    

    Es wird also fast alles dreimal gemacht, weil ich jeden Effekt verdreichfachen will um die verschiedenen Farben zu bekommen. Es wird dabei nach MaterialAmbient gesucht und dann genau abgezählt und die Einsen entsprechend gesetzt. (Das ist recht fehleranfällig, aber ich denke, dass die Abstände immer gleich sind)
    Was ich jetzt komisch finde ist, das das Spiel nur manchmal beim Laden abstürzt, wenn es grundlegender Fehler wäre würde er doch immer Abstürzen oder nicht?
    Nach der Debug-Ausgabe stürtzt er mal nach DEBUG_BEGINNE EFFEKT INIT, DEBUG_BEGINNE EFFEKT INIT2 oder DEBUG_BEGINNE EFFEKT INIT3 ab.
    Die InitMethode des Effekts sieht so aus:

    tbResult tbEffect::Init(char* pcCode,
    						int iSize)
    {
    	HRESULT hResult;
    
    	// Die Klasseninstanz zurücksetzen.
    	// Damit wird ermöglicht, dass der Init-Aufruf mehrere Male mit
    	TB_INFO("DEBUG_E1");
    	// derselben Instanz funktioniert.
    	Exit();
    
    	// Parameter prüfen und sicherstellen, dass Direct3D initialisiert wurde
    	if(!pcCode)							TB_ERROR_NULL_POINTER("pcCode", TB_ERROR);
    	if(iSize == 0 || iSize < -1)		TB_ERROR_INVALID_VALUE("iSize", TB_ERROR);
    	if(!tbDirect3D::IsInitialized())	TB_ERROR("Direct3D wurde noch nicht initialisiert!", TB_ERROR);
    	TB_INFO("DEBUG_E2");
    	EnterCriticalSection(tb_gCS);
    	// Länge anpassen
    	TB_INFO("DEBUGG");
    	if(iSize == -1) 
    	{
    		TB_INFO("DEBUGG1");
    		iSize = strlen(pcCode);
    		TB_INFO("DEBUGG2");
    	}
    	TB_INFO("DEBUGG3");
    	try
    	{
    		strcpy(m_cCode, pcCode);
    	}
    	catch(...)
    	{
    		TB_INFO("BIN IN CATCH");
    		if(m_cCode == NULL) TB_INFO("m_cCode ist NULL");
    		if(pcCode == NULL) TB_INFO("pcCode ist NULL");
    		TB_INFO("BIN HIER");
    
    		PostQuitMessage(0);
    	}
    	TB_INFO("DEBUG_SCHLECHT");
    	// Jetzt den Effekt erstellen
    
    	if(FAILED(hResult = D3DXCreateEffect(tbDirect3D::Instance().GetDevice(),
    										 pcCode, iSize, NULL, NULL, 0,
    										 tb_g_pEffectPool, &m_pEffect, NULL)))
    	{
    		// Fehler!
    		TB_ERROR_DIRECTX("D3DXCreateEffect", hResult, TB_ERROR);
    	}
    

    Wobei er ja im Fall eines unberechenbaren Absturzes im Catch-Block landet.
    Nach meiner Ansicht sind das alle relevanten Stellen oder fehlt irgendwas?

    Ich habe das Spiel schon häufiger mit Debugger laufen lassen, aber dabei ist der Fehler noch nie aufgetreten. Zudem ist das (für mich zumindest) ein Problem, da ich ja normal eine Exe ausführe und dieser Teil sich in der DLL befindet, in die ich beim Debuggen meiner Exe keine Breakpoints einbauen kann.

    Beim Ausgeben, wie erkenne ich denn dann, dass das Nullzeichen fehlt, und warum kann es überhaupt passieren das das Nullzeichen fehlt, ich mein am Ende des Strings arbeite ich schließlich gar nicht.

    Habt ihr noch eine Idee, wo der Fehler liegt, wäre echt nett, ich weiß dass das jetzt recht viel Code ist.

    Sry, noch eine Frage, ich hab mir den Code nochmal genau durchgelesen und ich habe den Code auf die anderen beiden Effekte so übertragen:

    m_pEffects[dwEffect+m_dwNumEffects].Header.dwEffectCodeSize = m_pEffects[dwEffect].Header.dwEffectCodeSize;
                    memcpy(m_pEffects[dwEffect+m_dwNumEffects].pcCode, m_pEffects[dwEffect].pcCode, m_pEffects[dwEffect].Header.dwEffectCodeSize
    

    Könnte das der Fehler sein, dass ich da memcpy und nicht strcpy verwendet habe?
    Welches dann allerding komisch wäre ist, dass er auch mal bei DEBUG_BEGINNE EFFEKT INIT abgestürzt ist, obwohl da eigentlich der unangetastete Effekt initialisiert wird.
    Viele Grüße
    Andreas



  • Wie Groß ist m_cCode? (std::strncpy mit Größe von m_cCode nehmen ...)

    Hmm nutzt einfach diret std::string und halte dich an const-correctness.


Anmelden zum Antworten