Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   AddModuleUnloadProc und DEC (https://www.delphipraxis.net/208507-addmoduleunloadproc-und-dec.html)

TurboMagic 7. Aug 2021 15:07

AddModuleUnloadProc und DEC
 
Hallo,

in der DEC (die ich ja "geerbt" habe) ist folgender Code drin:

Delphi-Quellcode:
Unit DECHashBase;

[..]

procedure ModuleUnload(Instance: NativeInt);
var // automaticaly deregistration/releasing
  i: Integer;
begin
  if TDECHash.ClassList <> nil then
  begin
    for i := TDECHash.ClassList.Count - 1 downto 0 do
    begin
      if NativeInt(FindClassHInstance(TClass(TDECHash.ClassList[i]))) = Instance then
        TDECHash.ClassList.Remove(TDECFormat.ClassList[i].Identity);
    end;
  end;
end;

initialization
  AddModuleUnloadProc(ModuleUnload);

  TDECHash.ClassList := TDECClassList.Create;

finalization
  RemoveModuleUnloadProc(ModuleUnload);

  TDECHash.ClassList.Free;
end.
Der registriert eine Prozedor ModuleUnload, die irgendwie beim Entladen eines Packages
aufgerufen wird, soweit ich die F1 Hilfe für AddModuleUnloadProc verstehe.

In der ModuleUnload werden alle im globalen Klassenregistrierungsmechanismus der DEC
(da kann man über eine ID den richtigen Algorithmus finden und eine Instanz davon erzeugen,
sofern der Algorithmus in dieser TDECHash.ClassList registriert ist.

Nun macht dieser ModuleUnload Code Ärger mit dem C++ Builder, der hat mit der Variable i
da drin scheinbar irgend ein Problem. Die Frage für mich ist aber: wozu brauche ich den
Code in ModuleUnload überhaupt? Reicht es nicht aus, dass ich diese Liste in finalization
freigebe?

Wenn diese Fragestellung geklärt ist, dürfte es auch zeitnah ein neues Rerlease 6.3 geben.

Grüße
TurboMagic

shebang 7. Aug 2021 23:54

AW: AddModuleUnloadProc und DEC
 
Bei deinem Problem kann ich dir nicht direkt helfen, mir ist nur aufgefallen, dass im Argument der Funktion TDECFormat statt TDECHash verwendet wird. Ist das wirklich korrekt?

Delphi-Quellcode:
TDECHash.ClassList.Remove(TDECFormat.ClassList[i].Identity);

TurboMagic 8. Aug 2021 10:15

AW: AddModuleUnloadProc und DEC
 
Hallo,

danke für die Meldung! Das ist natürlich falsch und wurde eben korrigiert,
löst aber leider trotzdem meine eigentliche Problemstellung nicht.

Grüße

TurboMagic

jaenicke 8. Aug 2021 14:13

AW: AddModuleUnloadProc und DEC
 
Zitat:

Zitat von TurboMagic (Beitrag 1493381)
Nun macht dieser ModuleUnload Code Ärger mit dem C++ Builder, der hat mit der Variable i da drin scheinbar irgend ein Problem. Die Frage für mich ist aber: wozu brauche ich den Code in ModuleUnload überhaupt? Reicht es nicht aus, dass ich diese Liste in finalization freigebe?

Die Frage ist wie sich dieser Ärger äußert. Vielleicht lässt sich das ja korrigieren.

Ja, es macht einen sehr großen Unterschied, ob du den Code vor dem Entladen des Moduls ausführst oder danach in finalization. In der Unload-Prozedur ist das Package noch nicht entladen, so dass es keine Probleme mit aus dem Hostprogramm in die Liste geschriebenen Klassen geben wird. Das finalization passiert aber erst danach (wo genau weiß ich aus dem Kopf bei Packages nicht), so dass es sein kann, dass es zu Fehlern bei der Freigabe dieser Objekte gibt.

So ähnlich ist auch der Grund weshalb man Interfaces, die zwischen Hauptprogramm und DLL ausgetauscht wurden, vor dem Entladen der DLL auf nil setzen sollte, damit es beim Beenden der Anwendung keine Schutzverletzungen gibt.

TurboMagic 8. Aug 2021 14:42

AW: AddModuleUnloadProc und DEC
 
Hallo,

danke schon mal für die Infos.
Der Ärger ist: ich muss nur ein C++ Konsolenprogramm erstellen
welches gar nichts tut, außer eine der Units die so einen Klassenregistrierungsmechanismus
haben, einzubinden. Es reicht dafür die HPP Datei (en) zu generieren und die Unit selber
ins Projekt aufzunehmen.

Danach das mal ausführen und siehe da, beim Beenden crasht es in genau der Prozedure
mit "Listeneintrag nicht gefunden". Der C++ Debugger zeigt, wenn man in der for Schleife
einen Breakpoint setzt auch einen astronomisch hohen und nachgewiesen (mittels CPU Registeransicht)
falschen Wert für i an.

Und hier hört's für micht auf. Unter Delphi läuft das einfach durch, ohne Fehlermeldung.
Kommentiert man die Routine aus, gibt's auch unter C++ Builder keine Fehlermeldung.

Grüße
TurboMagic

jbg 8. Aug 2021 18:59

AW: AddModuleUnloadProc und DEC
 
Ich nehme an TDECClassList war früher mal eine Liste (wahrscheinlich sogar TList wegen dem TClass-Cast). Im aktuellen Code ist die "Liste" nun ein TDictionary<Int64, TDECClass>. Der Code in ModuleUnload() geht aber weiterhin davon aus, dass es sich um eine TList handelt, deren Items TClass sind. Aber wegen dem Dictionary wird die for-Variable "i" nun nicht mehr als Index in die Liste verwendet sondern als Key ins Dictionary. Und das löst die Fehlermeldung aus, da es wahrscheinlich keinen Eintrag mit Key=0, Key=1 usw. gibt.

Dass es unter Delphi funktioniert und unter C++Builder nicht, liegt daran, dass C++Builder ModuleUnload() auch für das eigene Modul aufruft und Delphi eben nicht. Somit fällt der Fehler unter einem "DLL/BPL-freiem" Delphi nicht auf.

Als erstes würde ich die erste If-Anweisung um "if (Instance <> HInstance) and ..." erweitern. Der Aufruf mit der eigenen HInstance der Unit ist nutzlos, da im finalization sowie die Liste freigegeben wird. Wenn also die Unit aus dem eigenen Modul (DLL, BPL) entladen wird, muss nicht noch unnötige Extra-Arbeit geleistet werden.


Das behebt den "Index vs Key" Fehler aber noch nicht. Hierfür muss das Dictionary als Dictionary behandelt werden:

Delphi-Quellcode:
procedure ModuleUnload(Instance: NativeInt);
var // automaticaly deregistration/releasing
  i: Integer;
  Items: TArray<TPair<Int64, TDECCLass>>;
begin
  // C++Builder calls this function for our own module, but we destroy the ClassList in that case in the finalization section anyway.
  if (Instance <> HInstance) and
     (TDECHash.ClassList <> nil) and (TDECHash.ClassList.Count > 0) then
  begin
    Items := TDECHash.ClassList.ToArray;
    for i := Length(Items) - 1 downto 0 do
    begin
      if FindClassHInstance(Items[i].Value) = HINST(HInstance) then
        TDECHash.ClassList.Remove(Items[i].Key);
    end;
  end;
end;

Derselbe Fehler ist übrigens auch in
- DECCipherBase.pas
- DECFormatBase.pas
- DECHashBase.pas (hier beschriebener Fehler)

TurboMagic 9. Aug 2021 08:19

AW: AddModuleUnloadProc und DEC
 
Hallo,

danke für die wertvollen Infos/Tipps.
Werde das baldmöglichst so umsetzen und testen.

Grüße
TurboMagic

TurboMagic 10. Aug 2021 18:12

AW: AddModuleUnloadProc und DEC
 
Es gibt davon auch eine Prozedur mit NativeUInt Parameter.
Die würde ich gerne lieber nutzen um eine Warnung wegen
Vergleich int zu unit zu vermeiden.

Wäre das OK und seid wann gibt es diese?

jbg 10. Aug 2021 20:22

AW: AddModuleUnloadProc und DEC
 
In Delphi 2009 gibt es die bereits, wobei dort noch als "(HInstance: LongWord)". In Delphi XE ist es dann schon "(HInstance: THandle)".

Eine explizite "NativeUInt" Version gibt es nicht. Jedoch ist unter Delphi 2009 "THandle = LongWord" und unter Delphi XE "THandle = NativeUInt". Somit sollte die "HInstance: THandle" deklaration in allen Delphis ab 2009 (und vielleicht sogar 2006/2007) funktionieren.


Alle Zeitangaben in WEZ +1. Es ist jetzt 07:33 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz