Delphi: How to get (current code line, current uni

2019-04-08 17:28发布

问题:

I am trying to create a log system on my program that will log debugging messages on text files, and I want to save the exact place in the code where the log message called, but I don't want to use Assert function because it creates exceptions and this system is not for logging exceptions only, also I have to write some debugging info.

example usning assert:

procedure AnyProcedure();
begin
  try
    Assert(1=0);
  except
    on E: Exception do
      Log.AddLine('Log occurred is '+E.Message+' : Start');//Log occurred is "c:\progr~..jkdj.pas" at line [29]
  end;

  //....some code
  try
    Assert(1=0);
  except
    on E: Exception do
      Log.AddLine('Log occurred is '+E.Message+' : Step1 done');//Log occurred is "c:\progr~..jkdj.pas" at line [37]
  end;

  //....some code
  try
    Assert(1=0);
  except
    on E: Exception do
      Log.AddLine('Log occurred is '+E.Message+' : Step2 done');//Log occurred is "c:\progr~..jkdj.pas" at line [45]
  end;

  //....some code
  try
    Assert(1=0);
  except
    on E: Exception do
      Log.AddLine('Log occurred is '+E.Message+' : Step3 done');//Log occurred is  "c:\progr~..jkdj.pas" at line [53]
  end;

  //....some code
  try
    Assert(1=0);
  except
    on E: Exception do
      Log.AddLine('Log '+E.Message+' : End');//Log occurred is "c:\progr~..jkdj.pas" at line [61]
  end;
end;

this works fine the only thing that it raises an exception and the code become too big, so I can't use a function -see next example function LogMessage- and call it in another place because the line will be the same always also the file name will be where the LogMessage function implemented:

not working example:

procedure LogMessage(AMessage: String);
var AFile, ALine: String;
begin
  try
    Assert(1=0);             //line 29
  except
    on E: Exception do
    begin
      AFile:= Copy(E.Message, Pos(' (', E.Message)+2, Pos(', line ', E.Message)-Pos(' (', E.Message)-2);
      ALine:= Copy(E.Message, Pos(', line ', E.Message)+7, Pos(')', E.Message)-Pos(', line ', E.Message)-7);
      ShowMessage('Log occurred in file "'+AFile+'" at line ['+ALine+'] : '+AMessage);
    end;
  end;
end;

procedure AnyProcedure();
begin
  LogMessage('Start'); //Log occurred in file "c:\progr~....jkashdj.pas" at line [29]
//....
  LogMessage('step1'); //Log occurred in file "c:\progr~....jkashdj.pas" at line [29]
//....
  LogMessage('step2'); //Log occurred in file "c:\progr~....jkashdj.pas" at line [29]
//....
  LogMessage('step3'); //Log occurred in file "c:\progr~....jkashdj.pas" at line [29]
//....
  LogMessage('end');
end

please help, and thanks in advance.

回答1:

You can bind your own TAssertErrorProc procedure to the AssertErrorProc variable. You might write something like this:

procedure OnAssert(const Message, Filename: string; LineNumber: Integer;
  ErrorAddr: Pointer);
begin
  ShowMessage(Format('Assert in file "%s" at line %d.', [Filename, LineNumber]));
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  AssertErrorProc := OnAssert;
end;


回答2:

The easiest way to map from the instruction pointer to unit name and line number is to use one of the various debugging libraries: madExcept, EurekaLog, JclDebug etc.

These tools all rely on the detailed map file that is produced by the linker. Although these libraries are best known for producing bug reports from unexpected exceptions, they have all the functionality that you need.