Catch-Problem



  • (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.



  • m_cCode wurde so deklariert:

    char			m_cCode[2048];
    

    (std::strncpy mit Größe von m_cCode nehmen ...)

    Das muss ich mal ausprobieren, wenn aber m_cCode größer ist als pcCode, führt das dann nicht zu einer AccessViolation, weil er mehr zu kopieren versucht als pcCode hergibt?

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

    Das würde doch bedeuten dass ich mindestens die ganze Klasse auf std::string umschreiben müsste, wobei ich die Befehle zum direkten einlesen eines Strings aus einer Datei auch nicht so genau kenne, zudem geht die Init-Methode noch so weiter gehen:

    f(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);
    	}
    

    Dabei nimmt die Microsoft-Methode CreateEffect ja auch ein char-Array.
    Gibts nicht noch einen anderen Weg?



  • Hmm? std::string hat doch die Funktion c_str um dir einen const char* zurück zu geben ... sollte reichen (und length für die Länge).



  • und einen String aus einer Datei lesen, oder soll ich ein char* auslesen und daraus einen String machen?



  • mit fstreams arbeiten wäre dann wohl angesagt. ansonsten kannst du char* auch ganz leicht in std::strings umwandeln. (std::string bietet dafür einen eigenen konstruktor an)



  • ich hab aber nochmal eine verständnisfrage:
    Bisher hängt das Programm doch "nur" bei strcpy. Was sind denn alles Ursachen weshalb diese Methode einen Absturz verursachen kann?

    Ein Nullpointer kann es ja eigentlich nach den Testausgaben nicht sein.
    Das fehlende Nullzeichen, kann ich das so prüfen:

    if(pcCode[strlen(pcCode)-1] != '\0') TB_INFO("NULL FEHLT");
    

    Kann es noch weitere Fehlerquellen geben?



  • Andreas_LL schrieb:

    Das fehlende Nullzeichen, kann ich das so prüfen:

    if(pcCode[strlen(pcCode)-1] != '\0')
    

    lol... Vielleicht solltest du prüfen, ob das Nullzeichen noch im reservierten Speicher liegt. Was aber nicht geht, da du nicht weißt, wieviel Speicher reserviert wurde. ...

    PS: Deine Abfrage ergibt immer entweder einen Crash oder true. false kann dort gar nicht zurückgegeben werden. /edit: natürlich andersrum 🤡

    PPS: Ist nicht bös gemeint 🙂



  • Ist nicht bös gemeint

    Ist schon okay, kann C++ ja wirklich noch nicht so gut.

    PS: Deine Abfrage ergibt immer entweder einen Crash oder true.

    Das kann ich leider noch nicht ganz nachvollziehen, ich hätte am ehesten gesagt: "Deine Abfrage ergibt immer entweder einen Crash oder false."
    Gibt es also einen Crash, weil man überhaupt nicht auf den char* zugreifen kann, ohne einen Crash zu erzeugen?

    Was kann denn die Ursache für so etwas sein?
    Oder gibt es noch andere Gründe warum strcpy() versagt?



  • Andreas_LL schrieb:

    PS: Deine Abfrage ergibt immer entweder einen Crash oder true.

    Das kann ich leider noch nicht ganz nachvollziehen, ich hätte am ehesten gesagt: "Deine Abfrage ergibt immer entweder einen Crash oder false."

    Ups, hab da was vertauscht 🤡
    Das Ding ist halt, dass strlen ja soweit durch den Speicher geht, bis es eine '\0' findet. Dabei weiß es natürlich nicht, wann der reservierte Speicher für deinen string vorbei ist, also kann es durchaus auch darüber hinaus laufen. An der Stelle x-1 ist dann natürlich folglich nie eine 0, du weißt aber trotzdem noch nicht, ob du dich noch im für dich reservierten Speicher befindest. Achso, und falls der string leer ist (also strlen gibt 0 zurück), greifst du auch wieder auf Speicher zu, der dir nicht gehört.
    Von daher ist diese Abfrage nicht besonders praktisch 😉

    strcpy macht halt nix anderes, als vom übergebenen zu kopierenden String die Länge zu ermitteln und an die angegebene Speicherstelle zu schreiben. Ursachen für Fehler können halt sein, dass einer der Zeiger sonstwohin zeigt oder der reservierte Platz für die Kopie nicht groß genug ist.


Anmelden zum Antworten