Ich habe das Problem auf eine sehr spezielle Art gelöst. Es bleibt dabei, bei meinen über hundert Units wird FastMM vom Compiler nicht an der frühen Stelle initialisiert, sondern erst nachdem andere Units schon Speicher vom alten MemoryManager angefordert haben. FastMM4 tritt daher so seinen Dienst nicht an. Die Reihenfolge wird vom Compiler festgelegt, so dass man zunächst keine Möglichkeit hat einzugreifen. Aber es geht doch
Ich habe nämlich 2 Änderungen gemacht:
- Ich habe die INITILAIZATION in FastMM4.pas mit ASM NOP NOP NOP NOP END; als Kennung anfangen lassen.
- Ich habe die system.pas geändert und neu kompiliert. Bei InitUnits wird jetzt zuerst die FastMM4 gesucht und früher initialisiert als vom Compiler vorgegeben.
Zu 1.: Die FastMM4.pas sieht am Ende jetzt also so aus:
Delphi-Quellcode:
initialization
asm
NOP
NOP
NOP
NOP
end;
RunInitializationCode;
finalization
{$ifndef PatchBCBTerminate}
FinalizeMemoryManager;
{$endif}
end.
Die 4 NOPs ergeben 4 x $90 also ein
DWORD $90909090 im Code. Daran kann man diese
Unit in der Liste erkennen.
Zu 2.: Der Compiler legt im Quellcode eine Liste an, die aus je 2 Pointern auf Prozeduren für jede
Unit besteht: Init für die Initialisierung und FInit für die Finalisierung. Bevor beim Programmstart nun alle Units nacheinander in InitUnits in system.pas initialisiert werden, suche ich zuerst nach meinem Eintrag mit $90909090. Die Nummer in der Liste merke ich mir dann in der Variablen
iFastMM4. Beim Initialisieren wird die Liste vorwärts abgearbeitet, beim Finalisieren rückwärts. Wenn die 5.
Unit dran wäre, initialisiere ich dann immer erst die FastMM4. Wenn die FastMM4 regulär dran wäre ignoriere ich sie. Beim Finalisieren, wie gesagt, genau anders herum:
Delphi-Quellcode:
VAR iFastMM4:LongInt=-1;
{Falls die Unit FastMM4 früher behandelt werden muss}
procedure FinalizeUnits;
var
i,Count: Integer;
Table: PUnitEntryTable;
P: Pointer;
begin
if InitContext.InitTable =
nil then
exit;
Count := InitContext.InitCount;
Table := InitContext.InitTable^.UnitInfo;
try
FOR i:=Count-1
DOWNTO 0
DO BEGIN
InitContext.InitCount:=i;
IF i=iFastMM4
THEN Continue;
P:=Table^[i].FInit;
IF Assigned(P)
THEN TProc(P)();
IF (i=5)
AND (iFastMM4>-1)
THEN BEGIN
InitContext.InitCount:=iFastMM4+1;
TProc(Table^[iFastMM4].FInit)()
{erst die 5, dann FastMM4}
END;
END;
except
FinalizeUnits;
{ try to finalize the others }
raise;
end;
end;
const
errCaption:
array[0..5]
of Char = '
Error'#0;
{***********************************************************}
procedure InitUnits;
var
Count,i: Integer;
Table: PUnitEntryTable;
P,P1: Pointer;
begin
if InitContext.InitTable =
nil then
exit;
Count := InitContext.InitTable^.UnitCount;
Table := InitContext.InitTable^.UnitInfo;
{$IFDEF LINUX}
Inc(Cardinal(Table), InitContext.Module^.GOT);
{$ENDIF}
try
FOR i:=0
TO Count-1
DO BEGIN {Unit FastMM4 suchen}
P:=Table^[i].Init;
IF Assigned(P)
THEN BEGIN
P1:=P;
Inc(DWord(P1),9);
IF DWord(P1^)=DWord($90909090)
THEN BEGIN {Initialisierung der Unit FastMM4 gefunden! (muss dort mit ASM NOP NOP NOP NOP END anfangen! NOP=$90)}
IF I<=7
THEN Break;
iFastMM4:=I;
Break;
END;
END;
END;
FOR i:=0
TO Count-1
DO BEGIN
InitContext.InitCount:=i+1;
IF i=iFastMM4
THEN Continue;
IF (i=5)
AND (iFastMM4>-1)
THEN BEGIN
InitContext.InitCount:=iFastMM4+1;
TProc(Table^[iFastMM4].Init)()
{erst FastMM4, dann die 5}
END;
P:=Table^[i].Init;
IF Assigned(P)
THEN TProc(P)();
END;
except
FinalizeUnits;
raise;
end;
end;
Die system.pas muss man manuell im CMD-Fenster mit dem Kommandozeilencompiler
DCC32 kompilieren:
"C:\Program Files (X86)\Borland\Delphi7\Bin\DCC32.EXE" -$D+ -Y SYSTEM
und ohne Debug-Info:
"C:\Program Files (X86)\Borland\Delphi7\Bin\DCC32.EXE" -$D- -Y SYSTEM
Die geänderte system.pas kommt zurück nach "C:\Program Files (x86)\Borland\Delphi7\Source\
Rtl\Sys", die entstandene system.dcu mit Debug-Info nach "C:\Program Files (x86)\Borland\Delphi7\Lib\Debug" und die ohne nach "C:\Program Files (x86)\Borland\Delphi7\Lib". Ggf. muss man zum Kopieren
Admin-Rechte haben. Es ist nicht verkehrt, vorher alles zu sichern!
Wenn man in system.pas debuggen will, muss man in den Projekt-Optionen bei Compiler die Option
[ ] mit Debug-DCUs aktivieren.
Das ist wirklich eine harte Nuss gewesen! Bestimmt hätte es auch eine einfachere Lösung gegeben, habe ich aber nicht gefunden. Es ist mir unbegreiflich, warum die FastMM.pas so spät initialisiert wird, obwohl sie oben im *.dpr als erstes stand. Egal -
danke für Eure Hilfe.