Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi Pointer auf Oberklassen (https://www.delphipraxis.net/8931-pointer-auf-oberklassen.html)

theomega 15. Sep 2003 15:35


Pointer auf Oberklassen
 
Hallo Leute
ich möchte ein Teil meiner Anwendung (Datenbank-Zugriff) in eine DLL auslagern um ihn einfacher austauschbar zu machen. Ich brauche in meinem Hauptprogramm ein TQuery (qrmain) um auf die Datenbankfelder zuzugreifen und um Queries auszuführen. Eine DLL-Funktion soll intern ein eigenes Query-Objekt erstellen und auf Befehl einen Pointer auf dieses zurückgeben. Soweit so gut, jetzt scheitert die Sache aber an einer Schutzverletzung. Das hat vermutlich seinen Grund darin, das in der DLL je nach Datenbank in TIBQuery erstellt wird, ich aber in meinem Hauptprogramm nur ein TQuery erwarte.

Jetzt hier mal ein Paar fetzten Code:

erst mal die DLL:
Delphi-Quellcode:
library database;
uses
  IBCustomDataSet,
  IBQuery,
  IBDatabase,
  inifiles,
  dialogs,
  IBServices;

var qrmain: TIBQuery;
    trmain: TIBTransaction;
    dbmain: TIBDatabase;
{$R *.res}


procedure dll_initdatabase;
var ini:TInifile;
begin;
  ini := TInIfile.Create('./config.ini');
try

dbmain := TIBDatabase.Create(nil);
trmain := TIBTransaction.Create(nil);
qrmain := TIBQuery.Create(nil);
dbmain.LoginPrompt := false;
dbmain.DefaultTransaction := trmain;
dbmain.IdleTimer := 0;
dbmain.AllowStreamedConnected := false;

trmain.DefaultDatabase := dbmain;
trmain.Params.Add('concurrency');
trmain.Params.Add('nowait');
trmain.AutoStopAction := saNone;

qrmain.Database := dbmain;
qrmain.Transaction := trmain;
qrmain.CachedUpdates := false;


dbmain.DatabaseName := ini.ReadString('database','databasename','');
dbmain.SQLDialect := ini.ReadInteger('database','sqldialect',3);
dbmain.Params.Text :=
'password='+ini.ReadString('database','password','')+#10#13+
'lc_ctype='+ini.ReadString('database','charset','')+#10#13+
'user_name='+ini.ReadString('database','user_name','');

dbmain.Connected := true;
trmain.Active := true;

finally
  ini.Free;
end;
end;

function dll_getquery:pointer;
begin;
  result:= @qrmain;
end;

procedure dll_commit;
begin;
if trmain.intransaction then trmain.Commit;
Trmain.StartTransaction;
end;

exports dll_initdatabase,dll_getquery,dll_commit;

begin

end.
Und dann mein Hauptprogramm:
Delphi-Quellcode:
var qrmain: TQuery;

procedure dll_initdatabase; external 'database.dll';
function dll_getquery:TQuery; external 'database.dll';
procedure dll_commit; external 'database.dll';

implementation

procedure initdatabase;
begin;
//Creates
dll_initdatabase;
qrmain := TQuery(dll_getquery);
end;

procedure execquery(str:AnsiString;passv:boolean);
begin;
inc(c);

qrmain.Close;
qrmain.SQL.Clear;
qrmain.SQL.Add(str);

if (passv) then qrmain.Open
else qrmain.ExecSQL;
end;

procedure commit;
begin;
dll_commit;
end;
Die Init-Funktion funktioniert Problemlos, nur führt jeder Zugriff auf das qrmain-Objekt zu einer Schutzverletzung.

Warum? Und wie kann ich das ändern?

Grüße
TO

aogwaba 15. Sep 2003 17:48

Re: Pointer auf Oberklassen
 
sollte das:
Zitat:

function dll_getquery:TQuery;
begin;
result:= @qrmain;
end;
nicht so:
Zitat:

function dll_getquery:TQuery;
begin;
result:= qrmain;
end;
lauten.

cu
waba

theomega 15. Sep 2003 17:54

Re: Pointer auf Oberklassen
 
Shit, Fehler gemacht, habe mich oben verschrieben, ist jetzt editiert. Ich würde gerne einen Pointer übergeben und auf Hauptanwednungsseite wieder ein Query draus machen.

aogwaba 15. Sep 2003 19:56

Re: Pointer auf Oberklassen
 
Du erzeugst mit:
Zitat:

qrmain := TIBQuery.Create(nil);
ein Object der Klasse TIBQuery. 'qrmain' ist ein Zeiger, auf diese TIBQuery-Instanz, der @-Operator kann enfallen.

cu
waba

theomega 15. Sep 2003 20:13

Re: Pointer auf Oberklassen
 
Danke für den Hinweis, bringt allerdings garnicht, der Fehler bleibt erhalten.

Ich habe jetzt diesen code hier:

DLL:
Delphi-Quellcode:
procedure dll_initdatabase;
var ini:TInifile;
begin;
  ini := TInIfile.Create('./config.ini');
try

dbmain := TIBDatabase.Create(nil);
trmain := TIBTransaction.Create(nil);
qrmain := TIBQuery.Create(nil);
dbmain.LoginPrompt := false;
dbmain.DefaultTransaction := trmain;
dbmain.IdleTimer := 0;
dbmain.AllowStreamedConnected := false;

trmain.DefaultDatabase := dbmain;
trmain.Params.Add('concurrency');
trmain.Params.Add('nowait');
trmain.AutoStopAction := saNone;

qrmain.Database := dbmain;
qrmain.Transaction := trmain;
qrmain.CachedUpdates := false;


dbmain.DatabaseName := ini.ReadString('database','databasename','');
dbmain.SQLDialect := ini.ReadInteger('database','sqldialect',3);
dbmain.Params.Text :=
'password='+ini.ReadString('database','password','')+#10#13+
'lc_ctype='+ini.ReadString('database','charset','')+#10#13+
'user_name='+ini.ReadString('database','user_name','');

dbmain.Connected := true;
trmain.Active := true;

finally
  ini.Free;
end;

end;

function dll_getquery:pointer;
begin;
  result:= qrmain;
end;


exports dll_initdatabase,dll_getquery;

begin

end.
Hauptprogramm:

Delphi-Quellcode:
var qrmain: TQuery;

procedure dll_initdatabase; external 'database.dll';
function dll_getquery:pointer; external 'database.dll';

procedure initdatabase;
begin;
//Creates
dll_initdatabase;
qrmain := TQuery(dll_getquery);
end;
Weißt du, oder jemand anderes woran es liegt?

theomega 15. Sep 2003 20:22

Re: Pointer auf Oberklassen
 
Und es scheint auch egal ob ich schreibe
qrmain := dll_getquery;

oder

qrmain := TQuery(dll_getquery);

theomega 16. Sep 2003 10:23

Re: Pointer auf Oberklassen
 
Weiß da niemand was?!?! Würde mir echt helfen!

Phoenix 16. Sep 2003 10:28

Re: Pointer auf Oberklassen
 
Versuch mal, den Pointer zu dereferenzieren:

Code:
qrmain := dll_getquery^;

theomega 16. Sep 2003 10:35

Re: Pointer auf Oberklassen
 
Get leider nicht, wenn ich mache:
qrmain := dll_getquery^;

dann mault der compiler
Code:
[Fehler] database.pas(34): Inkompatible Typen: 'TQuery' und 'procedure, untyped pointer or untyped parameter'
Wenn ich dagegen mache:
qrmain := TQuery(dll_getquery^);
dann compiled er zwar, aber es kommt wieder ein Schutzverletzung!

Phoenix 16. Sep 2003 10:41

Re: Pointer auf Oberklassen
 
Äh.. ja:

in der DLL hast Du eine TIBQuery, und Du castest einen Pointer auf ein TIBQuery dereferenzierend auf ein TQuery.

TQuery und TIBQuery sind aber nicht mit einander kompatibel.
Du müsstest Dich schon entscheiden, ob Du nun mit Interbase oder einem 'normalen' Query arbeiten willst.

theomega 16. Sep 2003 10:43

Re: Pointer auf Oberklassen
 
Und warum sind die nicht kompatibel? TIBQuery ist doch ein Nachfahre von TQuery, oder? Ich kann mich halt nicht entscheiden, weil ich nachher eine DLL für Interbase und eine für MySQL benutzten will. Mein Hauptprogramm soll es ganz egal sein, was für eine Datenbank die DLL nutzt.

[edit] OK, simmt, sorry, du hast recht, TIBQuery ist ein Nachfahre von Tdataset nicht aber von TQuery![/edit]


ich habe das mal auf total simplen Code verringert:

Delphi-Quellcode:
var
  qrmain: TIBQuery;
  p:pointer;


procedure TForm1.Button1Click(Sender: TObject);
begin
qrmain := TIBQuery.Create(nil);
p := @qrmain;
end;

procedure TForm1.Button2Click(Sender: TObject);
var tempqr:TQuery;
begin
tempqr := TQuery(p^);

tempqr.Sql.Text := 'SELECT * FROM tabelle';
tempqr.ExecSQL;
end;
Und genau das schlägt auch schon fehl. Es liegt also nicht an der DLL sondern an der Funktion an sich!

Phoenix 16. Sep 2003 10:43

Re: Pointer auf Oberklassen
 
Alternativ, wenn Dir das Hilft, kannst Du ein TDataSet übergeben. Das ist die Basisklasse, aus der TIBDataSet -> TIBQuery und TBDEDataSet -> TQuery abgeleitet sind.

Phoenix 16. Sep 2003 10:46

Re: Pointer auf Oberklassen
 
Zitat:

Zitat von theomega
Und warum sind die nicht kompatibel? TIBQuery ist doch ein Nachfahre von TQuery, oder?

Eben nicht.
Hierarchie von TIBQuery:

TObject -> TPersistent -> TComponent -> TDataSet -> TIBCustomDataSet -> TIBQuery

Hierarchie von TQuery:

TObject -> TPersistent -> TComponent -> TDataSet -> TBDEDataSet -> TDBDataSet -> TQuery

Wie man sieht sind die Dinger nur bis TDataSet miteinander kompatible Typen.

Phoenix 16. Sep 2003 10:50

Re: Pointer auf Oberklassen
 
An der Stelle wird Dir nichts anderes übrig bleiben, als eine Wrapper-Klasse zu schreiben, die eine TQuery und eine TIBQuery als Member-Variablen hat.

Du übergibst einen TDataSet-Pointer.

Je nach dem, was übergeben wird (If myDataSet is TIBQuery oder eben If myDataSet is TQuery..) musst Du die jeweilige interne Variable belegen und alle Aufrufe inten auf das entsprechende Objekt umleiten.

Das ist so die einfachste Möglichkeit die ich sehe.

negaH 16. Sep 2003 10:53

Re: Pointer auf Oberklassen
 
Zitat:

Und warum sind die nicht kompatibel? TIBQuery ist doch ein Nachfahre von TQuery, oder?
Selbst eine EXE.TQuery ist nicht kompatibel mit DLL.TQuery.
Klassen können nicht aus DLL's übergreifend benutzt werden !! Nur wenn die EXE und die DLL die Packages VCL??.bpl und VCLDB??.bpl verwenden können Klassen die in diesen Packages deklariert wurden übergreifend benutzt werden.
Wird das nicht so gemacht dann linkt der Compiler für die EXE und die DLL jeweils separat die komplette VCL ein. Somit ist die Klasse TQuery in der EXE nicht mehr identisch mit der Klasse TQuery in der DLL. Sie sind zwar Speicherkompatibel, also der Klassenrecord in den Codesegemnten der EXE und DLL sind identisch aufgebaut aber es sind NICHT der gleiche Record, eg. Speicherbereich. Da z.B. der IS und AS Operator aber auf der Speicheradresse der Klassendefinition aufbaut funktionieren diese Operatoren nicht mehr. D.h. eine TQuery erzeugt in der DLL würde bei der Abfrage if (DLLQuery is TQuery) then ; in der EXE immer FALSE liefern.

Es ist also falsch DLL's zu bauen die mit Klassen arbeiten auf die die EXE zugreifen will. Es sei denn man benutzt Packages.

Gruß Hagen

Phoenix 16. Sep 2003 10:55

Re: Pointer auf Oberklassen
 
Ouch. Man lernt nie aus...
Das dürfte sich also generell schwierig gestalten...

theomega 16. Sep 2003 11:05

Re: Pointer auf Oberklassen
 
So eine Kacke, ihr habt recht:
jetzt läuft es ohne jegliche Schutzverletzungen, nur mein Dataset hat nie ein Ergebniss. Fällt jemanden von euch was anderes ein? Ist das mit dem package schwierig?

Ein konzept sehe so aus:
Code:
Hauptanwendung:      DLL1:         DLL2:

qrmain:TDataSet      qrmain:TIBQuery      qrmain:TZeosQuery
jetzt soll jeder Zugriff auf die qrmain, automatisch an die jeweilge DLL weitergeleitet werden, in das jeweilige Objekt!

negaH 16. Sep 2003 11:13

Re: Pointer auf Oberklassen
 
Zitat:

Das dürfte sich also generell schwierig gestalten...
Nicht unbedingt. Alle Module die gemeinsam auf Klassen zugreifen wollen müssen die Packages die diese Klassen deklarieren benutzen. Z.b. eine EXE lädt eine Formular DLL und eine Report DLL. Alle drei Module benutzen die Packages VCL??.bpl und VCLDB??.bpl. Da nun die gemeinsam genutzen Klassen in diesen Packages enthalten sind existiert deren Klassendefinition auch nur EINMALIG im Package. Alle Zugriffe auf diese Klasse aus der EXE und den beiden DLL's laufen auf Zugriffe in das Packages hinaus. Somit ist alles wieder richtig, denn diese Klasse existiert global für alle anderen Module nur einmal. Als Seiteneffekt bei größeren Projekten reduziert sich der Resourcenverbrauch. Z.b. ein Projekt mit 1 EXE und 50 Formular-DLL's + 200 Report-DLL's benötigt pro DLL ca. 50 Kb. 251 * 50Kb = 13Mb + Packages 7Mb = 20Mb für alle Module.
Ohne Packages werden alle benötigten Parts der VCL in das Modul eingelinkt. Wir wissen das eine einfache EXE dann sofort 400Kb groß wird. Dies trifft auf DLL's genauso zu. Also: 251 * 400Kb = 100Mb, schon 4 mal soviel wie mit Packages.

Grundsätzlich kann man die VCL+Klassen nicht in einfache DLL's global benutzen. Ich weis es gibt im WEB viele Anleitungen wie man zB. Formulare in DLL's benutzen kann, aber im Grunde sind diese alle falsch.

Gruß Hagen

negaH 16. Sep 2003 11:19

Re: Pointer auf Oberklassen
 
Zitat:

Ist das mit dem package schwierig?
Nein, nicht im geringsten. In den Projektoptionen der DLL+EXE einfach die Checkbox "Mit Packages compilieren" anhacken. Dann im Edit der benötigten Packages nur die Packagenamen eintragen die übergreifend benutzt werden sollen. In meinen Projekten nutzte ich dann ausschließlich Packges. Zusätzlich verfrachte ich das komplette Hauptformular ebenfalls in ein eigenes Packages, z.B. Main.bpl. Die EXE wird dann ca. 15 Kb groß sein, und alle DLL's benutzen ebenfalls das Package Main = Hauptformular. Somit haben diese DLL's auch Zugriff auf das Hauptformular der Anwendung, und können sich dort registrieren usw.
Übrigens Delphis IDE arbeitet genau nach diesem Prinzip. Erzeuge mal ein Package mit folgender Register procedure und installieren es mal in der Delphi IDE.

Delphi-Quellcode:
procedure Register;
begin
  Application.Title := 'Meine Delphi IDE';
end;
Gruß Hagen

Phoenix 16. Sep 2003 12:01

Re: Pointer auf Oberklassen
 
Ist das klasse...

Da bricht ja ein ganz neues Zeitalter in der Anpassung der IDE an :D


Alle Zeitangaben in WEZ +1. Es ist jetzt 05:05 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