Wednesday, May 6, 2009

class helper to get SourceFileName from an EAssertionFailed

In one of our applications, we have a default Config.xml file that is in our source tree, but it can be overridden by a Config.xml file relative to the application’s .EXE file.

So we have a two step process looking for the Config.xml, and we’d like to have the full path to the source file where the sources were build.

It seems the only way to get that, is by looking inside the Message of an EAssertionFailed exception.


So I wrote yet another class helper to get this code to work:




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;

Note the IStringListWrapper; I’ll get back to that in a later blog-post.


The full code of the class helper is right below.

Just a few remarks:



  1. EAssertionFailed is in the SysUtils unit, and builds its Message by formatting a string using the SAssertError const in the SysConst unit as a mask.

  2. We use that mask to “parse” the Message from right to left. Parsing from left to right is virtually impossible, as the first string is the assertion message itself, and we don’t know how that looks like.

  3. If you are not using Delphi 2009, then replace the CharInSet call with a regular set check.




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\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.

No comments: