delphi 7, FastMM4 cannot install work around

3.3k views Asked by At

i am working on an application that uses FastMM4, from sourceforge.net. So i have added the FastMM4.pas to the uses clause right at the beginning. In the application i need to run a batch file after FinalizeMemoryManager; in the finalization of unit FastMM4; like this

  initialization
        RunInitializationCode;

  finalization
        {$ifndef PatchBCBTerminate}
        FinalizeMemoryManager;
        RunTheBatFileAtTheEnd; //my code here..calling a procedure
       {$endif}

 end.

then my code for RunTheBatFileAtTheEnd is :

 procedure RunTheBatFileAtTheEnd;
 begin
 //some code here....
  sFilePaTh:=SysUtils.ExtractFilePath(applicaTname)+sFileNameNoextension+'_log.nam';
 ShellExecute(applcatiOnHAndle,'open', pchar(sExeName),pchar(sFilePaTh), nil, SW_SHOWNORMAL) ;
 end;

For this i need to use SysUtils,shellapi in the uses clause of fastmm4 unit. But using them this message comes enter image description here

But if i remove SysUtils,shellapi from the uses it works. I still need all the features of fastmm4 installed but with SysUtils,shellapi, fastmm4 is not installed

I have a unit of my own but its finalization is executed before fastmm4 finalization.

can anyone tell me can how to fix this problem?

EDIT- 1

unit FastMM4;
//...
...
implementation

 uses
   {$ifndef Linux}Windows,{$ifdef FullDebugMode}{$ifdef Delphi4or5}ShlObj,{$else}
  SHFolder,{$endif}{$endif}{$else}Libc,{$endif}FastMM4Messages,SysUtils,shellapi;

my application

 program memCheckTest;
   uses
      FastMM4,

EDIT-2 :

(after @SertacAkyuz answer),i removed SysUtils and it worked , but i still need to run the batch file to open an external application through RunTheBatFileAtTheEnd. The Reason is ..i want a external application to run only after FastMM4 as been out of the finalization. The sExeName is the application that will run the file sFilePaTh(.nam) . can any one tell how to do this? without uninstalling FastMM4.

2

There are 2 answers

6
Sertac Akyuz On BEST ANSWER

FastMM checks to see if the default memory manager is set before installing its own by a call to IsMemoryManagerSet function in 'system.pas'. If the default memory manager is set, it declines setting its own memory manager and displays the message shown in the question.

The instruction in that message about 'fastmm4.pas' should be the first unit in the project's .dpr file has the assumption that 'fastmm4.pas' itself is not modified.

When you modify the uses clause of 'fastmm4.pas', if any of the units that's included in the uses clause has an initialization section, than that section of code have to run before the initialization section of 'fastmm4.pas'. If that code requires allocating/feeing memory via RTL, then the default memory manager is set.

Hence you have to take care changing 'fastmm4.pas' to not to include any such unit in the uses clause, like 'sysutils.pas'.


Below sample code (no error checking, file checking etc..) shows how can you launch FastMM's log file with Notepad (provided the log file exists) without allocating any memory:

var
  CmdLine: array [0..300] of Char; // increase as needed
  Len: Integer;
  SInfo: TStartupInfo;
  PInfo: TProcessInformation;

initialization
  ...  // fastmm code

finalization
{$ifndef PatchBCBTerminate}
  FinalizeMemoryManager;       // belongs to fastmm

  // Our application is named 'Notepad' and the path is defined in AppPaths
  CmdLine := 'Notepad "';  // 9 Chars (note the opening quote)
  Len := windows.GetModuleFileName(0, PChar(@CmdLine[9]), 260) + 8;

  // assumes the executable has an extension.
  while CmdLine[Len] <> '.' do
    Dec(Len);

  CmdLine[Len] := #0;
  lstrcat(CmdLine, '_MemoryManager_EventLog.txt"'#0); // note the closing quote

  ZeroMemory(@SInfo, SizeOf(SInfo));
  SInfo.cb := SizeOf(SInfo);
  CreateProcess(nil, CmdLine, nil, nil, False,
                NORMAL_PRIORITY_CLASS, nil, nil, sInfo, pInfo);

{$endif}

end.
8
Jerry Dodge On

I agree with Sertac's answer, but also would like to give a recommendation, if you insist on using SysUtils.pas. The answer is don't use it, and extract what you need out of it and put it in your own copy. Here's what you would need below - ExtractFilePath used LastDeliminator, which used StrScan, and also 2 constants, so I copied them into this new unit and named it MySysUtils.pas.

This is also widely used for people who don't want to have a bunch of extra code compiled which they will never use (You would have to be absolutely sure it's not used anywhere in any units though).

unit MySysUtils;

interface

const
  PathDelim = '\';
  DriveDelim = ':';

implementation

function StrScan(const Str: PWideChar; Chr: WideChar): PWideChar;
begin
  Result := Str;
  while Result^ <> #0 do begin
    if Result^ = Chr then
      Exit;
    Inc(Result);
  end;
  if Chr <> #0 then
    Result := nil;
end;

function LastDelimiter(const Delimiters, S: string): Integer;
var
  P: PChar;
begin
  Result := Length(S);
  P := PChar(Delimiters);
  while Result > 0 do begin
    if (S[Result] <> #0) and (StrScan(P, S[Result]) <> nil) then
      Exit;
    Dec(Result);
  end;
end;

function ExtractFilePath(const FileName: string): string;
var
  I: Integer;
begin
  I := LastDelimiter(PathDelim + DriveDelim, FileName);
  Result := Copy(FileName, 1, I);
end;

end.