The Wiert Corner – irregular stream of stuff

Jeroen W. Pluimers on .NET, C#, Delphi, databases, and personal interests

  • My work

  • My badges

  • Twitter Updates

  • My Flickr Stream

    20140508-Delphi-2007--Project-Options--Cannot-Edit-Application-Title-HelpFile-Icon-Theming

    20140430-Fiddler-Filter-Actions-Button-Run-Filterset-now

    20140424-Windows-7-free-disk-space

    More Photos
  • Pages

  • All categories

  • Enter your email address to subscribe to this blog and receive notifications of new posts by email.

    Join 1,358 other followers

Delphi – getting the sourcefile name from a source file with an Assert trick using EAssertionFailed

Posted by jpluimers on 2009/08/21

For one of our projects, we have a set of configuration files that we want to be able to locate automatically.

(in this case they are XML files as most development environments have one way or the other to do some XML Data Binding that maps objects or interfaces to/from XML; in Delphi you can use the XML Data Binding Wizard for that: it has been there since Delphi 6, and CodeBeach has a nice Delphi XML data binding wizard video tuturoial on how to use that wizard)

We usually have a few instances of those config files hanging around:

  1. for testing; this one goes into the same directory as the Delphi source file generated by the XML Data Binding Wizard
  2. for the Delphi IDE (which might be relative to the Delphi IDE .exe)
  3. for the application (which usually is relative to the Appliation .EXE, or user settings directory)

If a specific version (higher numbers in the list above) does not exist, we want to revert to a more generic version (with lower numbre in the list above).
Ultimately we want to revert to the one for testing, which is in the subdirectory of a specific sourcefile.

Since all our development machines are configured in a similar way (i.e. having the same root path like C:\DEVELOP for our sources), it would be nice to be able to somehow automatically detect the file name of a source file.

There is a way in Delphi where you can get the name of a full source file: when an assertion failes, then Delphi raises an EAssertionFailed exception.
That exception contains the full path to the source file in which the assertion failed.

Note that this requires your application to be compiled with the $ASSERTIONS ON directive, which is the default, and also enabled for our production builds.

So this statement:

  Assert(False, 'no valid configuration file found relative to ' + Application.ExeName);

will result in a message like this:
no valid configuration file found relative to C:\Program Files\CodeGear\RAD Studio\6.0\bin\bds.exe (C:\develop\ActiveMQ\ActiveMQDemo\common\src\ConfigHelperUnit.pas, line 84)

Knowing this, we need a function like GetSourceFileName, preferably on the EAssertionFailed class that gets us the source filename that is inside the parentheses.

Having a function like that, would allow for code like this:

function GetConfigPathName(const Application: TApplication): string; overload;
var
  TriedFileNames: IStringListWrapper;
  SourceFileName: string;
begin
  TriedFileNames := TStringListWrapper.Create();
  Result := FindConfigPathName(Application, TriedFileNames);
  try
    AssertConfigFileNameExists(Result, Application.ExeName, TriedFileNames);
  except
    on E: EAssertionFailed do
    begin
      SourceFileName := E.GetSourceFileName();
      Result := FindConfigPathName(SourceFileName, TriedFileNames);
      AssertConfigFileNameExists(Result, Application.ExeName, SourceFileName, TriedFileNames);
    end;
  end;
end;

Since we want the function to be on EAssertionFailed, I have written a class helper like this:

unit AssertionFailedHelperUnit;

interface

uses
  SysUtils;

type
  TAssertionFailedHelper = class helper for EAssertionFailed
  public
    function GetSourceFilename: string;
  end;

implementation

uses
  SysConst;

function TAssertionFailedHelper.GetSourceFilename: string;
var
  Mask: string;
  ExceptionMessage: string;
  OpeningParenthesesPosition: Integer;
begin
  //no valid configuration file found relative to C:\Program Files\CodeGear\RAD Studio\6.0\bin\bds.exe (C:\develop\ActiveMQ\ActiveMQDemo\common\src\ConfigHelperUnit.pas, line 84)
  //  SAssertError = '%s (%s, line %d)';
  // now create this string " (, line 0)" and use it to parse from right to left
  Mask := Format(SAssertError, ['', '', 0]);
  ExceptionMessage := Self.Message;
  // remove the closing ")"
  if (ExceptionMessage <> '') then
  begin
    Delete(ExceptionMessage, Length(ExceptionMessage), 1);
    Delete(Mask, Length(Mask), 1);
    // remove the "0" from the Mask
    Delete(Mask, Length(Mask), 1);
  end;
  while (ExceptionMessage <> '') and (CharInSet(ExceptionMessage[Length(ExceptionMessage)], ['0'..'9'])) do
    Delete(ExceptionMessage, Length(ExceptionMessage), 1);
  // from the right side, remove the ", line " portion
  while (ExceptionMessage <> '') and (ExceptionMessage[Length(ExceptionMessage)] = Mask[Length(Mask)]) do
  begin
    Delete(ExceptionMessage, Length(ExceptionMessage), 1);
    Delete(Mask, Length(Mask), 1);
  end;
  // now find the opening "("
  OpeningParenthesesPosition := Length(ExceptionMessage);
  while (ExceptionMessage <> '') and (ExceptionMessage[OpeningParenthesesPosition] <> '(') do
    Dec(OpeningParenthesesPosition);
  Delete(ExceptionMessage, 1, OpeningParenthesesPosition);
  Result := ExceptionMessage;
end;

end.

This unit has made life for us a lot easier.
Hope it does it for you as well :-)

–jeroen

3 Responses to “Delphi – getting the sourcefile name from a source file with an Assert trick using EAssertionFailed”

  1. L-Tyrosine said

    Theres a much easier way, just set the AssertErrorProc global variable pointing to a procedure like that:

    AssertErrorProc := AssertErrorHandler

    AssertErrorHandler(const Message, Filename: string; LineNumber: Integer; ErrorAddr: Pointer);

    It alredy comes with unit filename, linenumber and error address. With a complete .map file in memory one can build code to even hint the current class/procedure name.

  2. [...] Delphi – getting the sourcefile name from a source file with an Assert trick using EAsser… [...]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
Follow

Get every new post delivered to your Inbox.

Join 1,358 other followers

%d bloggers like this: