Moin Moin,
im Zuge einer Fehlersuche nach langjährig versteckten Fehlern (z.B. globale Variable nach Objektfreigabe nicht genilt),
wollte ich endlich mal ein Logging einbauen.
Vor allem wenn ein Designtime-
Package compiliert wird und daher alle Abhängigen ent-/geladen werden, da knallt es ab und an gern Mal.
Leider erst später in den Tiefen der delphi-eigenen
IDE-Packages, wo man mit dem Stacktrace nichts mehr anfangen kann.
Auch ist es unmöglich die Adresse des "defekten" Objektzeigers zu bekommen, um den mit vorher bekannten Objekten vergleichen zu können, weil z.B. beim Free/Destroy/FreeMem die Parameter in den Registern liegen und daher schon lange überschrieben wurden, nachdem/sobald es knallte.
Mein Problem ist nun, wie/wo ich das Logging, am Besten "einheitlich", in alle unsere Packages/DLLs einfügen kann.
Bei EXE und
DLL gibt es den Entry-Point von Windows (BEGIN in der
DPR, was der main-Funktion vom C++ entspricht)
und man kann sich bei der ExitProc registrieren, für das Unload.
Eine BPL ist eigentlich auch nur DLL, mit paar Extras, aber dort fehlt die Main-Function in der DPK.
Der erste einfache Gedanke war eine kleine
Unit, welche als Erstes im CONTAINS steht,
aber deren Code wird niemals ausgeführt.
Selbst wenn ich diese
Unit auch noch ins USES von anderen Units einbinde, welche später definitiv geladen/genutzt werden.
Delphi-Quellcode:
unit SysDB_Init;
interface
implementation
{$IFDEF WINDOWS}
uses
Winapi.Windows;
(*type
IOTACustomMessage = interface(IUnknown)
end;
IOTAMessageServices40 = interface(IUnknown)
['{26EB0E4E-F97B-11D1-AB27-00C04FB16FB3}']
procedure AddCustomMessage(const CustomMsg: IOTACustomMessage);
procedure AddTitleMessage(const MessageStr: string);
procedure AddToolMessage(const FileName, MessageStr, PrefixStr: string; LineNumber, ColumnNumber: Integer);
procedure ClearAllMessages;
procedure ClearCompilerMessages;
procedure ClearSearchMessages;
procedure ClearToolMessages;
end;*)
initialization
//OutputDebugString('LOAD SysDB');
MessageBox(0, '
LOAD SysDB', '
', 0);
{var H := GetModuleHandle(PChar(Format('designide%d.bpl', 'IDEVersion'));
if A <> 0 then begin
var P := GetProcAddress('@Toolsapi@BorlandIDEServices');
if P <> nil then begin
I := P^;
if I <> nil then
//(I as IOTAMessageServices40).AddTitleMessage('Message');
(I as IOTAMessageServices40).AddToolMessage('Filename', 'Message', 'Prefix', 11, 22);
end;
end;}
finalization
//OutputDebugString('UNLOAD SysDB'); // im Programm
MessageBox(0, '
UNLOAD SysDB', '
', 0);
// in IDE (eigentlich via IOTAMessageServices40.AddToolMessage)
{$ENDIF}
end.
(Nicht wundern, das ist ein Runtime-Package, welches aber auch in der IDE als Abhängigkeit geladen wird ... aber im eigentlichen Programm darf natürlich die DesignIde-BPL nicht geladen werden ... drum ist die auch nicht hart verlinkt und ich hole mir die Logfunktion für's Compiler-Log manuell raus)
Wie ist es nun möglich, dass ich beim Load und Unload dieses/unserer Package(s) mein Logging immer "sicher" durchführen kann,
sowie z.B. beim Create/Destroy der der enthaltenen DatenModule. (Letzteres geht einfach)
Automatisch geladenen (via REQUIRED verlinkt) werden nicht alle Units initialisiert.
Beim manuellen Laden via LoadPackage wird die exportierte Funktion "finalize" ausgeführt, in welcher scheinbar alle Units initialisiert werden. (aber das machen wird nicht)