2

I assume that it is possible to get the complete path of the unit (not just the name) at runtime, since when I generate an error, Delphi already has this stored information:

try
  Assert (False, '#');
except
  on E: EAssertionFailed from
  begin
    ShowMessage (E.Message); // this show me the path
  end;
end;

Would anyone know if there is any function that returns me the path of a specific unit, or something similar?

  • 2
    That is only working with `Assert`, because the compiler adds the Filename and LineNumber to the Assert call. `procedure _Assert(const Message, Filename: string; LineNumber: Integer);` – Uwe Raabe Feb 20 '18 at 15:50
  • See that too, but is it possible to create my own call and make the compiler do the same? – Edgar Pavão Feb 20 '18 at 15:54
  • Uwe is right there. Actually the boolean is evaluated inline and the code is basically compiled to `if not AssertCondition then _Assert(, Filename, LineNumber);`, where `_Assert` just throws the exception (check System unit). There is no way to tell the compiler to do this kind of magic for your own function, but you could compile with a map file that lets you figure out the unit as well as the complete stack trace in run time. The JCL has tools and classes for this, as explained [in this answer](https://stackoverflow.com/questions/2326980//2337111#2337111) – GolezTrol Feb 20 '18 at 16:00
  • When I referred to if "it was possible to do the same", I ended up being ambiguous, sorry. I would like to create my own method to identify the unit, and not perform the Assert call. Assert is just one example in this case. But the rest, you emphasized well. The question was precisely this. I did not want to have to access the .map file, but everything points to that. Apparently there is this feature (I think), not even anything related to RTTI. Thank you! – Edgar Pavão Feb 20 '18 at 16:03
  • @StefanGlienke So why did you reopen this? – David Heffernan Feb 20 '18 at 16:52
  • Because in the comment before yours it was stated that it is not only about Assert but in the one you marked it was. – Stefan Glienke Feb 21 '18 at 08:39
  • @StefanGlienke No, the [question that I marked this as a duplicate of](https://stackoverflow.com/questions/16459608/) asks how to obtain the information without using the `Assert` hack. – David Heffernan Feb 21 '18 at 09:32

4 Answers4

3

The complete path of the unit as it was on the machine that compiled the project is only possible using Assert. However personally I don't find that information incredibly useful unless you have many units with the same name in different folders or lost control over your source repository and and library paths in effect when compiling.

To get the unit name you can turn on map file or debug information (aka TD32) and do the following:

Use the FileByLevel function from JclDebug - in your case with the default value (0).

The level parameter tells the function how many calls it look up the callstack. If you put that method into a FormCreate of a VCL form for example and pass 1 it will give you Vcl.Forms.pas as the event handler was called from there.

Stefan Glienke
  • 20,860
  • 2
  • 48
  • 102
2

I hesitate to write this answer as it shows a really dirty hack to get the unit name making use of the Assert compiler magic shown above.

Use the following unit:

unit UnitNameHack;

interface

const
  cUnitNameSentinel = '$$$sentinel$$$';

var
  HackUnitname: string = '';

implementation

var
  OrgAssertErrorProc: TAssertErrorProc = nil;

procedure MyAssertErrorProc(const Message, Filename: string; LineNumber: Integer; ErrorAddr: Pointer);
begin
  if Message = cUnitNameSentinel then begin
    HackUnitname:= Filename;
  end
  else begin
    OrgAssertErrorProc(Message, Filename, LineNumber, ErrorAddr);
  end;
end;

initialization
  OrgAssertErrorProc := AssertErrorProc;
  AssertErrorProc := MyAssertErrorProc;
finalization
  AssertErrorProc := OrgAssertErrorProc;
end.

Now whenever you need the unit name call

Assert(False, cUnitNameSentinel);

and retrieve the unit name from HackUnitname.

Note that you cannot wrap the Assert call and reading HackUnitName into a function, not even if inlined.

Uwe Raabe
  • 45,288
  • 3
  • 82
  • 130
  • Now, that's an interesting hack. Actually, couldn't that be executed once in the initialization section of each unit so the HackUnitName would be filled on unit start up? Yes, it's ugly (no, actually it's not, I somehow like it ;-) ), but it would work quite well. – dummzeuch Feb 21 '18 at 08:24
  • Interesting. Thank you for your attention. – Edgar Pavão Feb 21 '18 at 10:54
2

You've said, something similar. I see that it might have been in a different context, but anyway, for objects you can inspect UnitName or UnitScope to get the name of the module where the object instance was declared, without the module path (which is not valuable information anyway).

Victoria
  • 7,822
  • 2
  • 21
  • 44
2

Based on Uwe Raabe's answer:

// directly after the implementation uses:
var
  ThisUnit: string = '<unknown>';

procedure MyAssertErrorProc(const Message, Filename: string; LineNumber: Integer; ErrorAddr: Pointer);
begin
  ThisUnit := Filename;
end;

procedure InitThisUnit;
var
  OrgAssertErrorProc: TAssertErrorProc;
begin
  OrgAssertErrorProc := AssertErrorProc;
  try
    AssertErrorProc := MyAssertErrorProc;
    Assert(False);
  finally
    AssertErrorProc := OrgAssertErrorProc;
  end;
end;

// [...]

// call InitThisUnit in the initialization section
initialization
  InitThisUnit;
end.

It's still a hack, but a bit more elegant. ;-)

dummzeuch
  • 10,975
  • 4
  • 51
  • 158
  • When you place the ThisUnit variable in the interface part, you can get the unit name from everywhere by inspecting `.ThisUnit`. – Uwe Raabe Feb 21 '18 at 09:30
  • @UweRaabe Yes, but that would defeat the purpose: If I have to type .ThisUnit, I could just simply use ''. – dummzeuch Feb 21 '18 at 11:08
  • No, that won't give you the path of the unit. In addition, when you rename the unit, you have to take care to adjust the constant. With `.ThisUnit` you will get a syntax error. – Uwe Raabe Feb 21 '18 at 12:34
  • I'm not sure that getting a syntax error qualifies for an advantage. ;-) But yes, you are right: The advantage would be getting the whole path. – dummzeuch Feb 21 '18 at 13:46