Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi aktuelle Quellcodezeile im Programm ermitteln (https://www.delphipraxis.net/181608-aktuelle-quellcodezeile-im-programm-ermitteln.html)

jus 28. Aug 2014 17:13

aktuelle Quellcodezeile im Programm ermitteln
 
Ich bin gerade dabei meine Logfileroutine aufzupeppen. Ich habe mal in einem Logfile von einem anderen Programm folgendes gesehen und finde es extrem cool:
Code:
[2014-07-30 23:06:18.875] Programm Error - F:\relbranch-2_0\Adapter.cpp:309 - running failed with exception: System exception
Sprich da wird die aktuelle Codezeilennummer wie in diesem Fall Zeile "309" mit in das Programm einkompiliert. Kann man sowas in Delphi auch machen?

Mir geht es nur darum, dass ich im Logfile und im Ouputdebugstring punktuell die Quellzeile automatisch ermitteln kann, und nicht jedesmal mühsam den Funktionsnamen als Marker reinschreiben muß.

Lg,
jus

DeddyH 28. Aug 2014 17:15

AW: aktuelle Quellcodezeile im Programm ermitteln
 
Schau Dir mal Assertions an.

jus 28. Aug 2014 18:13

AW: aktuelle Quellcodezeile im Programm ermitteln
 
Zitat:

Zitat von DeddyH (Beitrag 1270191)
Schau Dir mal Assertions an.

Vielen Dank DeddyH! :thumb:

So wie ich es verstanden habe, kommt bei Assert immer eine Messagebox. Damit ich die Ausgabe in einer Logdatei verwenden kann, habe ich folgende Quick&Dirty Lösung zum Umleiten der Meldung verwendet, für den Fall, dass jemand so eine Lösung benötigt:
Delphi-Quellcode:
  procedure AssertErrorHandler(const Message, Filename: string; LineNumber: Integer; ErrorAddr: Pointer);

implementation

procedure DoMessage(Message, Filename: String; LineNumber: Integer; ErrorAddr: Pointer);
var
  S: String;
begin
  S := Format('%s (%s, line %d, address $%x)',
    [Message, Filename, LineNumber, Pred(Integer(ErrorAddr))]);
  WriteToLogFile(s);// <----- ist nur exemplarisch gemeint ;-)
  //OutputDebugString(PChar(S));
end;

procedure AssertErrorHandler(const Message, Filename: string; LineNumber: Integer; ErrorAddr: Pointer);
{ No local variables. Not compiler generated temporary variables. }
{ Using the call stack here will cause Access Violation errors. }
begin
  DoMessage(Message, Filename, LineNumber, ErrorAddr);
//  raise EMyAssert.Create('Boom!');
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  System.AssertErrorProc := @AssertErrorHandler;
end;
Ich habe den obigen von "AssertErrorProc (Delphi)" genommen und ein wenig gekürzt.

Lg,
jus

jaenicke 28. Aug 2014 19:22

AW: aktuelle Quellcodezeile im Programm ermitteln
 
Uns zumindest würde die reine Zeilennummer nicht besonders helfen, wenn es um Fehler geht. Daher speichern wir dabei normalerweise den kompletten Stacktrace bei einem Fehler.

MaBuSE 28. Aug 2014 19:59

AW: aktuelle Quellcodezeile im Programm ermitteln
 
Zitat:

Zitat von jus (Beitrag 1270190)
Sprich da wird die aktuelle Codezeilennummer wie in diesem Fall Zeile "309" mit in das Programm einkompiliert. Kann man sowas in Delphi auch machen?

Ja das geht. Das wird aber nicht "reinkompiliert" sondern zur Laufzeit ermittelt.

Die JCL (JEDI Component Library) kann das zum Beispiel.

Ein neues leeres VCL Projekt erstellen, einen TButton drauf, deppelklicken und folgenden Quelltext darauf legen:

Delphi-Quellcode:
...
procedure TForm1.Button1Click(Sender: TObject);
begin
  StrToInt('Zwei');
end;
...
Es kommt dann z.B. folgendes im Logfile an:
Code:
Exception class: EConvertError
Exception message: ''Zwei'' ist kein gültiger Integer-Wert.
Exception address: 0041EFB6
------------------------------------------------------------------------------
Stack list, generated 28.08.2014 20:51:12
[0041EFB1]{Project1.exe} SysUtils.ConvertErrorFmt
[0041FEF4]{Project1.exe} SysUtils.StrToInt
[0087B2FD]{Project1.exe} Unit1.TForm1.Button1Click (Line 31, "Unit1.pas")
[00497C6B]{Project1.exe} Controls.TControl.Click
[004CD2FA]{Project1.exe} StdCtrls.TCustomButton.Click
[004CDDE8]{Project1.exe} StdCtrls.TCustomButton.CNCommand
[00497700]{Project1.exe} Controls.TControl.WndProc
[0049BFC4]{Project1.exe} Controls.TWinControl.WndProc
[004CCFC4]{Project1.exe} StdCtrls.TButtonControl.WndProc
[00497324]{Project1.exe} Controls.TControl.Perform
[0049C117]{Project1.exe} Controls.DoControlMsg
[0049CB73]{Project1.exe} Controls.TWinControl.WMCommand
[0050F428]{Project1.exe} Forms.TCustomForm.WMCommand
[00497700]{Project1.exe} Controls.TControl.WndProc
[0049C0C4]{Project1.exe} Controls.TWinControl.DefaultHandler
[00497700]{Project1.exe} Controls.TControl.WndProc
[0049BFC4]{Project1.exe} Controls.TWinControl.WndProc
[0050C32C]{Project1.exe} Forms.TCustomForm.WndProc
[00405DBC]{Project1.exe} System.TMonitor.Enter
[00405C90]{Project1.exe} System.TMonitor.CheckOwningThread
[00405F66]{Project1.exe} System.TMonitor.Exit
[0049B664]{Project1.exe} Controls.TWinControl.MainWndProc
[0045792C]{Project1.exe} Classes.StdWndProc
[0049C0C4]{Project1.exe} Controls.TWinControl.DefaultHandler
[004980BC]{Project1.exe} Controls.TControl.WMLButtonUp
[00497700]{Project1.exe} Controls.TControl.WndProc
[0049BFC4]{Project1.exe} Controls.TWinControl.WndProc
[0049BBF3]{Project1.exe} Controls.TWinControl.WndProc
[004061BE]{Project1.exe} System.TMonitor.TryEnter
[00405DBC]{Project1.exe} System.TMonitor.Enter
[00405C90]{Project1.exe} System.TMonitor.CheckOwningThread
[00405F66]{Project1.exe} System.TMonitor.Exit
[00405F9F]{Project1.exe} System.TMonitor.Exit
[0047D543]{Project1.exe} Graphics.FreeMemoryContexts
[0049B664]{Project1.exe} Controls.TWinControl.MainWndProc
[0049B679]{Project1.exe} Controls.TWinControl.MainWndProc
[0049B857]{Project1.exe} Controls.TWinControl.IsControlMouseMsg
[0045792C]{Project1.exe} Classes.StdWndProc
[0049BFC4]{Project1.exe} Controls.TWinControl.WndProc
[00612599]{Project1.exe} dxHooks.TdxSystemHook.ProcessHookProcs
[004CCFC4]{Project1.exe} StdCtrls.TButtonControl.WndProc
[0049B664]{Project1.exe} Controls.TWinControl.MainWndProc
[0045792C]{Project1.exe} Classes.StdWndProc
[005152FB]{Project1.exe} Forms.TApplication.ProcessMessage
[0051533E]{Project1.exe} Forms.TApplication.HandleMessage
[00515669]{Project1.exe} Forms.TApplication.Run
[00889959]{Project1.exe} Project1.Project1 (Line 13, "")
------------------------------------------------------------------------------
Active Controls hierarchy:
TButton "Button1"
TForm1 "Form1"
Wir loggen noch viel viel mehr Informationen zu jedem Fehler.
Aber das sollte Deinen Ansprüchen erst mal genügen.

Das tolle ist, es ist kostenlos.

JCL installieren, Beispiel kompilieren und sehen wie es funktioniert. Dann einfach die für einen relevanten Teile in eine eigene Funktion extrahieren und fertig.

Ach ja, wenn man "Debug DCUs" in dem Projekt Optionen aktiviert, gibt es zu jeder Zeile im Stack den Dateinamen und Zeilennummer :stupid:

Ich hoffe das hilft Dir.
MaBuSE

[edit]
Anmerkung:
Wenn Du es nicht selbst programmieren möchtest, schau Dir mal madExcept an. (http://www.madexcept.com/madExceptDescription.htm)
Den Dialog kannst du auch abschalten und entsprechend die Infos im Code verwenden.
[/edit]

himitsu 28. Aug 2014 21:45

AW: aktuelle Quellcodezeile im Programm ermitteln
 
Zitat:

Die JCL (JEDI Component Library) kann das zum Beispiel.
Eurekalog und Co. haben auch solche Funktionen, welche man selber verwenden kann.

Für Delphi 7 gab es noch einen PreCompiler, welchen ein bekannter Typ (Andy) da reingehackt hatte, der sowas wie __LINE__ im Delphi zur Verfügung stellte. :cry:

jus 28. Aug 2014 22:15

AW: aktuelle Quellcodezeile im Programm ermitteln
 
Zitat:

Zitat von MaBuSE (Beitrag 1270198)
Die JCL (JEDI Component Library) kann das zum Beispiel.

@MaBuSE: Vielen vielen Dank für den Tipp! :thumb:
Da ich sowieso JCL im Einsatz habe, habe mich entschieden doch JCL zu verwenden und bin somit davon abgegangen den "System.AssertErrorProc" zu manipulieren, da vermutlich andere Asserts/Exceptions von anderen Units damit auch abgefangen werden.

Nach einigen Experimenten funktioniert folgender Code für mein Vorhaben zufriedenstellend:
Delphi-Quellcode:
implementation

uses JclDebug;

procedure LogMsg(Msg:String);
var
  s: String;
begin
  s := Format('%s:%u (%s) "%s"',[FileByLevel(1), LineByLevel(1), ProcByLevel(1), Msg]);
  WriteToLogFile(s); // <----- ist nur exemplarisch gemeint ;-)
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  LogMsg('Fehlermeldung: Blablabla...');
end;
Achja, für Leute die eingehend sich mit den Möglichkeiten von JCL Debug beschäftigen möchten, weitere Beispiele findet man im JCL Installationsunterverzeichnis unter ".\examples\windows\debug". Der obige Code basiert in Prinzip auf JCL Beispiel ".\examples\windows\debug\sourceloc\SourceLocExamp le.dpr".

Lg,
jus

MaBuSE 28. Aug 2014 22:37

AW: aktuelle Quellcodezeile im Programm ermitteln
 
Zitat:

Zitat von jus (Beitrag 1270210)
Nach einigen Experimenten funktioniert folgender Code für mein Vorhaben zufriedenstellend

Wenn ich nur den Stack haben will mache ich folgendes:

Delphi-Quellcode:
...
// Form mit 1 x TButton + 1 x TMemo
implementation

uses
  jclDebug;

{$R *.dfm}

function GetStackList(
  IncludeModuleName: Boolean = False;
  IncludeAddressOffset: Boolean = False;
  IncludeStartProcLineOffset: Boolean = False;
  IncludeVAddress: Boolean = False): string;
var
  sl: TStringList;
begin
  sl := TStringList.Create;
  try
    with TJclStackInfoList.Create(True, 0, nil) do
    try
      Delete(0); // TJclStackInfoList
      Delete(0); // TJclStackInfoList
      Delete(0); // GetStackList
      AddToStrings(sl, IncludeModuleName, IncludeAddressOffset, IncludeStartProcLineOffset, IncludeVAddress);
    finally
      Free;
    end;
    Result := sl.Text;
  finally
    sl.Free;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Memo1.Lines.Add(GetStackList);
end;

end.
Da kommt dann der Stack wie folgt raus:
Code:
[004DF643] Unit1.TForm1.Button1Click (Line 57, "Unit1.pas")
[00470E2B] Controls.TControl.Click (Line 7190, "Controls.pas")
[0049BD52] StdCtrls.TCustomButton.Click (Line 4562, "StdCtrls.pas")
[0049C840] StdCtrls.TCustomButton.CNCommand (Line 5023, "StdCtrls.pas")
[004708C0] Controls.TControl.WndProc (Line 7074, "Controls.pas")
[00475184] Controls.TWinControl.WndProc (Line 9831, "Controls.pas")
...

MaBuSE 28. Aug 2014 22:41

AW: aktuelle Quellcodezeile im Programm ermitteln
 
Zitat:

Zitat von MaBuSE (Beitrag 1270211)
Wenn ich nur den Stack haben will mache ich folgendes

Oft macht es Sinn zu wissen von wem bzw. wo meine Funktion aufgerufen wurde.

[edit]dummerweise Zitat statt Bearbeiten gedrückt. sorry[/edit]

jus 29. Aug 2014 15:44

AW: aktuelle Quellcodezeile im Programm ermitteln
 
Zitat:

Zitat von jaenicke (Beitrag 1270197)
Uns zumindest würde die reine Zeilennummer nicht besonders helfen, wenn es um Fehler geht. Daher speichern wir dabei normalerweise den kompletten Stacktrace bei einem Fehler.

Zitat:

Zitat von MaBuSE (Beitrag 1270212)
Oft macht es Sinn zu wissen von wem bzw. wo meine Funktion aufgerufen wurde.

@jaenicke und @MaBuSE: Vielen Dank für die Hinweise! Wieder mal was dazugelernt. :-D


Alle Zeitangaben in WEZ +1. Es ist jetzt 14:51 Uhr.
Seite 1 von 2  1 2      

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