11

I've created a console application and set ReportMemoryLeaksOnShutdown := True.

I've created a TStringList but did not free it.

When the program finishes executing, I see the memory leak for a brief second but then the console closes.

I've tried adding a ReadLn; to the end, but it only shows a blank console window when I do that, which makes sense.

I need to find a way to pause executing after the memory leak report, but before complete program shutdown.

I'm using Delphi 10 Seattle.

program Project1;

{$APPTYPE CONSOLE}

uses
  System.Classes,
  System.SysUtils;

var
  s : TStringList;

begin
  try
    ReportMemoryLeaksOnShutdown := True;
    s := TStringList.Create;

    //ReadLn doesn't work here, which makes sense.
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  //I need to be able to pause the program somewhere after the end statement here.
end.
FLDelphi
  • 508
  • 7
  • 20

5 Answers5

10

The easiest is to simply run the application in a previously opened command window.

If you insist on seeing the memory leak report while running in the IDE, do as follows:

  • Locate the ShowMessage procedure in GetMem.inc (line 4856 in Delphi 10 Seattle)
  • Place a breakpoint on the end; of that procedure.

Alternatively, as Sertac Akyuz commented, put a break point on the end. of the system unit.

You can also redirect the memory leak report to a file. Download the full version of FastMM from

https://sourceforge.net/projects/fastmm/

or better, thanks to Arioch 'The, from here:

https://github.com/pleriche/FastMM4

and set the needed options in FastMM4Options.inc

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
Tom Brunberg
  • 20,312
  • 8
  • 37
  • 54
8
var
  SaveExitProcessProc: procedure;
  s: TStringList;

procedure MyExitProcessProc;
begin
  ExitProcessProc := SaveExitProcessProc;
{$I-}
  ReadLn;
{$I+}
end;

begin
  SaveExitProcessProc := ExitProcessProc;
  ExitProcessProc := MyExitProcessProc;
  ReportMemoryLeaksOnShutdown := True;
  s := TStringList.Create;
end.
Atys
  • 109
  • 6
  • 1
    Errors are probably due trying to acquire memory for readln after the manager is finalized. On windows can be replaced with a messagebox. – Sertac Akyuz Sep 19 '16 at 22:13
  • @SertacAkyuz Win32 messagebox has a problem: you cannot copy a text from it to clipboard. IF that actually is needed - I'd re-install a stub manager, routing all Delphi calls to Win32 LocalAlloc and friends – Arioch 'The Sep 21 '16 at 13:28
  • 1
    @Arioch - It's in place of the readln, to stop execution - there's nothing to copy. Secondly you're wrong, press ctrl+c on a standard api message box and the caption and the text and the buttons' text will be copied to the clipboard. – Sertac Akyuz Sep 21 '16 at 13:32
  • @SertacAkyuz then perhaps it was added in later Windows. For quite long this copying was specific to VCL messagebox – Arioch 'The Sep 21 '16 at 22:44
  • 1
    @Arioch - I just tested and can confirm that it works on XP. I'm fairly certain that it worked on 2K too, but I don't have a 2K VM any more. – Sertac Akyuz Sep 21 '16 at 23:02
  • No more errors, with saving and restoring the exitprocessproc. – Atys Mar 02 '18 at 22:34
  • 1
    @Arioch'The actually, that's the other way around. Windows was first with copy behaviour for message boxes (in one of the Windows 2000 betas). Then a suggestion was done in RAID (which predated QC) to add the copy behaviour for the VCL because testers got tired of adding screenshots, then typing the text to be able to search threads for it. – Jeroen Wiert Pluimers Jul 24 '19 at 11:13
  • Nice idea, on windows 7 home edition, the ReadLn does get executed and it waits for the enter key to be pressed, but after the enter key is pressed the console application crashes with Runtime Error 103. If you can live with a runtime error at the end, then this is somewhat usefull, thank you for the idea at least ! ;) I prefer no runtime errors though at the end of a program run, otherwise I might believe that my own code my be at fault, causing this runtime error, so that is not any good ! ;) – oOo Jan 13 '23 at 12:46
  • @oOo just add {$I-}, I've updated the example – Atys Feb 14 '23 at 20:39
6

That is a bug in recent Delphi versions. I just checked it in that recent free Delphi 10.1 Starter and it behaves as you describe - but as it provides no RTL sources I can not check the exact reason.

In Delphi XE2 it behaves as expected: creates the task-modal dialog and waits for you to react, just like described by Sertak.

In Delphi 10.1 the leak is indeed reported to the console window, but the program is not stopped to wait for user attention. That is poor solution, for both this reason and for the possible use of console programs in scripting (CMD or PS scripts would not "understand" this message and might confuse it with legitimate output and fail execution of further stages programs.

I think you have to open regression-type bug report over Delphi 10.0 - but I do not think they would fix it until 10.2 release.

I also switched your application from Delphi-forked memory manager to the original one, and then the erroneous behavior was reverted: the program displayed the message box and waited until I dismiss it before exiting into IDE.

Currently i suggest you to use the mentioned original memory manager rather than Delphi fork of it.

program Project1;

{$APPTYPE CONSOLE}

uses
  FastMM4,
  System.Classes,
  System.SysUtils;
...

The original memory manager resides at http://github.com/pleriche/FastMM4 You can use Git client in your Delphi or a standalone one to keep yourself updated, or you can download the code once and stop updating, up to you.

The relevant quotes of its code are:

  {$ifdef LogErrorsToFile}
     {Set the message footer}
      LMsgPtr := AppendStringToBuffer(LeakMessageFooter, LMsgPtr, Length(LeakMessageFooter));
      {Append the message to the memory errors file}
      AppendEventLog(@LLeakMessage[0], UIntPtr(LMsgPtr) - UIntPtr(@LLeakMessage[1]));
  {$else}
      {Set the message footer}
      AppendStringToBuffer(LeakMessageFooter, LMsgPtr, Length(LeakMessageFooter));
  {$endif}
  {$ifdef UseOutputDebugString}
      OutputDebugStringA(LLeakMessage);
  {$endif}
  {$ifndef NoMessageBoxes}
      {Show the message}
      AppendStringToModuleName(LeakMessageTitle, LMessageTitleBuffer);
      ShowMessageBox(LLeakMessage, LMessageTitleBuffer);
  {$endif}
    end;
  end;
{$endif}
end;

and

{Shows a message box if the program is not showing one already.}
procedure ShowMessageBox(AText, ACaption: PAnsiChar);
begin
  if (not ShowingMessageBox) and (not SuppressMessageBoxes) then
  begin
    ShowingMessageBox := True;
    MessageBoxA(0, AText, ACaption,
      MB_OK or MB_ICONERROR or MB_TASKMODAL or MB_DEFAULT_DESKTOP_ONLY);
    ShowingMessageBox := False;
  end;
end;

This code depends upon being run on desktop Windows, so maybe Embarcadero tried to "fix" it to make it cross-platform. However the way they did it broken it on Windows console....

Also consider using adding other forms of logging - into the file and/or into the Windows Debug Strings. They would not be so attention-catching as the modal window, but would at least help you save the information, if you would know where to look for it.

Arioch 'The
  • 15,799
  • 35
  • 62
  • This is not really an answer, it does not pause the console program as desired. This is more a comment on a bug in Delphi. – oOo Jan 13 '23 at 12:59
  • @oOo well, perhaps. But SO does not provide any means to add resourcefuylk comments other than submitting them as non-accepted answers. It is this way or highway oh SO. – Arioch 'The Jan 14 '23 at 19:56
6

This is certainly a hack, don't use in production :)

ReportMemoryLeaksOnShutdown:= True;
IsConsole:= False;
TStringList.Create;

However, it causes the leak message (and some other messages) to be displayed in a message box (where all text can be copied by pressing Ctrl+C).

(Tested with Delphi 10.2, please report any side effects we wouldn't like)

maf-soft
  • 2,335
  • 3
  • 26
  • 49
  • 1
    That is a perfect solution for the question asked! Just add `IsConsole := false;` and a message box would be displayed with the memory leak information. – Xalo Apr 11 '21 at 19:16
  • 1
    This actually works on Delphi 10.3 and Windows 7 Home Edition, thank you ! =D – oOo Jan 13 '23 at 12:47
  • It pauses the console program/application most like during the clean up of units/finalization section, so it might depend on the order of the exit routines and such, most likely user units will be cleaned up first, but if exit procedures are overwritten very maybe if a memory leak occurs inside of those it might not catch it anymore, then again maybe it will, not sure if memory manager is still active/online in exit procedures, to many combinations to test for now I leave it at this. – oOo Jan 13 '23 at 13:14
0

Set a breakpoint on "end." in system.pas.

However this solution is not completely ideal, because exit procedures/unit finalizations will still execute after this "end." statement.

This can be "checked" by F7/debugging/stepping into the "end." statement, it will lead to some assembler function and once the assembler function is exited by stepping over assembler instructions with F8 it will return to a function called "FinalizeUnits" in system.pas, where this function recursively calls itself to clean up the finalize sections of units I suppose.

So as long as you don't have to pause after the cleaning up of the finalization sections of units, this solution is not so bad. However, cleaning up of units/finalization sections follows a certain order, it's likely that your own units's finalization section will be executed, before the memory manager is shutdown in the "end." statement.

Otherwise a different solution will have to be used.

To get into system.pas add it temporarely to a uses clausule or so, and choose open file, later removed it to prevent compile errors like: "[dcc32 Error] TestProgram.dpr(8): E2004 Identifier redeclared: 'System'"

oOo
  • 261
  • 2
  • 16
  • 1
    yeah, except that most of us do not use debug version of system.dcu and this also won't work outside the IDE – Atys Feb 14 '23 at 20:07