Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - Try-Except Raise funktioniert nicht richtig


schippi - Mo 22.10.07 11:16
Titel: Try-Except Raise funktioniert nicht richtig
Hallo, bei folgendem Programmcode funktioniert Try-Except nicht richtig.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
Var
  Sum: PByte;
  Size: Integer;
Begin
  Size := 2147483646;
  GetMem(Sum, Size);

  try
    FillChar(Sum^, Size, 0);
  except
    showmessage('Blabla');
  end;
End;


Hier tritt bei FillChar eine Access Violation auf. Wieso springt das Programm nicht in except :?:

Was bei mir ebenfalls nicht funktioniert:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
 Try
   a:=1/0;
 except
   showmessage('Test');
   raise Exception.Create('Beispiel-Exception');
 End;


es wird except einfach übersprungen. Wenn ich es aber ein klein wenig ändere, dann springt das Programm in except und zeigt die Meldung 'Test' an, aber die Meldung 'Beispiel-Exception' wird nicht angezeigt.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
 Try
   a:=1;
   a:=a/0;
 except
   showmessage('Test');
   raise Exception.Create('Beispiel-Exception');
 End;


Also ich steh aufm Schlauch... :autsch:

Moderiert von user profile iconNarses: Code- durch Delphi-Tags ersetzt


Alpha_Wolf - Mo 22.10.07 11:28

Anstelle von E:Exception kannst du alle Arten von Exceptions hernehmen. Mit E:Exception fängst du alle ab. Du kannst aber auch auf bestimmte eingehen wie in deinem Fall auf die EAccessViolation und darauf speziell reagieren. Raise musst du gar nicht aufrufen! Nimm einfach ein ShowMessage oder eine MessageBox mit dem jeweiligen Messagetyp.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
  try
    // irgendwas
  except
    on E:Exception do begin
      // Hier eine Meldung anstelle der Exception ausgeben, RAISE braucht nicht aufgerufen werden  
    end;
  end;


Marc. - Mo 22.10.07 11:29


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
  try
    GetMem(Sum, Size);
    FillChar(Sum^, Size, 0);
  except
    showmessage('Blabla');
  end;
End;

Da bereits in GetMem(); eine EOutOfMemory-Exception auftritt, solltest du diese auch mit in den Block nehmen. ;)

grüße,
Marc


Alpha_Wolf - Mo 22.10.07 11:34

user profile iconMarc. hat folgendes geschrieben:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
  try
    GetMem(Sum, Size);
    FillChar(Sum^, Size, 0);
  except
    on E:EOutOfMemory do begin
      showmessage('Blabla');
    end;

  end;
End;



Sonst gibt die App die Exception aus und danach deine Showmessage..


Narses - Mo 22.10.07 11:46

Moin!

Auch sehr praktisch: ;)

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
try
  GetMem(Sum, Size);
  FillChar(Sum^, Size, 0);
except
  ShowMessage(Exception(ExceptObject).Message); // Zeigt den Originaltext der Exception an
end;

cu
Narses


schippi - Mo 22.10.07 11:47

Hab jetzt alles probiert, was ihr mir empfohlen habt. Aber klappt bei mir immer noch net - ist immer noch so wie´s am Anfang war.


Alpha_Wolf - Mo 22.10.07 12:08

Dann zeig mal deinen modifizierten Code.. hast du auch wirklich das "on E:Exception do begin end;" mit eingabracht im "except" Teil?

Nur Fehler die im Try-Block auftreten werden auch im Except-Block behandelt. Alles andere wird nicht abgefangen.


schippi - Mo 22.10.07 12:27

Hab mal ein neues Projekt erstellt und nur mal die verschiedenen Codestellen ausprobiert. Da hat komischerweise alles funktioniert.
Dann hab ich mein anderes Projekt genommen,das wenn ich nicht absichtlich einen Fehler provoziere, einwandfrei funktioniert. Nur wenn ich dann folgenden Code reinkopiere, gehts wieder net.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
 Size := 2147483646;
  Try
    GetMem(Sum, Size);
    FillChar(Sum^, Size, 0);
  Except
    On E: EOutOfMemory Do
    Begin
      showmessage('Blabla');
    End;
  End;


Es kommt wieder beim FillChar des blöde Access Violation und nicht mein 'BlaBla' :bawling:


Marc. - Mo 22.10.07 12:30

Bei mir funktioniert dein Code wunderbar. :roll:


Narses - Mo 22.10.07 12:31

Moin!

user profile iconschippi hat folgendes geschrieben:
Es kommt wieder beim FillChar des blöde Access Violation und nicht mein 'BlaBla' :bawling:
Zunächstmal ist mir zwar schleierhaft, wozu man einen 2GB RAM-Block anfordern sollte (was auf jeder Win32-Maschine nicht klappen sollte...), aber nun gut. :?

Probier das nochmal so:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
 Size := 2147483646;
  Try
    GetMem(Sum, Size);
    if Assigned(Sum) then
      FillChar(Sum^, Size, 0);
  Except
    ShowMessage(Exception(ExceptObject).Message);
  End;

cu
Narses


schippi - Mo 22.10.07 12:35

Mir fällt gerade noch ein, ich benutz in dem Projekt auch die TNBFPA-Socket Komponente von Narses. Ich glaub da werden die Fehlermeldungen irgendwie anders abgefangen. Kann das was mit meinem Problem zu tun haben?


Narses - Mo 22.10.07 12:46

Moin!

user profile iconschippi hat folgendes geschrieben:
Mir fällt gerade noch ein, ich benutz in dem Projekt auch die TNBFPA-Socket Komponente von Narses. Ich glaub da werden die Fehlermeldungen irgendwie anders abgefangen. Kann das was mit meinem Problem zu tun haben?
Kommt darauf an, wo die Exception auftritt. Wenn du innerhalb der OnExecute-Methode eine Exception produzierst, dann wird das über die OnError-Methode abgewickelt, und nicht als Exception. :idea: (Steht aber auch genau so in der Doku! :les: ;) )

Aber ohne den Code zu kennen, kann ich nicht mehr dazu sagen. :nixweiss:

cu
Narses


schippi - Mo 22.10.07 12:46

Hallo Narses!
Den 2 GB Ram-Block benutz ich ja nur um die Exception auszuprobieren. Sonst natürlich net.
Der Code funktioniert auch nicht in meinem Projekt. Kann das was mit deiner Komponente zutun haben? Weil in einem neuen Projekt funktionierts ja...

Viele Grüße
Schippi


Alpha_Wolf - Mo 22.10.07 12:50

Wenn du dein Programm in deiner Laufzeitumgebung kompilierst und startest dann kommt immer die Exception (außer du stellst sie in den Projektoptionen aus) Starte mal die exe direkt in deinem Zielverzeichnis. Da sollte nur deine Meldung erscheinen.


schippi - Mo 22.10.07 13:08

Erwischt! Ich bin in der OnExcecute-Methode! :oops: Ich hab da wirklich net mehr dran gedacht.

Kann ich trotzdem den Fehler irgendwie abfangen, so dass kein Access violation kommt?

Zitat:
Alpha_Wolf:
Starte mal die exe direkt in deinem Zielverzeichnis. Da sollte nur deine Meldung erscheinen.


Dann wird das Programm einfach beendet ohne irgendeine Meldung.


Narses - Mo 22.10.07 13:29

Moin!

Hier mal der entsprechende Ausschnitt aus dem TNBFPA-Quelltext (Methode TProtocolAdapter.ParseStack):

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
        if Assigned(FOnExecute) then // externer Prozessor zugewiesen?
          try // der externe Befehlsprozessor könnte eine Exception "erleiden"
            FOnExecute(FSender,Self); // dann aufrufen
          except // fangen, damit die eigenen Datenstrukturen nicht strubbelig werden
            if Assigned(FOnError) then
              FOnError(FSender,Self,paeException,Integer(ExceptObject));
          end;
Wie man sieht, werden Exceptions in der OnExecute-Methode gefangen und an die OnError-Methode weitergeleitet.

Jetzt kommt es darauf an, was genau passiert ist bzw. wie dein Code aussieht und warum die Exception aufgetreten ist. Es ist IMHO leider keine sonderlich schlaue Idee, mit dem Anfordern eines 2GB-RAM-Blocks einen Fehlerfall zu simulieren, weil Windows nach meiner Erfahrung mit speichermengenbasierten Problemen komische Reaktionen zeigt (und wenn nicht Windows, dann spätestens der Delphi-MemManager). Weiterhin könnte dein Code selbst "komisch" reagieren, wenn die Programmteile nach der Exception - die dann ja nicht durchlaufen werden - irgendwelche Datenstrukturen hätten initialisieren sollen, das aber nicht erfolgt ist und du später versuchst, darauf zuzugreifen. :idea:

Vielleicht solltest du einfach mal mit einer "realistischeren" Fehlersituation testen, da ich den 2GB-RAM-Block-Fall als unrealistisch ausschliessen würde (sowas würde ich schon vor dem Versuch abfangen)... :gruebel:

Weiterhin gilt:
user profile iconNarses hat folgendes geschrieben:
Aber ohne den Code zu kennen, kann ich nicht mehr dazu sagen. :nixweiss:


cu
Narses


schippi - Mo 22.10.07 14:03

So unrealistisch find ich den Code gar net, weil ich hab ja eine Socketverbindung und bekomme einen String gesendet. Zusätzlich bekomm ich noch die Länge des Strings (Size).Würde ich jetzt 10 Zeichen bekommen, wäre Size=10.
Bei einem Übertragungsfehler, könnte der Wert in Size ja plötzlich riesengroß sein(z.B. 235627282). Dann würde ja in dem Code eine Exception ausgelöst werden.


Delphi-Quelltext
1:
2:
  GetMem(Data, Size); 
  FillChar(Data^, Size, 0);


Oder sollte ich das vielleicht nicht unbedingt mit except abfangen?

Viele Grüße


Narses - Mo 22.10.07 14:09

user profile iconschippi hat folgendes geschrieben:
Bei einem Übertragungsfehler, könnte der Wert in Size ja plötzlich riesengroß sein(z.B. 235627282).
user profile iconNarses hat folgendes geschrieben:
sowas würde ich schon vor dem Versuch abfangen
:les: ;)


schippi - Mo 22.10.07 14:31

Alles klar :wink:

Danke an alle!


Narses - Mo 22.10.07 15:20

Moin!

user profile iconschippi hat folgendes geschrieben:
Mir fällt gerade noch ein,
Mir auch: ;)

user profile iconschippi hat folgendes geschrieben:
ich benutz in dem Projekt auch die TNBFPA-Socket Komponente von Narses.
Die können gar keine größeren Frames als 64KB transportieren; dein "2GB-Problem" ist damit also definitiv (zumindest auf Frame-Ebene des NBFPA) unmöglich. ;)

cu
Narses


schippi - Di 23.10.07 08:05

Hi!

Zitat:

Die können gar keine größeren Frames als 64KB transportieren; dein "2GB-Problem" ist damit also definitiv (zumindest auf Frame-Ebene des NBFPA) unmöglich.

Ich übertrag ja mehrere Frames. Werden beispielsweise 150000 Bytes übertragen, dann ist "size=150000" und die Daten werden halt auf 3 Frames aufgeteilt. Eigentlich sogar 4. Size kommt in einen extra Frame.
Sooo der Plan bis jetzt :wink:

Viele Grüße


alzaimar - Di 23.10.07 08:16

Ihr habt aber -glaube ich- das Problem einfach umschifft, indem ihr sagt: Das kann so nicht passieren. Das mag stimmen, aber die Frage bleibt: Wieso schmiert die Anwendung einfach ab und ist nicht in der Lage, die Exception so zu behandeln, wie sie sollte? :nixweiss:


Narses - Di 23.10.07 12:11

Moin!

user profile iconalzaimar hat folgendes geschrieben:
die Frage bleibt: Wieso schmiert die Anwendung einfach ab und ist nicht in der Lage, die Exception so zu behandeln, wie sie sollte? :nixweiss:
Das sehe ich auch so. ;)

Allerdings habe ich hierzu noch nix weiter gehört, und da vermute ich den Fehler:
user profile iconNarses hat folgendes geschrieben:
Weiterhin könnte dein Code selbst "komisch" reagieren, wenn die Programmteile nach der Exception - die dann ja nicht durchlaufen werden - irgendwelche Datenstrukturen hätten initialisieren sollen, das aber nicht erfolgt ist und du später versuchst, darauf zuzugreifen. :idea:
[...]
Weiterhin gilt:
user profile iconNarses hat folgendes geschrieben:
Aber ohne den Code zu kennen, kann ich nicht mehr dazu sagen. :nixweiss:


user profile iconschippi hat folgendes geschrieben:
Ich übertrag ja mehrere Frames. Werden beispielsweise 150000 Bytes übertragen, dann ist "size=150000" und die Daten werden halt auf 3 Frames aufgeteilt. Eigentlich sogar 4. Size kommt in einen extra Frame.
Was machst du denn mit den Daten? Alle im RAM lagern? :gruebel:

cu
Narses


schippi - Di 23.10.07 14:03

Hallo!
Das mit dem Code ist ein bißchen schwierig, weil der Code ziemlich umfangreich ist, es jetzt funktioniert und ich nicht genau weiß woran es genau lag. Aber ich benutze einige male "System.Move()" und das könnte meiner Meinung nach nicht ganz unkritisch sein, falls der reservierte Speicherbereich überschritten wird.
Hier z. B. mal was, das trotz Try-Except eine AccessViolation auslöst.

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
Procedure TForm1.Button1Click(Sender: TObject);
Var
  CheckSum: PByte;
  TestW: Word;
  SizeCheckSum, TestInt, Zahl: Integer;
Begin
  TestW := 999;
  TestInt := 100;
  Zahl := 3;
  Try
    SizeCheckSum := 7;
    GetMem(CheckSum, SizeCheckSum);
    FillChar(CheckSum^, SizeCheckSum, 0);

    System.Move(Pointer(@TestW)^, CheckSum^, 2);
    inc(CheckSum, 4);
    System.Move(Pointer(@TestInt)^, CheckSum^, 4); // Daten kopieren
    Inc(CheckSum, 4);
    System.Move(Pointer(@Zahl)^, CheckSum^, 4); // Daten kopieren
    Inc(CheckSum, 4);
    System.Move(Pointer(@Zahl)^, CheckSum^, 4); // Daten kopieren
    Inc(CheckSum,19);

    Dec(Checksum, SizeCheckSum);

    FreeMem(CheckSum, SizeCheckSum);
  Except
    showmessage('a');
  End;
End;

Beim ersten mal, wenn man den Button1 drückt funktionierts und beim 2. mal nicht mehr. Falls es trotzdem immer funktioniert, einfach mal die Werte bei Inc ein bißchen ändern.
Zitat:

Was machst du denn mit den Daten? Alle im RAM lagern?


Ich bekomme die Daten, kopiere sie in einen String (wo wir wieder bei system.move wären :wink: ), bilde die Checksum und falls die stimmt, übergeb ich den String einer Funktion in einer DLL.

So kopiere ich die Daten in den String:

Delphi-Quelltext
1:
2:
  SetLength(Data, DataLength);
  System.Move(PChar(PA.Inbound.Strings[5])^, Pointer(@Data[1])^, DataLength);


Grüße


Narses - Di 23.10.07 14:19

Moin!

:hair:

user profile iconschippi hat folgendes geschrieben:
Aber ich benutze einige male "System.Move()" und das könnte meiner Meinung nach nicht ganz unkritisch sein, falls der reservierte Speicherbereich überschritten wird.
:shock: Wenn du das doch schon weißt, warum gehst du dann nicht entsprechend vorsichtig damit um?! :?

user profile iconschippi hat folgendes geschrieben:
Hier z. B. mal was, das trotz Try-Except eine AccessViolation auslöst.

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
Procedure TForm1.Button1Click(Sender: TObject);
Var
  CheckSum: PByte;
  TestW: Word;
  SizeCheckSum, TestInt, Zahl: Integer;
Begin
  TestW := 999;
  TestInt := 100;
  Zahl := 3;
  Try
    SizeCheckSum := 7;
    GetMem(CheckSum, SizeCheckSum);
    FillChar(CheckSum^, SizeCheckSum, 0);

    System.Move(Pointer(@TestW)^, CheckSum^, 2);
    inc(CheckSum, 4);
    System.Move(Pointer(@TestInt)^, CheckSum^, 4); // Daten kopieren
    Inc(CheckSum, 4);
    System.Move(Pointer(@Zahl)^, CheckSum^, 4); // Daten kopieren
    Inc(CheckSum, 4);
    System.Move(Pointer(@Zahl)^, CheckSum^, 4); // Daten kopieren
    Inc(CheckSum,19);

    Dec(Checksum, SizeCheckSum);

    FreeMem(CheckSum, SizeCheckSum);
  Except
    showmessage('a');
  End;
End;

Beim ersten mal, wenn man den Button1 drückt funktionierts und beim 2. mal nicht mehr. Falls es trotzdem immer funktioniert, einfach mal die Werte bei Inc ein bißchen ändern.
:eyecrazy: Und du wunderst dich tatsächlich, dass das nicht richtig funktioniert?! :roll:

Ich möchte hier sicherheitshalber einmal kurz klarstellen, dass das überhaupt nix mit den TNBFPA-Komponenten zu tun hat, wenn dein Code bei solchen Aktionen abschmiert! :?

cu
Narses

//EDIT: "bei solchen Aktionen" in den letzten Satz eingefügt. ;)


Lossy eX - Di 23.10.07 14:58

Narsens: Also ich würde von meinem Code nie behaupten, dass er vollkommen fehlerfrei ist. Sondern immer, dass er keine offensichtlichen Fehler mehr enthält. Alles Andere kann man nie mit Gewissheit sagen. Nur nahezu ausschließen. ;)

Schippi: Nichts destro trotz teile ich Narsens Meinung. Mich würde mal interessieren was du mit dem Code vor hast? Willst du nur einen Fehler produzieren? Dann wäre der Zugriff auf einen nil pointer die bessere Alternative. Denn das erzeugt genau nur eine Zugriffsverletzung und du zerstörst damit keine fremden Speicherbereiche.

Denn nichts anderes tust du da. Du schreibst willenlos in anderen Speicherbereiche und gibst davon auch noch Stücke frei, denn 4 + 4 + 4 + 19 sind nicht 7. Und damit erreichst du lediglich, dass die Intigrität anderer Programmteile nicht mehr gewährleistet werden kann.

Der Speichermanager von Delphi alloziiert größere Speicherbereiche und bei GetMem wird ein kleineres Stück davon zurückgegeben. Wenn du jetzt eine Zugriffsverletzung bekommst dann besagt die lediglich, dass du außerhalb eines von Delphi kontrollierten Bereiches zugreifen wolltest. Wenn dein Pointer aber genau innerhalb eines solchen Blockes liegt hast du noch einige Kilobytes (keine Ahnung wie viel genau) in denen andere Daten von deinem Programm stecken können. Wenn du Glück hast wird der Block nicht benutzt. Dann passiert rein gar nichts. Aber ansonsten kann so ziemlich alles passieren.

[Edit] Was ich ganz vergessen habe. Dadurch dass du andere Bereiche veränderst schaffst du es natürlich solch einen Try Except Block zu umgehen, da du zwar noch im Speicher von Delphi bist aber die Daten von Programmcode zerstört hast die auf so etwas nicht gefasst sind. Also die nicht in einem Try Except Block liegen. Aus Geschwindigkeitsgründen etc. Das bedeutet der Fehler kommt eigentlich woanders her wird aber vor dir hervorgerufen.


Narses - Di 23.10.07 15:06

Moin!

user profile iconLossy eX hat folgendes geschrieben:
Narsens: Also ich würde von meinem Code nie behaupten, dass er vollkommen fehlerfrei ist. Sondern immer, dass er keine offensichtlichen Fehler mehr enthält. Alles Andere kann man nie mit Gewissheit sagen. Nur nahezu ausschließen. ;)
:) Hast ja recht, war nur so geschockt. ;) Hiermit ziehe ich also öffentlich die Fehlerfreiheit meines Codes wieder in den bezweifelbaren Bereich. 8) habe meinen Code nie als grundsätzlich Fehlerfrei hingestellt - hätte man aber vielleicht in meinem letzten Post so lesen können, OK

user profile iconLossy eX hat folgendes geschrieben:
Schippi: Nichts destro trotz teile ich Narsens Meinung. Mich würde mal interessieren was du mit dem Code vor hast?
Das würde mich allerdings auch interessieren. :gruebel:

cu
Narses

//EDIT: @user profile iconLossy eX: Danke für die Erläuterungen; hätte ich auch mal machen können, statt mich aufzuregen... :oops: Also, "Sorry" an user profile iconschippi :beer:


schippi - Di 23.10.07 15:21

Hallo Narses!
Mir ist schon klar, dass man damit vorsichtig umgehen muss und dass das nicht funktioniert..

Hab halt verschiedene Fehlerfälle durchgespielt und da war auch der Fehler mit System.move dabei. Dachte dass sich das trotzdem irgendwie abfangen lässt, wenn so was mal passieren sollte. Weiß halt nicht wie und habs deswegen hier gepostet.

Zitat:

Ich möchte hier sicherheitshalber einmal kurz klarstellen, dass das überhaupt nix mit den TNBFPA-Komponenten zu tun hat, wenn dein Code abschmiert!


Ich wollte nicht, dass das so rüberkommt, als wäre deine Komponente daran Schuld.
Ich hab deine Socketkomponente nur angesprochen, weil sich das Try-Except mit Einbinden der Komponente anders verhielt als ohne und ich wusste nicht warum. Da du ja in diesem Forum bist und es wahrscheinlich am besten weißt...

Ich hoffe, ich hab jetzt nochmal die Kurve gekriegt :wink:


Lossy eX - Di 23.10.07 15:36

Narsens: War auch nicht ganz ernst gemeint. ;)

Schippi: Okay. Fehlerfälle durchspielen ist durchaus ein Argument. Allerdings löst dein Code ja nicht direkt den Fehler aus sondern nur indirekt und damit nützt das Try Except in dem Falle gar nichts. Außer du würdest auf ungültigen Speicher zugreifen.

Aber einige Fehlerfälle dürfen auch einfach nicht passieren. Zum Beispiel wenn du an die Adresse deiner lokalen Variable schreibst. Zum Beispiel ein lokales statisches Array. Dann befinden sich diese Daten auf dem Stack und wenn du dort rüber schreibst kann es passieren, dass du dir die Daten des Try Except Blockes zerschießt was dann zur Folge hat dass Delphi nicht mehr weiß was es machen soll. Und schwupp ist deine gesammte Anwendung weg.

Besser ist es sich genau Gedanken zu machen was passieren könnte und darauf so gut wie möglich zu reagieren. Testen kann man leider sowieso nicht alles.


schippi - Di 23.10.07 18:08

Zitat:

Also, "Sorry" an schippi :beer:

Kein Problem! Hab halt auch blöd gefragt, dann bekommt man ja bekanntermaßen auch ne blöde Antwort :wink:

Also wenn jemand noch ein dümmerer Fehler einfällt als mir, dann her damit. Ich bring dann damit mein Programm zum Absturz und frag anschließend Narses, was mit seiner Komponente nicht stimmt :zwinker:

Viele Grüße