Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi Problem mit GetClass in stat. App bei dyn. geladenen Package (https://www.delphipraxis.net/122870-problem-mit-getclass-stat-app-bei-dyn-geladenen-package.html)

MaBuSE 23. Okt 2008 14:26


Problem mit GetClass in stat. App bei dyn. geladenen Package
 
Hallo zusammen,
ich habe folgendes Problem:

Ich möchte gerne aus einer Delphi-Applikation (die keine Runtime-Package (VCL, RTL, ...) verwendet) ein einzelnes Package dynamisch laden und auf eine enthaltene Klasse zugreifen, die dann im Kontext der Anwendung laufen soll.

Wenn ich die Anwendung auf das Benutzen von dynamischen Package konfiguriere, dann klappt der GetClass Aufruf.
Dann ist meine Applikation aber klein und verwendet die Standard Package.
Ich möchte aber eine Stand-alone-Exe haben, die bei Bedarf nur auf einem Package etwas nachladen soll.

Beisp Package: (der Einfachheit-halber nur ein Formular)
Delphi-Quellcode:
package myPackage;
...
contains
  u_frmPackage in 'u_frmPackage.pas' {frmPackage};
end.
Delphi-Quellcode:
unit u_frmPackage;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TfrmPackage = class(TForm)
    Memo1: TMemo;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

implementation

{$R *.dfm}

procedure TfrmPackage.Button1Click(Sender: TObject);
var
  i: Integer;
begin
  for i := 0 to Application.ComponentCount - 1 do
  begin
    Memo1.Lines.Add(Application.Components[i].Name + ': ' + Application.Components[i].ClassName);
  end;
end;

initialization
  RegisterClasses([TfrmPackage]);
  ShowMessage('Class registered');

finalization
  UnRegisterClasses([TfrmPackage]);
  ShowMessage('Class unregistered');

end.
Die Anwendung:
Delphi-Quellcode:
u_frmApp
...
type
  TmyFormClass = class of TForm;

procedure TfrmApp.Button1Click(Sender: TObject);
var
  i: HMODULE;
  c: TmyFormClass;
  f: TForm;
begin
  i := LoadPackage('myPackage.bpl');
  c := TmyFormClass(GetClass('TfrmPackage'));
  if c = nil then
  begin
    Caption := 'Form wurde nicht gefunden';
  end
  else
  begin
    Caption := c.ClassName;
    f := c.Create(self);
    f.ShowModal;
    f.Free;
  end;
  UnLoadPackage(i);
end;
...
Wenn "Menu -> Projekt -> Optionen -> Packages -> Laufzeit Packages -> [X] LaufzeitPackages aktivieren" aktiviert ist, dann funktioniert es.

Was muß ich tun um diese Funktionalität auch zutzen zu können,
wenn "Menu -> Projekt -> Optionen -> Packages -> Laufzeit Packages -> [ ] LaufzeitPackages aktivieren" nicht aktiviert ist?

Es werden zwar die Klassen registriert (Dialog mit 'Class registered' bzw 'Class unregistered' ist zu sehen),
aber GetClass() gibt nur nil zurück.

Ich hoffe jemand heit einen guten Vorschlag ;-)

Unsere Anwendungen komplett auf Packages umzustellen geht aus verschiedenen technischen Gründen nicht.

Danke und viele Grüße
MaBuSE

:dp:

oki 10. Mär 2009 14:22

Re: Problem mit GetClass in stat. App bei dyn. geladenen Pac
 
Hallo,

ich kämpfe gerade mit dem gleichen Problem. Nachdem ich wie von MaBuSE beschrieben die Laufzeitpackages aktiviert haben klappt bei mir GetClass für die angegebene Klasse. Dabei taucht gedoch ein anderes Problem auf.

Meine Fensterklasse ist von einer eigenen Basisklasse abgeleitet. Deren Vorfahre ist TForm. Diese Basisklasse liegt wieder in einem eigenen Package.
Ich kann das Package mit meiner neuen Fensterklasse laden und auch das Fenster erstellen. Klappt soweit alles. Wenn ich jetzt aber eine Prüfung mittels is auf den Vorfahren mache liefert dies False zurück. Meine Vorfahrenklasse ist registriert (hab ich geprüft).

Hier noch mal die Hierarchie:

TMyBaseForm (liegt in BasePackage und ist mittels RegisterClass registriert)
TMySpecialForm = class(TMyBaseForm) (liegt in SpecialPackage und ist registriert)

Anwendung lädt Specialpackage mittels LoadPackage und kreiert AForm vom Typ TMySpecialForm.
Typprüfung schlägt fehl:
Delphi-Quellcode:
if AForm is TMyBaseForm then
  ....
Warum das jetzt? Ich habe andere Formulare in meiner Anwendung die auch den Vorfahren TMyBaseForm besitzen. Diese sind im Projekt der Anwendung eingebunden. Eine Prüfung auf TMyBaseForm funzt da ohne Probleme. Nur halt bei den geladenen nicht.

Gruß oki

Bernhard Geyer 10. Mär 2009 14:29

Re: Problem mit GetClass in stat. App bei dyn. geladenen Pac
 
Entweder alles Runtimepackages oder gar nichts! Mixbetrieb ist nicht möglich (bzw. nur mit sehr viel Glück und vielen harten Casts).

Hat deine Anwendung keine Runtimepackages aber die DLL schon, so ist TObject der Exe <> TObject der BPL da beide vollkommen unabhängige RTTI haben!

Apollonius 10. Mär 2009 14:37

Re: Problem mit GetClass in stat. App bei dyn. geladenen Pac
 
Ich bin ehrlich gesagt irritiert, das LoadPackage keine Exception wirft. Eigentlich sollte LoadPackage auf doppelte Units prüfen und gegebenenfalls eine Exception auslösen. Das aber selbst mit Tricks GetClass nicht funktionieren kann, ist auch klar: Letztlich schreibt RegisterClass die Klasse nur in eine globale Variable der Unit Classes. Und diese Variable ist natürlich nur über Modulgrenzen hinweg benutzbar, wenn man Laufzeitpackages verwendet.

oki 10. Mär 2009 15:11

Re: Problem mit GetClass in stat. App bei dyn. geladenen Pac
 
ok,

schon mal Dank.
Leider klemmt es jetzt etwas mit dem Verständnis des gesagten; oder besser mit der Umsetzung.

Das mit der RTTI und der Registrierung leuchtet mir ein. Die Frage ist jetzt nur, wo genau sollte ich nur mit Runtimepackages arbeiten. Reicht es in meiner Exe oder muss ich da bei meinen Packages etwas beachen?

Sorry für die platten Fragen, aber an der Stelle bin ich völlig blass. Hab halt immer meine Packages erstellt (Compiler Design/Runtime) und gut wars. Hatte nie den Bedarf Packages zur Laufzeit zu laden.

Ich fasse auch noch mal zusammen, was ich bis jetzt an welcher Stelle gemacht habe:
- Package "BasePackage" mit Compilereinstellung "Entwurf und Laufzeit" erstellt. (Ausgabeverzeichnis ...CodeGear\5.0\...)
- Package "SpecialPackage" mit Compilereinstellung "Entwurf und Laufzeit" erstellt. (Ausgabeverzeichnis ...CodeGear\5.0\...)

Meine Anwendung die SpecialPackage laden soll habe ich mit "Laufzeit-Package verwenden" aktiviert compiliert. Hier konnte ich jedoch die beiden Packages nicht mit eintragen. Kommt der Fehler "F2084 Interner Fehler: U1036" wenn meine MainForm.pas compiliert wird. Das MainForm ist ebenfalls ein Nachfahre meines BaseForms aus "BasePackage". Somit werden da die Units aus dem Package eingebunden.
Ich denke das ist vielleicht eine Folge des besagen Mischbetriebes.

Da diese Anwendung nur für den internen Gebrauch gedacht ist habe ich kein Problem mit den Laufzeitpackages. Ein sauberer Weg ist mir recht. Nur wie?

Schon mal Dank und Gruß

oki

Apollonius 10. Mär 2009 15:18

Re: Problem mit GetClass in stat. App bei dyn. geladenen Pac
 
Alle Units, die in mehreren Modulen verwendet werden, müssen in allen verwendenden Modulen mit einem Laufzeitpackage eingebunden werden. Insbesondere müssen also sowohl die Packages als auch das Hauptprogramm mit den Packages RTL und VCL kompiliert werden.

oki 10. Mär 2009 15:30

Re: Problem mit GetClass in stat. App bei dyn. geladenen Pac
 
Hi Apollonius,

das ging zu schnell. ich glaub ich brauch das grad etwas praktischer.

Bsp.:
ich habe eine Unit MyBaseWindow.pas die ich in folgenden Modulen verwende:
- BasePackage.bpl
- SpecialPackage.bpl
- MyApp.exe

in den Projektoptionen habe ich jetzt die Lasche "Beschreibung". Dort kann ich für die angegebenen Packages einmal den Schalter "Verwenden für" mit den Optionen "Entwurf", "Laufzeit" und "Entwurf und Laufzeit" einstellen. Bei allen Modulen kann ich in der Lasche "Packages" die Checkbox "Laufzeitpackages verwenden" wählen. Darüber befindet sich das ListView "Entwurfs-Packages" in der alle in meiner IDE geladenen Packages gecheckt sind.

Wo muss ich jetzt was rein und was raus nehmen.

Ich stell mich heute vielleicht etwas blöd an, aber mich drückt der Terminschuh und ich hoffe ich schaffe es heute mit euch ohne eigenes Grundlagenstudium.

Dank und Gruß oki

Apollonius 10. Mär 2009 17:44

Re: Problem mit GetClass in stat. App bei dyn. geladenen Pac
 
Jedes Package kann Units enthalten (contains), andere Units in das Package einkompilieren und mit requires andere Packages einbinden. Requires greift dabei auf die Units zu, die im anderen package unter contains aufgelistet sind.
Nun gilt die Regel: Jede Unit darf nur in einem einzigen Modul einkompiliert sein. Wenn sie in mehreren gebraucht wird, muss ein Package die Unit unter contains aufführen, welches die anderen mit requires einbinden.
Konkret heißt das bei dir: Deine Unit MyBaseWindow musst du entweder in BasePackage oder in SpecialPackage hineinkompilieren; vermutlich wirst du BasePackage wählen. Das heißt, dass du neben der normalen Auflistung in der Uses-Klausel die Unit in die Contains-Klausel schreibst. In allen anderen Packages, die die Unit verwenden, schreibst du dann BasePackage in die Requires-Klausel. Auf die Requires-Klausel hast du auch direkt im Projekt-Baum Zugriff.

Das selbe musst du dann auch für das Hauptprogramm tun. Da es dort jedoch keine Requires-Klausel gibt, musst du die Projekt-Optionen bemühen: Bei Packages die Checkbox "Laufzeitpackages verwenden" setzen und dein Package in das Edit-Feld hinzufügen.

Die selben Regeln gelten auch für alle anderen gemeinsam genutzten Units - auch die Delphi-eigenen. Daher musst du in jedem Fall das Package RTL einbinden, gegebenenfalls auch VCL.

oki 10. Mär 2009 17:49

Re: Problem mit GetClass in stat. App bei dyn. geladenen Pac
 
hab ich verstanden.

Ich teste es.

Gruß oki

oki 11. Mär 2009 06:33

Re: Problem mit GetClass in stat. App bei dyn. geladenen Pac
 
Moin,

ich habe jetzt gründlich aufgeräumt. Alle Units sind nur noch in einem Package eingebunden. In meiner Exe habe ich dann mein BasePackage in der Rubrik "Laufzeitpackage verwenden" zusammen mit "rtl" und "vcl" eingetragen und erfolgreich kompiliert. Das lief dann auch ohne die zuvor erwähnten Fehler ab.

Dafür erst mal Dank! :thumb:

Jetzt hat sich folgendes herausgestellt. Ich lade mein SpecialPackage zur Laufzeit mittels LoadPackage. Die in diesem Package registrierte Fensterklasse TMySpecialForm wird durch GetClass gefunden. Die Prüfung mit "is" ist erfolgreich. Leider werden aber alle in BasePackage definierten und registrierten Fensterklassen jetzt nicht mehr erkannt. Alle Registrierungen erfolgen in den entsprechenden Units mittels RegisterClass im initialization-Teil der Unit.
Ich habe dann in einer Unit des SpezialPackage im initialization-Teil alle Fenster des BasePackage noch einmal mittels RegisterClasses aufgenommen. Siehe da, jetzt werden sie erkannt.

Ich hatte eigentlich gehofft, dass ich durch die Einbindung des Packages BasePackage als Runtimemodule das in dem nachzuladenden Package nicht mehr tun muss. Ich habe den Hinweis zur Klassenregistrierung in der Unit Classes zwar wahrgenommen, wie das aber konkret abläuft und sich auswirkt hab ich noch nicht verstanden. Auch das Thema "Modulgrenzen" ist mir so nicht richtig klar geworden.
Im Grunde ist mein "Hauptmodul" ja meine Exe. Diese hat das Package BasePackage, angegeben als Runtimepackage, eingebunden. Damit sollte es doch alle registrierten Klassen aus BasePackage kennen. Würde meine Exe jetzt die Klasse aus dem nachgeladenen SpezialPackage nicht kennen, so könnte ich das verstehen. Aber es ist genau umgekehrt. Der Code in meiner Exe erkennt die Klassen aus dem nachgeladenen Spezialpackage, aber nicht die Klassen aus BasePackage. Für mich ist das grad mal verkehrte Welt.

Ich habe auch gerade mal eine Idee getestet. Bevor ich das SpezialPackage lade kennt meine Anwendung die registrierten Klassen. Nach dem Laden nicht mehr. Wird da vielleicht im Hintergrund das BasePackage der Exe entladen, damit das in "erforderlich" eingetragenen BasePackage des geladenen SpezialPackage verwendet wird? Da fällt mir ein, dass in den Finalization-Teil der Fensterunits auch ein UnregisterClass für die Fensterklassen aufgenommen habe.


Mal schauen, wenn einer eine Idee hat, her damit.

Und wieder Dank im Voraus,

Gruß oki


Alle Zeitangaben in WEZ +1. Es ist jetzt 13:50 Uhr.
Seite 1 von 2  1 2      

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