Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   GUI-Design mit VCL / FireMonkey / Common Controls (https://www.delphipraxis.net/18-gui-design-mit-vcl-firemonkey-common-controls/)
-   -   Delphi DLL + DataModule in Objektinspektor (https://www.delphipraxis.net/61081-dll-datamodule-objektinspektor.html)

sir-archimedes 16. Jan 2006 21:04


DLL + DataModule in Objektinspektor
 
Hi,

ich möchte gerne Teile meines Projekts in dlls auslagern. Dazu habe ich mir nun in einem Testprojekt folgendes erstellt:

* eine Hauptform, die die DLL lädt
* eine DatenModul-Klasse BaseDataModule = class(TDataModule)

Die Hauptform erzeugt zur Laufzeit das DatenModul und bindet es unter public ein. (Die globale DataModule-Variable habe ich entfernt).

In der DLL wird die Unit des DatenModuls eingebunden, damit die DLL die Unit überhaupt kennt. Die dll kann nun eine Form erzeugen und diese direkt mit dem DataModule verbinden. (Create umgeschrieben).

Nungut - die DLL "kennt" das DatenModul also und kann ohne weiteres damit arbeiten. Zur Designzeit kennt die Form in der DLL das DataModule auch. Ich kann ohne Probleme z.B. ein DBGrid mit einem DataSource des DatenModuls verbinden.

Starte ich das Programm, wird diese Bindung von Grid zum DataSource aber nicht wieder hergestellt. Das funktioniert leider nur zur Designzeit. Weiß jemand wieso? Ich kann bei umfangreicheren Dialogen schlecht immer wieder alles per SourceCode machen - will ja schließlich RAD nutzen :dancer:

Achja: egal, wie ich das DatenModul in der Form in der dll nenne, im Objektinspektor kann immer über BaseDataModule darauf zugegriffen werden. Komisch das... :gruebel:

Danke schon mal für eure Hilfe? :)

Gruß

DirkG 31. Jan 2006 17:27

Re: DLL + DataModule in Objektinspektor
 
Kann es sein, das die DLL das Problem ist?

Wenn ich ein Datenmodul in einer DLL verwende, nutze ich in der DLL.dpr immer folgende Routinen:

Delphi-Quellcode:
library myDLL;

uses
  ShareMem,
  MyDatenmodul in 'MyDatemodul.pas' {MyDatenmodul: TDataModule},
...

resourcestring
  //-- Datenbank DLL Vorgaben -------------------------------------------------
  dllPath                             = 'plugins';
  //-- Datenbank DLL Fehlermeldungen ------------------------------------------
  dllPathError                        = 'Der PlugIn Ordner %s konnte nicht erstellt werden';
  dllCoInitError                      = '%s hat einen CoInitialize-Fehler gemeldet!';
  dllDMCreateError                    = '%s hat einen Fehler beim Erzeugen des Datenmoduls %s gemeldet!';

{$R *.RES}
   
var  DLLProcNext      : procedure(Reason: Integer); stdcall = nil;
      bDoCoUninitialize : Boolean;
      bDoDestroyDM_ADO : Boolean;

// ============================================================================
// Interne - Hilfsfunktionen
// ============================================================================

// ----------------------------------------------------------------------------
// function DBInit: Integer; stdcall;
// ----------------------------------------------------------------------------
// Funktion...: Alle Eemente der DLL initialisieren bzw erzeugen
// Parameter..: keine
// Erreichbar.: intern
function DBInit: boolean; stdcall;
var sMsg: string;
begin
  Result := true; try;
    //-- ActivX initialisieren ------------------------------------------------
    sMsg:= Format(dllCoInitError, [dllName]);
    CoInitialize(nil); bDoCoUninitialize := True;
    //-- Datenmodul erzeugen --------------------------------------------------
    sMsg:= Format(dllDMCreateError, [dllName, DatTable]);
    MyDatenmodul:= TMyDatemodul.Create(Application);
  except; ShowMessage(sMsg); Result := false; end;
end;
// ----------------------------------------------------------------------------
// function DBUnInit: Integer; stdcall;
// ----------------------------------------------------------------------------
// Funktion...: Datenmodul deinitialisieren
// Parameter..: keine
// Erreichbar.: intern
function DBUnInit: boolean; stdcall;
begin
  Result           := true;
  bDoCoUninitialize := false;
  bDoDestroyDM_ADO := false;
  try
    if bDoDestroyDM_ADO then
      MyDatemodul.Free;
    if bDoCoUninitialize then
      CoUninitialize;
  except
    Result := false;
  end;
end;
// ----------------------------------------------------------------------------
// procedure DLLMain(Reason: Integer); stdcall;
// ----------------------------------------------------------------------------
// Funktion...: eigene Strukturen und Objekte initialisieren
// Parameter..: [Reason] == Grund des Aufrufs
// Erreichbar.: intern
procedure DLLMain(Reason: Integer); stdcall;
begin
  case Reason of
    //-- Die DLL wurde in den Speicherbereich des akt. Prozesses geladen ------
    DLL_PROCESS_ATTACH : begin
      DisableThreadLibraryCalls(hInstance);
      DBInit;
    end;
    //-- Der aktuelle Prozeß erzeugt einen neuen Thread -----------------------
    DLL_THREAD_ATTACH : begin
    end;
    //-- Ein Thread wird beendet ----------------------------------------------
    DLL_THREAD_DETACH : begin
    end;
    //-- Die DLL wird wieder aus dem Speicherbereich des akt.Prozesses entfernt
    DLL_PROCESS_DETACH : begin
      DBUnInit;
    end;
  end;
  if Assigned(DLLProcNext) then DLLProcNext(Reason);
end;

// ============================================================================
// Initialisierungscode
// ============================================================================
begin
  DLLProcNext := Pointer(InterlockedExchange(Integer(DLLProc),Integer(@DLLMain)));
  DLLMain(DLL_PROCESS_ATTACH);
end.
Ich habe das irgendwo aus dem Netz. Hoffe es hilft dir weiter.

mkinzler 31. Jan 2006 18:03

Re: DLL + DataModule in Objektinspektor
 
Wenn du ein Package anstatt einer Dll verwendest hast du weinger Probleme.

BTW. wenn du Programteile dynamisch zulädst verlierst du zwwangsläufig zur Designzeit eingestellte Verbindungen.

hanspeter 1. Feb 2006 06:45

Re: DLL + DataModule in Objektinspektor
 
Zitat:

Zitat von mkinzler
Wenn du ein Package anstatt einer Dll verwendest hast du weinger Probleme.

Ziehst Dir aber eine bpl Hölle an Land.
Alle Laufzeit BPL müssen mit ausgeliefert werden.
Da kommen schnell mal an die 100 zusammen und wehe es sind nicht alls auf den neusten Stand.

Gruß Peter

mkinzler 1. Feb 2006 08:46

Re: DLL + DataModule in Objektinspektor
 
Du kannst aber mehere bpls wiederrum in eine verpacken und diese dann ausliefern.

hanspeter 3. Mär 2006 08:52

Re: DLL + DataModule in Objektinspektor
 
Zitat:

Zitat von mkinzler
Du kannst aber mehere bpls wiederrum in eine verpacken und diese dann ausliefern.

Scheint ab D2006 nicht mehr zu gehen, da die zugehörigen Tools (PCE.Exe) nicht mehr mit ausgeliefert werden.


Gruß Peter

RWarnecke 23. Apr 2006 15:45

Re: DLL + DataModule in Objektinspektor
 
Hallo,

ich habe mir jetzt schon mehrmals den kompletten Beitrag gelesen und komme nicht weiter. Ich habe auch versucht den Code hier :

Delphi-Quellcode:
library myDLL;

uses
  ShareMem,
  MyDatenmodul in 'MyDatemodul.pas' {MyDatenmodul: TDataModule},
...

resourcestring
  //-- Datenbank DLL Vorgaben -------------------------------------------------
  dllPath                             = 'plugins';
  //-- Datenbank DLL Fehlermeldungen ------------------------------------------
  dllPathError                        = 'Der PlugIn Ordner %s konnte nicht erstellt werden';
  dllCoInitError                      = '%s hat einen CoInitialize-Fehler gemeldet!';
  dllDMCreateError                    = '%s hat einen Fehler beim Erzeugen des Datenmoduls %s gemeldet!';

{$R *.RES}
   
var  DLLProcNext      : procedure(Reason: Integer); stdcall = nil;
      bDoCoUninitialize : Boolean;
      bDoDestroyDM_ADO : Boolean;

// ============================================================================
// Interne - Hilfsfunktionen
// ============================================================================

// ----------------------------------------------------------------------------
// function DBInit: Integer; stdcall;
// ----------------------------------------------------------------------------
// Funktion...: Alle Eemente der DLL initialisieren bzw erzeugen
// Parameter..: keine
// Erreichbar.: intern
function DBInit: boolean; stdcall;
var sMsg: string;
begin
  Result := true; try;
    //-- ActivX initialisieren ------------------------------------------------
    sMsg:= Format(dllCoInitError, [dllName]);
    CoInitialize(nil); bDoCoUninitialize := True;
    //-- Datenmodul erzeugen --------------------------------------------------
    sMsg:= Format(dllDMCreateError, [dllName, DatTable]);
    MyDatenmodul:= TMyDatemodul.Create(Application);
  except; ShowMessage(sMsg); Result := false; end;
end;
// ----------------------------------------------------------------------------
// function DBUnInit: Integer; stdcall;
// ----------------------------------------------------------------------------
// Funktion...: Datenmodul deinitialisieren
// Parameter..: keine
// Erreichbar.: intern
function DBUnInit: boolean; stdcall;
begin
  Result           := true;
  bDoCoUninitialize := false;
  bDoDestroyDM_ADO := false;
  try
    if bDoDestroyDM_ADO then
      MyDatemodul.Free;
    if bDoCoUninitialize then
      CoUninitialize;
  except
    Result := false;
  end;
end;
// ----------------------------------------------------------------------------
// procedure DLLMain(Reason: Integer); stdcall;
// ----------------------------------------------------------------------------
// Funktion...: eigene Strukturen und Objekte initialisieren
// Parameter..: [Reason] == Grund des Aufrufs
// Erreichbar.: intern
procedure DLLMain(Reason: Integer); stdcall;
begin
  case Reason of
    //-- Die DLL wurde in den Speicherbereich des akt. Prozesses geladen ------
    DLL_PROCESS_ATTACH : begin
      DisableThreadLibraryCalls(hInstance);
      DBInit;
    end;
    //-- Der aktuelle Prozeß erzeugt einen neuen Thread -----------------------
    DLL_THREAD_ATTACH : begin
    end;
    //-- Ein Thread wird beendet ----------------------------------------------
    DLL_THREAD_DETACH : begin
    end;
    //-- Die DLL wird wieder aus dem Speicherbereich des akt.Prozesses entfernt
    DLL_PROCESS_DETACH : begin
      DBUnInit;
    end;
  end;
  if Assigned(DLLProcNext) then DLLProcNext(Reason);
end;

// ============================================================================
// Initialisierungscode
// ============================================================================
begin
  DLLProcNext := Pointer(InterlockedExchange(Integer(DLLProc),Integer(@DLLMain)));
  DLLMain(DLL_PROCESS_ATTACH);
end.
in ein Beispielprojekt einzufügen. Nur leider bekomme ich beim Compilieren der DLL schon den Fehler, das dllname unbekannt sei. Was habe ich verkehrt gemacht ?

DirkG 24. Apr 2006 07:23

Re: DLL + DataModule in Objektinspektor
 
Du hast nichts falsch gemacht.

Als ich den Code aus einem Project von mir kopiert und hier eingefügt habe, vergass ich die beiden Constanten zu erwähnen.

Also, ich glaube hier hast du den Fehler bekommen.

Delphi-Quellcode:

    sMsg:= Format(dllDMCreateError, [dllName, DatTable]);
dllName : ist eine String Konstante mit dem Dateinamen der DLL
DatTable : ist eine String Konstante mit dem Namen der Tabelle in der Datenbank

Ich hoffe, das hilft dir jetzt weiter.

RWarnecke 24. Apr 2006 18:06

Re: DLL + DataModule in Objektinspektor
 
Also, wenn ich das ganze jetzt richtig verstehe, dann kann ich über diese Weise die Parameter meiner Komponenten in dem DataModule nicht füllen oder ? Denn ich möchte in meinem Programm mich auf verschiedene Datenbanken und Datenbanktypen verbinden. Das heißt, ich möchte gerne meine Verbindungsparameter an die Komponente ZConnection übergeben und die ZQuery soll mir dann die SQL-Befehle ausführen.

Kann ich das über diese Weise machen oder nicht ? Wenn ja, wie muss ich das machen ?

DirkG 25. Apr 2006 07:41

Re: DLL + DataModule in Objektinspektor
 
Wenn du den Code in einer Komponente verwenden willst, kannst du ein Feld für den DLL-Namen und ein Feld für die Tabelle vorsehen. Anstelle der String-Konstanten werden dann in der Fehlermeldung halt die Werte der Felder angezeigt.
Du kannst auch die Fehlermeldungen aus dem Code nehmen und darauf hoffen, das es ohne Fehler läuft.

Delphi-Quellcode:
library myDLL;

uses
  ShareMem,
  MyDatenmodul in 'MyDatemodul.pas' {MyDatenmodul: TDataModule},
...

{$R *.RES} 
   
var  DLLProcNext      : procedure(Reason: Integer); stdcall = nil;
      bDoCoUninitialize : Boolean;
      bDoDestroyDM_ADO : Boolean;

// ============================================================================
// Interne - Hilfsfunktionen
// ============================================================================

// ---------------------------------------------------------------------------- 
// function DBInit: Integer; stdcall;
// ---------------------------------------------------------------------------- 
// Funktion...: Alle Eemente der DLL initialisieren bzw erzeugen
// Parameter..: keine
// Erreichbar.: intern
function DBInit: boolean; stdcall;
begin
  Result := true;
  try;
    //-- ActivX initialisieren ------------------------------------------------ 
    CoInitialize(nil);
    bDoCoUninitialize := True;
    //-- Datenmodul erzeugen -------------------------------------------------- 
    MyDatenmodul:= TMyDatemodul.Create(Application);
  except
    Result := false;
  end;
end;
// ---------------------------------------------------------------------------- 
// function DBUnInit: Integer; stdcall;
// ---------------------------------------------------------------------------- 
// Funktion...: Datenmodul deinitialisieren
// Parameter..: keine
// Erreichbar.: intern
function DBUnInit: boolean; stdcall;
begin
  Result           := true;
  bDoCoUninitialize := false;
  bDoDestroyDM_ADO := false;
  try
    if bDoDestroyDM_ADO then
      MyDatemodul.Free;
    if bDoCoUninitialize then
      CoUninitialize;
  except
    Result := false;
  end;
end;
// ---------------------------------------------------------------------------- 
// procedure DLLMain(Reason: Integer); stdcall;
// ---------------------------------------------------------------------------- 
// Funktion...: eigene Strukturen und Objekte initialisieren
// Parameter..: [Reason] == Grund des Aufrufs
// Erreichbar.: intern
procedure DLLMain(Reason: Integer); stdcall;
begin
  case Reason of
    //-- Die DLL wurde in den Speicherbereich des akt. Prozesses geladen ------ 
    DLL_PROCESS_ATTACH : begin
      DisableThreadLibraryCalls(hInstance);
      DBInit;
    end;
    //-- Der aktuelle Prozeß erzeugt einen neuen Thread ----------------------- 
    DLL_THREAD_ATTACH : begin
    end;
    //-- Ein Thread wird beendet ---------------------------------------------- 
    DLL_THREAD_DETACH : begin
    end;
    //-- Die DLL wird wieder aus dem Speicherbereich des akt.Prozesses entfernt
    DLL_PROCESS_DETACH : begin
      DBUnInit;
    end;
  end;
  if Assigned(DLLProcNext) then DLLProcNext(Reason);
end;

// ============================================================================
// Initialisierungscode
// ============================================================================
begin
  DLLProcNext := Pointer(InterlockedExchange(Integer(DLLProc),Integer(@DLLMain)));
  DLLMain(DLL_PROCESS_ATTACH);
end.
Ein Problem stellt noch die Verwendung von ShareMem dar. Wenn du eine Komponente entwickelst, muss dem Anwender mitgeteilt werden, das er im Projekt die Unit ShareMem in der Projekt Datei einbinden muss. Ansonsten kommt es zu nicht nachzuvollziehbaren Fehlern.

Delphi-Quellcode:
program netguide;

uses
  ShareMem,
  Forms,
  Unit1 in 'Unit1.pas' {Form1};

{$R *.RES}

begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.
Ich habe bei mir nur eine Unit für die Allgemeine Verwendung des DM in einer DLL geschrieben. Die füge ich zu meinem Project hinzu, lege ein DM an und arbeite dann wie gewohnt in der IDE. Die DLL-Namen und Tabellen-Konstanten lege ich in einer Res oder Inc Datei ab, die ich mit folgendem Aufruf in das Project mit einbinde.

Delphi-Quellcode:
{$I 'meineKonstanten.inc'}
Ich hoffe, das hilft dir weiter.

RWarnecke 25. Apr 2006 18:15

Re: DLL + DataModule in Objektinspektor
 
Soweit ist mir das klar. Nur habe ich da noch ein Verständnisproblem, bezüglich den Verbindungsdaten. Wenn ich eine Unit habe, dann binde ich doch ganz einfach mit dem Befehl :

Delphi-Quellcode:
uses
  DataModule;
Mein DataModule in die Unit meiner Application ein. Danach kann ich ja mit der folgenden Codezeile :

Delphi-Quellcode:
DataModule.ZConnection.Host := Edit1.Text;
DataModule.ZConnection.Database := Edit2.Text;
.
.
.
u.s.w.
meine Verbindungsdaten an die Komponente ZConnection senden. Wie mache ich das ganze jetzt, wenn es über die DLL geht ?

DirkG 26. Apr 2006 09:43

Re: DLL + DataModule in Objektinspektor
 
Nun eine DLL hat eine Schnittstelle, mit der sie nach aussen kommuniziert. Ich gehe immer den Weg, das meine Komponente die Eigenschaften besitzt, die ich für die Steuerung der DLL benötige. Die DLL wird von der Komponente dynamisch geladen und dann die DLL Funktionen (Eigenschaft lesen) / Prozeduren (Eigenschaft setzen) aufgerufen, die für die folgende Aufgabe notwendig sind. Dann rufe ich die Prozedur der DLL auf hole das Ergebnis in die Komponente und entlade die DLL wieder. Dieser weg ist zwar etwas umständlich (Komponente und DLL sind ja etwa das gleiche, wenn ich die Funktionen betrachte) aber ich kann in meinen Programmen problemlos auf die DLL zugreifen.
Es gibt da noch die Möglichkeit mit OCX und Interface aber da bin ich selber noch nicht ganz hintergestiegen. Wenn du das hinbekommst, kannst du die DLL oder OCX über die Delphi IDE direkt einbinden. Die Schnittstelle wird von Delphi generiert. Aber dazu müsstes du hier mal jemand anders befragen.

RWarnecke 28. Apr 2006 18:38

Re: DLL + DataModule in Objektinspektor
 
Ich verstehe im Moment nur Bahnhof. Was muss ich jetzt genau machen, wenn ich das was ich hier geschrieben habe :


Zitat:

Zitat von RWarnecke
Wenn ich eine Unit habe, dann binde ich doch ganz einfach mit dem Befehl :

Delphi-Quellcode:
uses
  DataModule;
Mein DataModule in die Unit meiner Application ein. Danach kann ich ja mit der folgenden Codezeile :

Delphi-Quellcode:
DataModule.ZConnection.Host := Edit1.Text;
DataModule.ZConnection.Database := Edit2.Text;
.
.
.
u.s.w.
meine Verbindungsdaten an die Komponente ZConnection senden. Wie mache ich das ganze jetzt, wenn es über die DLL geht ?

mit einer DLL erreichen will ?

Mein erster Gedanke war, ich schreibe meine Verbindungsdaten in eine temporäre Datei und führe dann die Funktion DBInit aus. Damit könnte sich dann meine ZConnection mit der Datenbank verbinden. Soweit mein Gedankengang. Jetzt wüsste ich aber nicht, wie ich jetzt die DataSource mit einem DBGrid zum Beispiel verbinde. Wie muss ich das jetzt machen ? Mein DBGrid zum Beispiel ist in der Hauptform.

Für ein bisschen Sourcecode wäre ich sehr dankbar, da ich im Moment komplett auf der Leitung stehe.

USchmidt 12. Jun 2006 15:44

Re: DLL + DataModule in Objektinspektor
 
Hallo,

auch wenn ich mich erst jetzt der Sache anschließe, habe ich damit folgendes Problem:

Ich generiere im "DLL_PROCESS_ATTACH" - Abschnitt das Datenmodul incl. CoInitialize(nil).
Im "DLL_PROCESS_DETACH" möchte ich es wieder freigeben. Leider führt dies aber zum Fehler.
Lasse ich der Freigabe (Datenmodul.Free) weg, funktioniert es unter NT/W2k aber bei XP gibt
es eine Fehlermeldung (Datenmodul wird mit nil erzeugt). Die Fehlermedlung verweist auf die
MSDART.DLL mit der anschließenden super Fehlermeldung '216'. Im OnDestroy-Ereignis des
Datenmoduls gebie ich nur meine Objekte frei, das scheint auch noch zu funktionieren,
da der Fehler nach dem Verlassen der Procedure auftritt. Nur habe ich doch keine andere
Möglichkeit das Modul freizugeben. Die Unit "ShareMem" muß doch nur eingebunden werden,
wenn man z.B. Strings und TStrings und der gleichen mit der Applikation austauschen will ?

Noch 'ne Frage:

Delphi-Quellcode:
function DBUnInit: boolean; stdcall;
begin
  Result           := true;
  bDoCoUninitialize := false;
  bDoDestroyDM_ADO := false;
  try
    if bDoDestroyDM_ADO then
      MyDatemodul.Free;
    if bDoCoUninitialize then
      CoUninitialize;            -> das wird doch nie ausgeführt ? 
  except
    Result := false;
  end;
end;
Ich hoffe, jemend kann mir einen Tipp geben - Danke ??


Alle Zeitangaben in WEZ +1. Es ist jetzt 10:42 Uhr.

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