Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Die Delphi-IDE (https://www.delphipraxis.net/62-die-delphi-ide/)
-   -   Zur Laufzeit, Zeilennummer feststellen (https://www.delphipraxis.net/196005-zur-laufzeit-zeilennummer-feststellen.html)

Eppos 13. Apr 2018 17:04

Zur Laufzeit, Zeilennummer feststellen
 
Hallo zusammen,

gibt es die Möglichkeit zur Laufzeit die Zeilennummer im Code auszugeben.

Beispiel:
101
102
103 ShowMessage( {Zeilennummer} );
104

Fenster: 103

Gruß
Eppos

Der schöne Günther 13. Apr 2018 19:17

AW: Zur Laufzeit, Zeilennummer feststellen
 
Ohne großen Aufwand kenne ich nichts außer Assert(..)

Delphi-Quellcode:
procedure p(const Message, Filename: string; LineNumber: Integer; ErrorAddr: Pointer);
begin
   ShowMessage( LineNumber.ToString() );
end;

procedure TForm19.Button1Click(Sender: TObject);
begin
   System.AssertErrorProc := p;
   assert(false, 'message');
end;

Kannst du ein praktisches Beispiel geben wofür du das brauchst? Geht sicher besser.

Eppos 16. Apr 2018 08:35

AW: Zur Laufzeit, Zeilennummer feststellen
 
Ich würde gerne ausgeben, an welcher Stelle ein Fehler auftritt.

haentschman 16. Apr 2018 08:43

AW: Zur Laufzeit, Zeilennummer feststellen
 
Moin...:P
http://www.madshi.net/

http://help.madshi.net/madExceptDemo.htm

Eppos 16. Apr 2018 08:57

AW: Zur Laufzeit, Zeilennummer feststellen
 
Das deckt leider nicht mein internes Fehlerhandling ab. 8-)

KodeZwerg 16. Apr 2018 09:13

AW: Zur Laufzeit, Zeilennummer feststellen
 
Fehler Behandlungs Beispiel:
Delphi-Quellcode:
 var
   number1, number0 : Integer;
 begin
   try
     number0 := 0;
     number1 := 1;
     number1 := number1 div number0;
     ShowMessage('1 / 0 = '+IntToStr(number1));
   except
     on E : Exception do
     begin
       ShowMessage('Exception ClassName = '+E.ClassName); // hier gibt's den ClassName des Verursachers
       ShowMessage('Exception Nachricht = '+E.Message); // und seine Fehlermeldung
     end;
   end;
 end;

Man kann auch auf verschieden Fehler in On Events reagieren, Beispiel:
Delphi-Quellcode:
  except
     On E : EInOutError do
       ShowMessage('IO Fehler : '+E.Message);
     On E : EDivByZero do
       ShowMessage('Division durch NULL : '+E.Message);
     else
       ShowMessage('andere Fehler');
   end;
Für Zeilennummern solltest Du Dich mit
Delphi-Quellcode:
on e: exception do begin showmessage(e.StackTrace); end;
und GetStackInfoStringProc beschäftigen.


edit
Link1 und Link2 wo ich diese Informationen her habe.

p80286 16. Apr 2018 09:48

AW: Zur Laufzeit, Zeilennummer feststellen
 
Könntest Du Dich etwas ausführlicher äußern?
Eine Exception zeigt im allg. auch die Position im Compilat an, an der es knallt. Mit Hilfe des Debuggers kommt man dann zu der fehlerhaften Sourcecodestelle.

Gruß
K-H

haentschman 16. Apr 2018 10:31

AW: Zur Laufzeit, Zeilennummer feststellen
 
Zitat:

Das deckt leider nicht mein internes Fehlerhandling ab.
...:gruebel: verstehe ich nicht. Was hat der madExcept nicht?

Eppos 16. Apr 2018 11:29

AW: Zur Laufzeit, Zeilennummer feststellen
 
Danke für die Vielzahl von Antworten.

Wir haben ein eigenes Fehlerhandling programmiert. Die Vorgabe war, keine externen Hilfsmittel zuzulassen (madExcept).
Fehler zu melden, die nicht systemseitig erzeugt werden, sondern durch andere Fälle entstehen werden bei uns gesammelt und ausgewertet.

himitsu 16. Apr 2018 12:23

AW: Zur Laufzeit, Zeilennummer feststellen
 
Im Compilat gibt es keine Zeilennummern mehr.

Bei Assert wird vom Compiler an der Stelle die Zeilennummer einkompilert,
aber sowas wie _FILE_ und _LINE_ ala PHP oder anderer Scriptsprachen gibt es leider nicht.

Alle anderen Varianten nehmen die Debuginfos, bzw. die MAP-Datei, welche der Compiler/Linker erstellen kann, und lesen/suchen daraus die naheliegenste Zeile zur entsprechenden Codeposition.
Wenn keine Fremdkomponenten genommen werden sollen, damm müsst ihr eben das alles selber machen.

Eurekalog und vermutlich auch madExcept nehmen die MAP-Datei, holen sich diese Infos da raus und hängen sie in einem eigenen Format als Ressource an das Compilat an.

Delphi.Narium 16. Apr 2018 12:56

AW: Zur Laufzeit, Zeilennummer feststellen
 
Programm mit ausführlicher MAP-Datei kompilieren.

Fehleradresse in dieser suchen.

Vor der Fehleradresse steht die Zeilennummer.

Die erste Zeile mit einer Quelltextangabe (Line numbers for ...), vor der gefundenen Fehleradresse, gibt die Quelltextdatei an, in welcher der Fehler auftrat.

Damit hat man dann die Quelltextdatei, Fehleradresse und die Zeilennummer des fehlerverursachenden Quelltextes. Die Fehlermeldung entnimmt man direkt der Exception.

Das sollte mit wenig Aufwand in 'nem halben Arbeitstag umzusetzen sein. Und wenn man schon ein eigenes Fehlerhandling hat, kann man das entsprechend um eine Methode erweitern, die die Fehleradresse annimmt, die MAP-Datei entsprechend durchsucht und als Ergebnis den Namen der Quelltextdatei, zuzüglich der Zeilennummer, liefert.

Es sind prinzipiell alle Informationen bereits vorhanden, man muss sie nur nutzen.

günni0 16. Apr 2018 13:13

AW: Zur Laufzeit, Zeilennummer feststellen
 
Wenn ihr JCL nutzt, kann das hier helfen.
Das Projekt wird im Debug-Modus etwa 100 KB größer. Im Release bei mir 140 KB. Also nicht die Welt.


https://stackoverflow.com/questions/...w-an-exception

KodeZwerg 16. Apr 2018 20:14

AW: Zur Laufzeit, Zeilennummer feststellen
 
Aber Zeilennummern aus einer Objekt orientierten Programmiersprache heraus zu holen ist doch eh ein falscher Ansatz.
Beispiel Procedure X ruft Function Y auf die wiederum einen externen Call Z macht, welche Zeilennummer soll angezeigt werden bei Fehler?
Das was in X falsch war oder in Y oder gar Z ??? Ich hoffe das ich meine Sicht verständlich darlege.
Besser bei Klassennamen und deren Message bleiben, da sollte dann sofort klar sein wo & warum Fehler YXZ entstand.
Alternativ kannst Du Dir ja einen Globalen Counter reinbauen und dem bei jeder Neuen Code Zeile dessen Zeilennummer voranstellen, noch primitiver gehts kaum aber Du bist am Ziel.
Beispiel
Delphi-Quellcode:
0 - var Zeile : Cardinal;
1 - procedure x;
2 - begin
3 -  Zeile := 3; MachWas;
4 -  Zeile := 4; MachMehr;
5 - end;

FaTaLGuiLLoTiNe 17. Apr 2018 10:13

AW: Zur Laufzeit, Zeilennummer feststellen
 
Wirklich aussagekräftig für den Entwickler ist eigentlich nur die Kombination aus Fehlerklasse, Fehlermeldung und Callstack. Das wäre schon mal eine gute Momentaufnahme der Situation, die man auch im Debugger sehen könnte, wenn der Fehler dort auftreten würde.

Das einzige was dann noch fehlt, wäre der Zustand des Programms zum Fehlerzeitpunkt (also die Variableninhalte), aber das würde wohl eindeutig den Rahmen jedweder Fehlermeldung oder Logdatei sprengen.

jus 17. Apr 2018 15:01

AW: Zur Laufzeit, Zeilennummer feststellen
 
Hallo,

ich verwende zum Ermitteln der Zeilennummer die JCL, da ich in meinem Programmen häufig Fehler in eine Logdatei reinschreibe, der coole Tipp kam von diesem Forum hier. Der Vorteil ist, dass JCL eigentlich nicht wirklich was kostet.
Meine Logfunktion schaut in etwa so aus:
Delphi-Quellcode:
implementation
uses JCLDebug;

procedure Log(S: String);
var
  msg: String;
  Year, Month, Day, Hour, Min, Sec, MSec, H: Word;
begin
  DecodeTime(now, Hour, Min, Sec, MSec);
  msg := Format('%.2d:%.2d:%.2d.%.3d %s:%u (%s) "%s"',[Hour,Min,Sec,MSec,FileByLevel(1), LineByLevel(1), ProcByLevel(1), S]);

  // hier schreibe ich die Fehlermeldung in eine Datei oder lasse es im Programm ausgeben
  //......
end;
Meine Logdatei schaut dann so aus, wobei nach dem Unitnamen die Zeilennummer ist.
Code:
23:03:53.262 Unit1.pas:2787 (TForm1.Update_CmB_Printer) "Fehler! Der Druckername "TM88IV" konnte nicht unter den installierten Druckern gefunden werden."
23:03:53.264 Unit1.pas:2794 (TForm1.Update_CmB_Printer) "Der aktive Druckername wird auf "TMU290" gesetzt."
23:03:53.266 Unit1.pas:536 (TForm1.FillPrinterDataToGui) "Current printer model "TM88IV" match with "C:\ProgramData\XXX\XXX\PrinterInfo.xml"
23:03:53.268 PrinterLib.pas:1104 (PrinterXmlGetStandardFeedsources) "In C:\ProgramData\XXX\XXX\PrinterInfo.xml <feedsource name="01"> node no <borderless> found."
23:03:53.270 Unit1.pas:550 (TForm1.FillPrinterDataToGui) "C:\Users\abc\AppData\Roaming\XXX\MediaNotVisible found"
23:03:53.272 Unit1.pas:1172 (TForm1.LoadPresets) "---TForm1.LoadPresets---"
23:03:53.273 Unit1.pas:1173 (TForm1.LoadPresets) "TForm1.LoadPresets: Suche alle Presets in C:\Users\abc\AppData\Roaming\XXX\PRESET\*.epl mit dem Druckermodel TM88IV"
23:03:53.282 Unit1.pas:1231 (TForm1.LoadPresets) "INVALID: C:\Users\abc\AppData\Roaming\XXX\PRESET\14_20150730_190038.epl"
Die JCL Debugfunktionenn können natürlich viel mehr wie z.B. Stacktrace ausgeben usw. siehe den alten Beitrag hier.

Lg,
jus

günni0 17. Apr 2018 15:01

AW: Zur Laufzeit, Zeilennummer feststellen
 
Zitat:

Der Vorteil ist, dass JCL eigentlich nicht wirklich was kostet.
Bis auf einen kleinen Hinweis im Programm, dass es JCL verwendet, sollte es nichts kosten :P

Könntest du eventuell ProcByLevel erklären?

Wenn Prozedur A einen Fehler hat und Prozedur B_Log aufruft und letztere wieder ProcByLevel(0), dann habe ich im Log den Namen B_log stehen.
Heißt ProcByLevel(1) nur "gehe eine Prozedur weiter zurück? Wenn ja, was, wenn ich X Prozeduren habe die sich hintereinander aufrufen?

himitsu 17. Apr 2018 15:11

AW: Zur Laufzeit, Zeilennummer feststellen
 
Zitat:

Heißt ProcByLevel(1) nur "gehe eine Prozedur weiter zurück?
Ja, aber du solltest das Programm mit Stack-Frames kompileren, denn sonst erkennt der Stacktrace kleinere Funktionen garnicht.

günni0 17. Apr 2018 15:12

AW: Zur Laufzeit, Zeilennummer feststellen
 
Hat das irgendwelche Nachteile bezüglich der Sicherheit des fertigen Kompilats?

FileByLevel, LineByLevel, ProcByLevel. Was aber wenn ich nicht wie weit ich zurück muss? Ist mir mir gerade nicht so, aber für die Zukunft wäre das interessant.

Hier nur ein Beispiel.
In meiner Mainform ist eine Prozedur AppExceptionHandler.
Die ruft eine Prozedur doLog in einer Log-Unit auf und doLog ruft letzten Endes die Prozedur auf, die das Log schreibt.
Gehe ich 3x zurück, lande ich bei Vcl.Forms.pas.

himitsu 17. Apr 2018 15:23

AW: Zur Laufzeit, Zeilennummer feststellen
 
Zitat:

Zitat von günni0 (Beitrag 1399572)
Was aber wenn ich nicht wie weit ich zurück muss?

Sowas hab ich grade in Postgres bei einer Funktion eingebaut, im Grunde mit einer Blacklist.

Die Funktion wird auch von Anderen aufgerufen, wobei Einige dafür auch nur interne Loggingfunktionen sind.
Ich gehe den Stacktrace nun einfach so lange rückwärts, bis ich einen "mir unbekannten" Funktionsnamen finde und dieser kommt ins Log.

günni0 17. Apr 2018 15:34

AW: Zur Laufzeit, Zeilennummer feststellen
 
Gerade mal mit einer Schleife und ProcByLevel nachgebaut.


Zitat:

TLogFunctions.doExceptionLog
Form.Main.TForm1.AppExceptionHandler
Forms.TApplication.HandleException
Classes.StdWndProc$qqsp6HWND__uiuiui
FileByLevel dementsprechend
Zitat:

uLog.pas
<projektname>.Form.Main.pas
Vcl.Forms.pas
System.Classes.pas
Danach kommt nix mehr. Darf man bei sowas keinen eigenen HandleException haben?

himitsu 17. Apr 2018 16:46

AW: Zur Laufzeit, Zeilennummer feststellen
 
Application.HandleException ist da, wo es bei dir schon raus ist.

Delphi-Quellcode:
function HandleMessage;
try
  CallWindowsEvents; // hier hat es in einer unterfunktion geknallt
except
  HandleException; // aber hier ist dein HandleException-Event drin, also ist ein Stacktrace von HIER aus sinnlos
end
System.ExceptObject ... das entspricht dem on E: Exception im EXCEPT-Block
System.ExceptAddr ... hier hatte es geknallt
System.ReturnAddress ... mein Caller (bzw. der Befehl danach)

Schau dir mal die Exception-Klasse genau an. Da kann man sich für die Stacktrace-Behandlung registrieren.
Also während die Exception ausgelöst wird ... da kannst du den Stacktrace auslesen und innerhalb des aktuellen Exception-Objektes speichern. (Delphi bietet nur die Zugangspunkte, aber ohne eigene Funktion)
Und in der anschließenden Exceptionbehandlung kannst du über das Exception-Objekt an dessen Stacktrace wieder rankommen.

günni0 17. Apr 2018 17:06

AW: Zur Laufzeit, Zeilennummer feststellen
 
Wie loggt ihr denn dann Fehler wenn nicht mit einer Exception-Prozedur? Ich kann schlecht überall try-except hinschreiben.

himitsu 17. Apr 2018 17:23

AW: Zur Laufzeit, Zeilennummer feststellen
 
Mit Try-Except und alles was wir vergessen haben über TApplicationEvents.OnException (nicht direkt an Application)

günni0 17. Apr 2018 17:40

AW: Zur Laufzeit, Zeilennummer feststellen
 
Also doch try-except. Ich dachten immer man soll sauber funktionierenden Code schreiben und try-except sei nur eine Notlösung.

Zitat:

über TApplicationEvents.OnException (nicht direkt an Application)
Den Teil verstehe ich nicht. Dann hat man ja aber wieder keinen Zugriff auf genaue Fehlerquellen.

himitsu 17. Apr 2018 18:26

AW: Zur Laufzeit, Zeilennummer feststellen
 
Hier haben wir ein in Jahrzehnten gewachsenes Programm aus mehreren EXEn, vielen DLLs und massig BPLs.
Das wurde von Turbo Pascal (DOS) über Delphi 7 (Windows), aktuell Delphi XE und parallel neu für 10.x .
533 vorwiegend eigene Units, ~60 Projekte (EXE/DLL), ~30 Packages und vielen Fremdkomponenten.
Wo mehrere Leute drin rumpfuschen .... mal ehrlich, erwartest du wirklich, dass es da alles reibungslos funktioniert?

Neben Eurekalog sind da noch ein paar eigene Exceptionbehandlungen mit Erweitern der Exception-Message, da Eurekalog auch nicht immer den Stacktrace ausreichend aufbauen kann.

Nathan 18. Apr 2018 08:57

AW: Zur Laufzeit, Zeilennummer feststellen
 
Hatten dies auch schon, das in unseren Exe beim Kunden AVs wie "Access violation at address 005CDF76 in module 'DummyProject. exe'. Read address 00000000." aufgetreten. Hat dann immer jeder gesagt, weis nicht wo der Fehler liegt. Bin dann hin gegangen und lies die Exe mit MAP Files kompilieren. Passend zur Exe Version wurde die entsprechende MAP Datei gesichert. Verteilt an den Kunden wurde nur die Exe.

Hat jetzt ein Kunde eine AV, kann ich mit einem selbst geschriebenen Programm, Zeilennummer der Exception filtern.

Das Programm hab ich unter Github https://github.com/Thurnreiter/Thurnreiter.MapFile veröffentlicht.

Ein Aufruf von "NathanMapFile.exe -MapFilename:.\DummyProject.map -CrashAddress:$005CDF76" ergibt dann:

Offset: 6
Codeline: 34
Startaddress from Module: $001CCCA0
Name of procedure from address: Unit1.TForm1.Button1Click
Sourcename from address: Unit1.pas

Voraussetzung ist, zur Exe eine passende MAP Datei. Es ist dann auch keine IDE nötig. Vielleicht hilft dir das Programm was.

KodeZwerg 18. Apr 2018 12:46

AW: Zur Laufzeit, Zeilennummer feststellen
 
Hallo Nathan, verstehe ich das richtig, ich binde in meinem Projekt keinen Code von Dir ein sondern kompiliere es mit .MAP Datei Erzeugung, Kunde bekommt .exe, ich behalte .MAP.
Nun verwende ich Dein Tool wie Du es beschrieben hast und fertig?
Falls das der Fall ist, könntest Du eventuell auch ein binary bereitstellen?
JCL wollte ich nicht installieren um dein Tool kompilieren zu können um JCL danach gleich wieder los zu werden.

Nathan 18. Apr 2018 13:55

AW: Zur Laufzeit, Zeilennummer feststellen
 
Funktioniert genau wie Du sagst. Musst kein Code von mir einbinden. Nur deine Anwendung mit MAP Files erzeugen. Die MAP Files liefere ich nicht aus, da sonst die Update zu groß werden. Halten aber für jeder Exe Version welche veröffentlicht wird, das entsprechende MAP File vor. Tritt dann beim Kunden eine unerwartete AV auf, kann ich meistens den Fehler genauer einschränken und muss nicht hoffen, das der Fehler reproduzierbar ist.
Ist halt leider nur eine Konsolenanwendung.

Bindary für Win32 liegen auf GutHub unter https://github.com/Thurnreiter/Thurn...ster/Bin/Win32

KodeZwerg 18. Apr 2018 14:14

AW: Zur Laufzeit, Zeilennummer feststellen
 
Vielen Dankeschön für prompte Antwort Nathan!
Ja da hätte ich auch darauf kommen können mal ins \bin\ zu gehen, tut mir leid dafür. Ich hab nur "clone or download" beim ersten mal geklickert.
Ich habe es gerade mit einer TestApp die bewusst Fehler erzeugt ausprobiert. Resultat = It works!
Ich bin begeistert und teste Garantiert noch diese oder jene Fehler aus um zu schauen was dein Tool darüber sagen kann.
Das würde mich von MadExcept befreien, was ja auch ein ganz schöner Brocken ist.
Mein Empfinden nach Ersten Test = Ich finde es Klasse! Console oder GUI ist mir in diesem Fall mehr als egal.

CCRDude 18. Apr 2018 14:31

AW: Zur Laufzeit, Zeilennummer feststellen
 
Nur die .map-Files zu verwenden kann manchmal helfen, aber was genau hilft es Dir, wenn Fehleradresse + .map-File Dir dann sagen, dass der Fehler in StrToInt etwa liegt? Doch nur, dass irgendwo im gesamten Programm ein String nicht ordentlich in eine Zahl umgewandelt werden kann.

Deshalb wurde weiter oben ja schon mehrfach erwähnt, dass erst Callstacks wirklich helfen - nur so kommst Du auch an den Kontext!

Und den callstack ordentlich herauszufinden ist halt eine Herausforderung, weswegen madExcept, JclDebug, EurekaLog etc. erprobte Erleichterungen sind.

günni0 18. Apr 2018 14:39

AW: Zur Laufzeit, Zeilennummer feststellen
 
Zitat:

weswegen madExcept, JclDebug, EurekaLog etc. erprobte Erleichterungen sind.
JclDebug aber nur wenn man keinen eigenen ExceptionHandler nutzt.

CCRDude 18. Apr 2018 14:50

AW: Zur Laufzeit, Zeilennummer feststellen
 
Ich habe zugegeben wenig Erfahrungen mit JclDebug, warum sollt man JclCreateStackList nicht in eigenem Handler verwenden können?

Bin großer madExcept-Fan noch aus aktiven Delphi-Zeiten, unter Lazarus bin ich schon verwöhnt, weil GetStackTrace schon drin ist.

KodeZwerg 18. Apr 2018 14:58

AW: Zur Laufzeit, Zeilennummer feststellen
 
Ich schrieb ja auch nicht das es DIE Universal Lösung für jedermann ist, nach vorab Test = für meine Zwecke passt's.

freimatz 18. Apr 2018 15:25

AW: Zur Laufzeit, Zeilennummer feststellen
 
Ich kenne beide, finde aber madExcept deutlich besser.

günni0 18. Apr 2018 15:34

AW: Zur Laufzeit, Zeilennummer feststellen
 
Kann man madExcept kostenlos verwenden, wenn man keinen Gewinn mit dem eigenen Programm erwirtschaftet?
Kenne madExcept jetzt nicht. Aber bei Google sehe ich viele Bilder mit so einem madExcept Fenster wo dutzende Informationen zum Fehler stehen. Kann man sich den Fehler nicht als String beschaffen und in ein eigenes Log schreiben?

freimatz 18. Apr 2018 15:40

AW: Zur Laufzeit, Zeilennummer feststellen
 
kostenlos? Schon gegoogelt? ;-)
Statt einem Fenster kann man das sicher auch in ein Log schreiben. M.W. macht das EurekaLog auch so.

CCRDude 18. Apr 2018 15:46

AW: Zur Laufzeit, Zeilennummer feststellen
 
madExcept
Zitat:

free for non-commercial usage, inexpensive for commercial usage
madExcept bietet da einen Haufen interaktiver und automatischer Optionen an. Speichern in Datei, in die Zwischenablage, verschicken per Email, posten an einen Webserver. Automatisch, oder als Optionen im Fehlerdialog.


Alle Zeitangaben in WEZ +1. Es ist jetzt 08:59 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz