Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Delphi DLL Programmierung - Wie übergebe ich am sinnvollsten meine Daten? (https://www.delphipraxis.net/190411-dll-programmierung-wie-uebergebe-ich-am-sinnvollsten-meine-daten.html)

Aviator 30. Sep 2016 15:07

DLL Programmierung - Wie übergebe ich am sinnvollsten meine Daten?
 
Hallo zusammen,

in letzter Zeit häufen sich die Threads die ich selber öffne. :shock:

Konkret geht es darum, dass ich gerade dabei bin ein komplett neues Projekt zu beginnen. In einem anderen Thread habe ich bereits geschrieben, dass es sich um ein selbstentwickeltes DMS handelt.

Ich möchte mein Programm in ein paar Teile aufsplitten und das ganze dann in eine DLL auslagern. Das ganze wird nicht nur aus Gründen der (hoffentlich) einfacheren Updatefähigkeit so gemacht sondern auch, weil ein anderes Programm auf die Daten meines DMS zugreifen soll.

Mein Problem besteht jetzt darin das ich nicht weiß, wie ich denn die Daten die meine DLL beschaffen soll (von einem SQL-Server) in meine bzw. die Fremde Anwendung "rüberreichen" kann/soll. Das ich hierzu ein Interface verwenden sollte ist schonmal klar. Für einen anderen einfacheren Fall habe ich auch bereits. Die Frage ist nur, wie baue ich das richtig auf. Das Problem ist ja schonmal die Übergabe von String die dann als PChar übergeben und dann in der Anwendung einem String zugewiesen werden müssen. Das ist so hoffentlich korrekt. :gruebel:

Um mal ein konkretes Beispiel zu nennen:
  • Meine Anwendung (das DMS) legt diverse Benutzer in einer Datenbank an
  • Mein Programm muss beim Start von einer DLL diese Benutzer wieder zurückerhalten um eine Auswahlform zu erzeugen bei der der Nutzer wählen kann, mit welchem Benutzernamen er sich anmelden will
  • Die andere fremde Anwendung soll das ebenfalls können

Ein Benutzerobjekt besteht aktuell aus den folgenden Teilen:
  • Benutzername
  • Vorname
  • Nachname
  • evtl. verschlüsseltes Passwort (wird aber wahrscheinlich in der DLL gegengeprüft und somit nicht an die eigene bzw. fremde Anwendung weitergegeben
  • E-Mail Adresse
  • evtl. Berechtigungen in Form eines Int64 Wertes


Wie muss ich jetzt mein Interface respektive meine DLL aufbauen, damit ich solche Informationen zurückgeben kann? Ein Objekt zwischen Anwendung und DLL zu tauschen ist ja nicht optimal habe ich mal hier gelesen. Wie funktioniert so etwas richtig und auf was muss ich achten? Bin für jeden Tipp dankbar.

Zacherl 30. Sep 2016 15:12

AW: DLL Programmierung - Wie übergebe ich am sinnvollsten meine Daten?
 
Zitat:

Zitat von Aviator (Beitrag 1349370)
Ein Objekt zwischen Anwendung und DLL zu tauschen ist ja nicht optimal

Stimmt, auf Objekte solltest du verzichten, aber Records stellen kein Problem dar. Insbesondere, wenn alle DLLs und Anwendungen in Delphi geschrieben sind und die selben Compileroptionen verwenden. Bezüglich Strings: Die gleiche Vorraussetzung annehmend, kannst du mitlerweile "glaube" ich sogar direkt Strings statt Pointer (PChar) verwenden. Ich meine gelesen zu haben, dass FastMM4 sowas selbstständig handhabt. Müsstest du am besten aber nochmal ausprobieren.

Aviator 30. Sep 2016 15:15

AW: DLL Programmierung - Wie übergebe ich am sinnvollsten meine Daten?
 
Zitat:

Zitat von Zacherl (Beitrag 1349371)
Zitat:

Zitat von Aviator (Beitrag 1349370)
Ein Objekt zwischen Anwendung und DLL zu tauschen ist ja nicht optimal

Stimmt, auf Objekte solltest du verzichten, aber Records stellen kein Problem dar. Insbesondere, wenn alle DLLs und Anwendungen in Delphi geschrieben sind und die selben Compileroptionen verwenden. Bezüglich Strings: Die gleiche Vorraussetzung annehmend, kannst du mitlerweile "glaube" ich sogar direkt Strings statt Pointer (PChar) verwenden. Ich meine gelesen zu haben, dass FastMM4 sowas selbstständig handhabt. Müsstest du am besten aber nochmal ausprobieren.

Also die Anwendungen sind beide in Delphi geschrieben. Bei der Übergabe von Records habe ich aber auch gelesen, dass es da Probleme gibt/geben kann. FastMM4 verwende ich nicht. Sollte man das denn? Bisher bin ich ganz gut ohne einen fremden MemoryManager klar gekommen.

Zacherl 30. Sep 2016 15:22

AW: DLL Programmierung - Wie übergebe ich am sinnvollsten meine Daten?
 
FastMM ist seit längerem der Standard MemoryManager von Delphi :) Ja, es war früher so, dass es Probleme geben konnte, wenn die Records Felder mit Referenzzähler enthalten bzw. Felder, die finalisiert werden müssen (dyn. Arrays, Strings, etc). Wie gesagt: Am besten mal ausprobieren. Records mit primitiven Datentypen machen auf jeden Fall zu 100% keine Probleme.

Aviator 30. Sep 2016 15:26

AW: DLL Programmierung - Wie übergebe ich am sinnvollsten meine Daten?
 
Achso ok. :oops:

Jetzt stelle ich mir nur die Frage, wie ich denn dann mein Interface aufbaue. Ich muss ja mehrere - in dem Fall dann Records - übergeben. Nämlich pro User der in der Datenbank angelegt wurde einen. Das wiederum wäre dann ja wieder ein Array of TUserData oder so.

Kann ich also bedenkenlos einen Record aufbauen in dem ein bzw. mehrere Strings enthalten sind? Wie stelle ich das am besten an?

Zacherl 30. Sep 2016 16:10

AW: DLL Programmierung - Wie übergebe ich am sinnvollsten meine Daten?
 
Also wenn du komplett sichergehen willst, nimmst du für String-Felder
Delphi-Quellcode:
ShortString
bzw.
Delphi-Quellcode:
array[0..n-1] of Char
. Hierbei ist es wichtig, dass die Größe statisch ist. Zum Zurückgeben eines Arrays mit dynamischer Anzahl von Elementen, kannst du dich an der Windows API orientieren. Die handhabt sowas in folgender Form:

Delphi-Quellcode:
type
  PRecord = ^TRecord;
  TRecord = record
    Field1: Integer;
    Field2: array[0..255] of Char;
  end;

function ReturnArrayOfRecords(Records: PRecord; Size: DWord; var NumberOfRecords: Integer): Boolean;
Auf der anderen Seite sieht das dann folgendermaßen aus:
Delphi-Quellcode:
var
  Records: PRecord;
  Size: DWord;
  I, NumberOfRecords: Integer;
begin
  // NumberOfRecords ermitteln
  ReturnArrayOfRecords(nil, 0, NumberOfRecords);
  Size := NumberOfRecords * SizeOf(TRecord);
  GetMem(Records, Size);
  try
    // Daten ermitteln
    if (ReturnArrayOfRecords(Records, Size, NumberOfRecords)) then
    begin
      for I := 0 to NumberOfRecords - 1 do
      begin
        // Arbeite mit Record #I
        Inc(Records);
      end;
    end;
  finally
    FreeMem(Records);
  end;
end;
Alternativ kannst du statt der Pointer hier auch ein dynamisches Array und
Delphi-Quellcode:
SetLength
verwenden.

Aviator 30. Sep 2016 16:33

AW: DLL Programmierung - Wie übergebe ich am sinnvollsten meine Daten?
 
Irgendwie stehe ich gerade auf dem Schlauch. Es ist nicht so, dass ich deine Antwort nicht verstehen würde, aber es hat noch nicht so richtig "klick" gemacht. :evil:

Ich hänge immer nochdabei, wie ich das auf mein Programm anwenden soll. Es kann doch nicht so schwer sein, eine DLL zu programmieren.

Wie binde ich denn eigentlich FastMM4 ein. Ein
Delphi-Quellcode:
ReportMemoryLeaksOnShutdown := True
wird es ja bestimmt nicht nur gewesen sein, oder? Eine Unit FastMM4 gibt es bei mir nicht. CnPack findet bei der Eingabe lediglich die Unit FastMMMemLeakMonitor. Aber so wie ich das gehört habe, gehört da ja noch ein bisschen mehr dazu, oder?

ReportMemoryLeaks... zeigt ja am Schluss auch nur eine MessageBox an bei der ich danach selbst auf die Suche nach dem Fehler gehen muss. Ich dachte FastMM4 wäre da etwas ... naja ... aussagekräftiger.

samso 30. Sep 2016 16:47

AW: DLL Programmierung - Wie übergebe ich am sinnvollsten meine Daten?
 
FastMM4 muss nicht mehr manuell eingebunden werden. Der ist immer eingebunden.

Aviator 30. Sep 2016 16:50

AW: DLL Programmierung - Wie übergebe ich am sinnvollsten meine Daten?
 
Zitat:

Zitat von samso (Beitrag 1349388)
FastMM4 muss nicht mehr manuell eingebunden werden. Der ist immer eingebunden.

Das heißt also, dass sich das alles nur im Hintergrund abspielt und ich davon nix merke? Und ReportMemoryLeaks... immer noch nur dieses Standardfenster ausgibt?

Ich dachte immer, dass FastMM viel mehr Informationen zur Verfügung stellen würde. Wenn ich mich recht entsinne gab es doch eine Inc Datei in der Einstellungen vorgenommen werden konnten. Verzeiht mir die vielleicht blöden Fragen, aber damit habe ich mich nie wirklich beschäftigt.

samso 30. Sep 2016 16:55

AW: DLL Programmierung - Wie übergebe ich am sinnvollsten meine Daten?
 
Zitat:

Zitat von Aviator (Beitrag 1349390)

Ich dachte immer, dass FastMM viel mehr Informationen zur Verfügung stellen würde. Wenn ich mich recht entsinne gab es doch eine Inc Datei in der Einstellungen vorgenommen werden konnten. Verzeiht mir die vielleicht blöden Fragen, aber damit habe ich mich nie wirklich beschäftigt.

Dazu muss man dann doch wieder Hand anlegen. "FullDebugMode" ist dazu das Stichwort. Aber das ist ja eigentlich nicht das Thema.

Aviator 30. Sep 2016 17:00

AW: DLL Programmierung - Wie übergebe ich am sinnvollsten meine Daten?
 
Zitat:

Zitat von samso (Beitrag 1349391)
Aber das ist ja eigentlich nicht das Thema.

Naja bedingt ja schon. Zacherl hat ja darauf hingewiesen, dass FastMM da ein Auge drauf hat. Natürlich würde ich dann auch ganz gerne wissen wie ich Fehler (auch im Bezug auf DLL Programmierung) finde wenn denn welche auftreten. Ich will natürlich jetzt nicht für alle möglichen kleinen Fragen eine extra Thread öffnen. Das kommt mir dann immer so unnötig viel / überflüssig vor.

samso 30. Sep 2016 17:04

AW: DLL Programmierung - Wie übergebe ich am sinnvollsten meine Daten?
 
Vielleicht hilft Dir das ja weiter:

http://www.delphipraxis.net/183846-f...nshutdown.html

und natürlich

Quelltext FastMM

Aviator 30. Sep 2016 20:45

AW: DLL Programmierung - Wie übergebe ich am sinnvollsten meine Daten?
 
Zitat:

Zitat von samso (Beitrag 1349393)
Vielleicht hilft Dir das ja weiter:

http://www.delphipraxis.net/183846-f...nshutdown.html

und natürlich

Quelltext FastMM

Danke. Werde ich mir mal anschauen.

Trotzdem bleibt für mich immer noch das Problem, dass ich noch nicht den 100%igen Durchblick habe, wie ich denn die DLL jetzt aufbauen muss. Kann da vielleicht mal jemand ein kleines Beispiel machen.

Pseudoabfrage von einem SQL-Server und Übergabe der Daten an mein Programm. Die Funktion wird dann logischerweise aus dem Programm heraus aufgerufen. Wäre sowas machbar?

jaenicke 1. Okt 2016 05:45

AW: DLL Programmierung - Wie übergebe ich am sinnvollsten meine Daten?
 
Also wir benutzen Interfaces. Das hat den enormen Vorteil, dass die reine DLL Schnittstelle sehr schlank sein kann. Wir haben eine Funktion in der DLL, die von der Anwendung aufgerufen wird nachdem die DLL geladen ist. Diese bekommt das Anwendungsinterface. Darüber meldet sich umgekehrt die DLL an der Anwendung an und übergibt ebenfalls ein Interface.

Darüber können nun Interfaces bei der Anwendung registriert oder angefordert werden.

Es gibt eine Klasse TApplicationInterface, die das ganze kapselt. Und dann geht das ganze direkt generisch. Zum Beispiel funktioniert dann das sowohl in DLL als auch in der Anwendung:
Delphi-Quellcode:
var
  Params: IInterfaceGetterParams;
  Data: IDataset;
begin
  Params := TDatabaseGetterParams.Create(TDatasetKind.Query, 'select * from foo');
  Data := TApplicationInterface.Get<IDataset>(Params);
  while not Data.Eof do
  ...
Interfaces kennen ja nun keine generischen Methodenparameter. Das funktioniert daher, indem die Klassenmethode Get die GUID des generischen Parameters ermittelt und über das Interface an der Hauptanwendung diese GUID und das Parameterinterface übergibt. Dort wird dann über die GUID die registrierte Factorymethode ermittelt, mit der das Interface geholt werden kann, und mit dem Parameterinterface aufgerufen.

Natürlich kann man bei wenigen Interfaces (bei uns sind es eben sehr viele) auch im Interface der Hauptanwendung direkt z.B. eine Methode GetDataset: IDataset usw. anlegen und braucht keine Generics dafür.

Aviator 1. Okt 2016 17:46

AW: DLL Programmierung - Wie übergebe ich am sinnvollsten meine Daten?
 
Also Interfaces würde ich ja auch benutzen. Das steht außer Frage. Nur stehe ich auf dem Schlauch, was das Abrufen von Ergebnissen vom SQL-Server und das Übergeben der Ergebnisse an die Anwendung angeht. Innerhalb meiner Anwendung würde ich die Ergebnisse gerne in Form eines Objektes zwischenspeichern. Um mal bei den Benutzern zu bleiben als Instanz bzw. mehrere Instanzen eines TUser Objektes.

Wäre es sinnvoll so etwas zu tun? Nur mal als Beispiel und hier im Editor runtergetippt.

Delphi-Quellcode:
IUserExchanger = interface(IInterface)
  procedure AddNewUser(const Username: string; FirstName: string; LastName: string);
  procedure AddUserGroup(const ID: Integer; const Name: string);
  [...]
end;
Die DLL Exportiert dann eine Funktion GetUsers oder wahlweise auch GetInstance die als Parameter eine Instanz eines IUserExchanger Objektes erwartet.
Delphi-Quellcode:
IUserInterface = interface(IInterface)
  procedure GetUsers(UserExchanger: IUserExchanger);
end;
Delphi-Quellcode:
TUserExchanger = class(TInterfacedObject, IUserExchanger)
private
  FUsers: TObjectList<TUser>;
  User: TUser;
public
  procedure AddNewUser(const Username: string; FirstName: string; LastName: string);
  procedure AddUserGroup(const ID: Integer; const Name: string);
end;

implementation

procedure TUserExchanger.AddNewUser(const Username: string; FirstName: string; LastName: string);
begin
  User := TUser.Create;
  User.UserName := UserName;
  User.FirstName := FirstName;
  User.LastName := LastName;
  FUsers.Add(User);
end;

procedure TUserExchanger.AddUserGroup(const ID: Integer; const Name: string);
begin
  User.Groups.Add(ID, Name);
end;
Nur mal so ein Gedanke. Falls da etwas unklar sein sollte, dann versuche ich gerne es etwas besser zu beschreiben.

Macht man so etwas oder gibt es da Nachteile? Laut Zacherl wäre es ja auch mittlerweile kein Problem mehr, Strings direkt auszutauschen. Beide Anwendungen die darauf zugreifen sind in Delphi geschrieben.

Wenn das doch nicht gehen sollte, würde dann so etwas funktionieren? Bitte auf das PChar achten. Das Interface habe ich hierfür nicht extra noch einmal geschrieben.

Delphi-Quellcode:
procedure TUserExchanger.AddUserGroup(const ID: Integer; const Name: PChar);
var
  LName: string;
begin
  LName := Name;
  User.Groups.Add(ID, LName);
end;

Aviator 4. Okt 2016 10:56

AW: DLL Programmierung - Wie übergebe ich am sinnvollsten meine Daten?
 
Hat hierzu noch jemand eine Idee? Kann man das so machen bzw. macht man das überhaupt so wie in meinem Beispiel gezeigt?

Wenn nicht, dann wäre es super wenn mir vielleicht jemand ein Beispiel machen könnte wie man so etwas am Elegantesten löst. Ich finde hierzu irgendwie keine passenden Informationen oder kann sie zumindest nicht auf meinen Fall anwenden.

Aviator 5. Okt 2016 12:10

AW: DLL Programmierung - Wie übergebe ich am sinnvollsten meine Daten?
 
Irgendwie kann ich mir nicht vorstellen, dass noch niemand eine DLL in Delphi geschrieben haben soll. :(

Da muss es doch einfache Möglichkeiten geben sowas zu machen.

Ich zähle nochmal kurz auf, was ich benötige:
  • Übergabe von Strings respektive PChar an die DLL (natürlich auch andere Datentypen wie Integer, Float und auch Records ... welche aber ja keine Probleme machen sollten (hoffentlich) wenn die Frage gelöst ist)
  • Rückgabe von Daten aus der DLL an die Anwendung
  • Bei der Rückgabe sollten eigentlich Objekte zurückgegeben werden, was bei einer DLL aber ja, wie ich es gelesen habe und es auch hier schon erwähnt wurde, Probleme macht; deshalb könnten Records verwendet werden
  • Wie verhalte ich mich, wenn ich mehrere Records (also Array of Record) zurückgeben müsste (dynamische Arrays)
  • Die Daten aus dem Record müsste ich dann in ein eigenes Objekt/Klasse übertragen wenn ich sie zurückbekommen habe

Alternativ, wie kann man es besser bzw. anders machen. Ich habe gefühlt jedes Tutorial welches etwas mit DLLs zu tun hat durch. Trotzdem bin ich in dem Punkt nicht wirklich schlauer.

Wäre super wenn vielleicht doch noch jemand einen Tipp für mich hat. Wenn etwas unklar sein sollte bei dem was ich benötige, dann fragt mich bitte. Ich bin über jede Antwort die mich weiterbringt dankbar. :|

taveuni 5. Okt 2016 12:22

AW: DLL Programmierung - Wie übergebe ich am sinnvollsten meine Daten?
 
Wenn eine Schnittstelle zu anderen Programmen zur Verfügung gestellt werden muss ist man mit (Delphi-) DLL's beschränkt. Deshalb arbeiten wir seit mehr als 10 Jahren mit RemObjects. Damit kannst Du Multitier Anwendungen erstellen und eine sehr komfortable IPC realisieren. Durch die Auswahl unzähliger Channels ist es möglich jedem eine Schnittstelle zu bieten und intern mit dem Objekten zu arbeiten (eher komfortable Records/Arrays). Du musst Dich nicht um Verbindungen, Reconnectes usw. kümmern. Ausserdem kann auch mit Events gearbeitet werden. Ist jetzt nicht direkt eine Antwort auf Deine DLL Frage. Einfach ein anderer Ansatz (der natürlich etwas kostet).

Aviator 5. Okt 2016 13:04

AW: DLL Programmierung - Wie übergebe ich am sinnvollsten meine Daten?
 
Hallo taveuni,

sieht ja auch interessant aus. Nur wollte ich das alles für den Anfang ein wenig kleiner halten. Bin leider auch nicht so ein Freund davon, direkt Komponenten für ein Vorhaben zu kaufen. So etwas sollte sich ja eigentlich problemlos mit DLLs lösen lassen. Wenn man es denn kann.

Interessant sieht das aus, keine Frage. Nur weiß ich eben auch nicht, ob dass das Richtige für mich/uns ist. Und der Preis ist ja auch nicht gerade gering (um es mal vorsichtig auszudrücken). :o

Eine Testversion halte ich da nicht für angebracht. Ich denke, dass ich da bestimmt länger bräuchte um mich da einzuarbeiten als es der Testzeitraum zulässt. Und bevor ich nachher merke, dass es doch nicht das Richtige ist ... aber danke für den Vorschlag.

Was gäbe es denn für Alternativen? Ich habe schon sehr viel von REST gehört, weiß aber nicht ob das für meinen Fall das Richtige wäre. Könnte man das dafür verwenden? Vorteil wäre ja auch, dass ich theoretisch die Grundlage für eine Mobile Anwendung geschaffen hätte. Ist das korrekt?

Fritzew 5. Okt 2016 13:33

AW: DLL Programmierung - Wie übergebe ich am sinnvollsten meine Daten?
 
Um auf Deine DLL Frage zurückzukommen:

In eine DLL Schnitstelle gehören nur einfache Typen,
also z.B

Integer,
Word,
Double,
Single,
PWideChar,
PAnsiChar etc..

Auf keinen Fall normale Strings.

Records auch nur aus diesen Typen

Das hat den Vorteil das man Compilerunabhängig ist.

Wir benutzen das heftig in mixed Projekten (Delphi, VCC, und sogar noch Fortran)

Für Records (Structs) gilt dasselbe

also
z.B Delphi :

Delphi-Quellcode:
const
 cwmaxnamesize = 30;
type
 tApi_Materialrec = packed record
    Name: array [0 .. cwmaxnamesize - 1] of Ansichar;
    Group: array [0 .. cwmaxnamesize - 1] of Ansichar;
    MatCode: array [0 .. cwmaxnamesize - 1] of Ansichar;
    Mate1, mate2, mate3, matg1, matg2, matgew: single; // Materialgewicht

    Mattyp: integer;
    list_matbez,
    mat_photoflag,
    mat_photo1,
    mat_photo2: integer;
  end;
//C++

Code:
// Ausrichtung an Byte-Grenzen wegen Delphi
#pragma pack(push,1)

struct CHA_API_API Api_mat
{
  char     name[30], Group[30], MatCode[30];
  float    mate1,mate2,mate3, matg1,matg2,matgew;
  int      mattyp;
  int      list_matbez;
  int      photoflag, Photo1, Photo2;
};
Wenn Wir Interfaces benutzen gilt dasselbe.

einziger Unterschied ist das wir in Interface auch schon mal WideString benutzen,
da dort das Speichermanagement von Windows grift.

Funktionen geben niemals Records zurück sondern sind bei uns immer aufgebaut nach dem Schema:


Delphi-Quellcode:
function getMatDataforId(aid : Integer; var matrec : tApi_Materialrec) : Bool;
Das funktioniert bei uns soweit problemlos.

Aviator 5. Okt 2016 13:53

AW: DLL Programmierung - Wie übergebe ich am sinnvollsten meine Daten?
 
Hallo Fritzew,

danke für die Antwort. Das mit den Strings war mir bewusst. Nach den etlichen Tutorials die ich durchgelesen habe umso mehr. :-D

Zitat:

Zitat von Fritzew (Beitrag 1349845)
Delphi-Quellcode:
const
 cwmaxnamesize = 30;
type
 tApi_Materialrec = packed record
    Name: array [0 .. cwmaxnamesize - 1] of Ansichar;
    Group: array [0 .. cwmaxnamesize - 1] of Ansichar;
    MatCode: array [0 .. cwmaxnamesize - 1] of Ansichar;
    Mate1, mate2, mate3, matg1, matg2, matgew: single; // Materialgewicht

    Mattyp: integer;
    list_matbez,
    mat_photoflag,
    mat_photo1,
    mat_photo2: integer;
  end;
Funktionen geben niemals Records zurück sondern sind bei uns immer aufgebaut nach dem Schema:


Delphi-Quellcode:
function getMatDataforId(aid : Integer; var matrec : tApi_Materialrec) : Bool;
Das funktioniert bei uns soweit problemlos.

Die Funktion hätte ich auch mit einem Var Parameter aufgebaut, da ich das so auch aus der WinAPI kenne.

Wie wird denn jetzt die von dir beschriebene Funktion aufgerufen. Muss das dann auch zwingend ein Array of AnsiChar sein? Kann man da nicht einfach eine Variable vom Typ PChar nehmen? Und wenn ja, wie läuft der Aufruf dann ab sofern sich da was ändern sollte. Meine Ergebnisse können eine unterschiedliche Länge haben die ich vorher nicht kenne. Infolge dessen kann ich auch keine festen Arrays nehmen.

himitsu 5. Okt 2016 14:00

AW: DLL Programmierung - Wie übergebe ich am sinnvollsten meine Daten?
 
Delphi-Quellcode:
packed record
müssen es nicht unbedingt sein, aber man sollte dann wenigstens explizit die Speicherausrichtung definieren.
z.B.
Delphi-Quellcode:
type
  {$ALIGN 4}  // das gilt ab hier und nur bis zum Unit-Ende (oder bis zum nächsten $ALIGN in der Unit)
  TMyRecord = record
    ...
  end;
Das Selbe gibt für ENUMs, die man vielleicht gleich auf Integer festlegen sollte, da in vielen C-Sprachen die auch immer 4 Byte sind.
Delphi-Quellcode:
{$MINENUMSIZE 4}

In Delphi ist das Minimum standardmäßig 1 Byte, wenn der Enum maximal 256 Werte besitzt.

Fritzew 5. Okt 2016 14:01

AW: DLL Programmierung - Wie übergebe ich am sinnvollsten meine Daten?
 
Wenn Du mit unterschiedlichen Datenlängen hantieren willst muss Sichergestellt werden wem der Speicher gehört.
Also brauchst Du dann zusätzliche Funktionen um den Speicher zu verwalten.

Ähnlich wie es ja an vielen Stellen in der WINAPI gemacht wird.

entweder vorher nachfragen wieviel Speicher benötigt wird
oder eine Funktion um allozierten Speicher wieder freizugeben.

Wobei immer die alte Regel gilt das nur der Freigibt der auch angefordert hat.

Aviator 5. Okt 2016 14:10

AW: DLL Programmierung - Wie übergebe ich am sinnvollsten meine Daten?
 
Zitat:

Zitat von Fritzew (Beitrag 1349853)
Wenn Du mit unterschiedlichen Datenlängen hantieren willst muss Sichergestellt werden wem der Speicher gehört.
Also brauchst Du dann zusätzliche Funktionen um den Speicher zu verwalten.

Ähnlich wie es ja an vielen Stellen in der WINAPI gemacht wird.

entweder vorher nachfragen wieviel Speicher benötigt wird
oder eine Funktion um allozierten Speicher wieder freizugeben.

Wobei immer die alte Regel gilt das nur der Freigibt der auch angefordert hat.

Okay. Habe zwar bisher wenig mit der WinAPI gearbeitet, aber das Prinzip habe ich verstanden. Allerdings hänge ich bei der Umsetzung noch ein wenig.

Wärst du so nett und würdest mir vielleicht ein Minimalbeispiel anhängen wie das dann in der Anwendung und der DLL auszusehen hat?

Ich denke die Lösung mit dem Anfragen wie viel Speicher benötigt wird ist die "bessere", da sie an die WinAPI angelehnt ist. Oder siehst du das anders?

[EDIT]Vergessen zu erwähnen. Ein Record müsste bei mir in etwa so aussehen (könnte auch noch erweitert werden):

Delphi-Quellcode:
TDocumentData = record
  DocumentID: Int64; // Kann man den benutzen oder ist man da auf Integer beschränkt? Wäre sehr schlecht.
  DocumentName: PChar; // Normalerweise String (der wäre theoretisch auf 255 Zeichen begrenzt, könnte aber auch erweitert werden)
  DocumentDescription: PChar; // Hier steht eine Beschreibung die evtl. sogar RichText beinhalten soll
  Deleted: Boolean; // Der sollte ja keine Probleme machen, oder?
end;
[/EDIT]

Fritzew 5. Okt 2016 14:51

AW: DLL Programmierung - Wie übergebe ich am sinnvollsten meine Daten?
 
Delphi-Quellcode:
unit Unit10;

interface
  uses
   sysutils;
type
 {$ALIGN 4}
 TDocumentData = record
  DocumentID: Int64; // Kann man den benutzen oder ist man da auf Integer beschränkt? Wäre sehr schlecht.
  DocumentName: PChar; // Normalerweise String (der wäre theoretisch auf 255 Zeichen begrenzt, könnte aber auch erweitert werden)
  DocumentDescription: PChar; // Hier steht eine Beschreibung die evtl. sogar RichText beinhalten soll
  Deleted: Boolean; // Der sollte ja keine Probleme machen, oder?
end;


 function getDocumentData(const aId : Int64; var aDocument : TDocumentData): LongBool;
 procedure freeDocumentData(var aDocument : TDocumentData);

implementation

  function getDocumentData(const aId : Int64; var aDocument : TDocumentData): LongBool;
  begin
   result := false;

   // Pick up the Data
    if True then
    begin
      fillchar(aDocument, sizeof(aDocument), 0);
      aDocument.DocumentID := aId;
      // reserve memory
      aDocument.DocumentName := Strnew('Documentname');
      aDocument.DocumentDescription := StrNew('DocumentDescription');
      result := true;
    end;
  end;


  procedure freeDocumentData(var aDocument : TDocumentData);
  begin
    if aDocument.DocumentName <> nil then StrDispose(aDocument.DocumentName);
    if aDocument.DocumentDescription <> nil then StrDispose(aDocument.DocumentDescription);
    fillchar(aDocument, sizeof(aDocument), 0);
  end;


end.

Benutzung :



Delphi-Quellcode:
procedure Test;
 var data : TDocumentData;
 begin

  if getDocumentData(1, data) then
  begin
     //Use Data
     freeDocumentData(data);
  end;


 end;

Einfach mal so hingeschrieben aber du siehst den Flow denke ich

Aviator 5. Okt 2016 15:23

AW: DLL Programmierung - Wie übergebe ich am sinnvollsten meine Daten?
 
Zitat:

Zitat von Fritzew (Beitrag 1349866)
Einfach mal so hingeschrieben aber du siehst den Flow denke ich

Also ich sehe wie du das meinst. Vielen Dank! :thumb:

Du bist jetzt aber von der anderen Variante ausgegangen, dass die Funktionen in der DLL den Speicher reservieren und auch wieder freigeben. Ist das denn der bessere Weg?

:?: Wie sieht es mit meinen Anmerkungen bzgl. Int64 aus? Kann man den denn uneingeschränkt benutzen? Weil den Datentyp würde ich sehr häufig brauchen. :?:

Fritzew 5. Okt 2016 15:38

AW: DLL Programmierung - Wie übergebe ich am sinnvollsten meine Daten?
 
Zitat:

Du bist jetzt aber von der anderen Variante ausgegangen, dass die Funktionen in der DLL den Speicher reservieren und auch wieder freigeben. Ist das denn der bessere Weg?

:?: Wie sieht es mit meinen Anmerkungen bzgl. Int64 aus? Kann man den denn uneingeschränkt benutzen? Weil den Datentyp würde ich sehr häufig brauchen. :?:
Wir benutzen meistens diesen Weg einfach weil es für uns der bessere Flow ist.
Meistens ist es einfacher so. Aber ich denke das bleibt jedem selber überlassen. Sollte aber wohl am besten konsistent sein.

Und Int64 benutzen wir auch viel macht keine Probleme

Aviator 5. Okt 2016 15:48

AW: DLL Programmierung - Wie übergebe ich am sinnvollsten meine Daten?
 
Zitat:

Zitat von Fritzew (Beitrag 1349874)
Wir benutzen meistens diesen Weg einfach weil es für uns der bessere Flow ist.
Meistens ist es einfacher so. Aber ich denke das bleibt jedem selber überlassen. Sollte aber wohl am besten konsistent sein.

Und Int64 benutzen wir auch viel macht keine Probleme

Super danke dir! Ich werde das jetzt mal so ausprobieren. :coder2: :coder:

Wenn ich Fragen habe, dann melde ich mich nochmal hier. Aber jetzt habe ich auf jeden Fall schon mal einen Ansatz wie ich das lösen kann. Jetzt muss es nur noch so funktionieren wie ich mir das vorstelle. Dann wäre es perfekt.

Nochmal ein dickes Danke! :thumb: :thumb: :thumb:

Rollo62 6. Okt 2016 06:35

AW: DLL Programmierung - Wie übergebe ich am sinnvollsten meine Daten?
 
Könnte sein das es auch mit Messages geht:
http://docwiki.embarcadero.com/RADSt...en_mit_der_RTL

Hab mal sowas läuten hören, aber nicht ausprobiert ...

Rollo

Aviator 10. Okt 2016 15:56

AW: DLL Programmierung - Wie übergebe ich am sinnvollsten meine Daten?
 
Hallo zusammen,

habe jetzt schon ein gutes Stück meiner DLL fertig. Wie von Fritzew vorgeschlagen habe ich mir auch eine FreeAllocatedMemory() Procedure geschrieben die den angeforderten Speicher wieder freigibt, wenn die Informationen angekommen und zwischengespeichert sind. Funktioniert nach ein paar Tests so wie gewünscht. Ich hoffe es bleibt auch dabei. :cyclops:

Delphi-Quellcode:
procedure TDMSUserManager.FreeAllocatedMemory(var UserExchange: array of TUserExchange);
var
  i: Integer;
begin
  for i := Low(UserExchange) to High(UserExchange) do begin
    if Assigned(UserExchange[i].UserName) then
      StrDispose(UserExchange[i].UserName);

    if Assigned(UserExchange[i].FirstName) then
      StrDispose(UserExchange[i].FirstName);

    if Assigned(UserExchange[i].LastName) then
      StrDispose(UserExchange[i].LastName);

    if Assigned(UserExchange[i].EMail) then
      StrDispose(UserExchange[i].EMail);

    if Assigned(UserExchange[i].Password) then
      StrDispose(UserExchange[i].Password);
  end;
end;
Jetzt wäre die Frage, ob es hier eine einfache Möglichkeit gibt, alle PChars automatisch freizugeben?!

Funktioniert da so etwas wie
Delphi-Quellcode:
Finalize()
? Habe es jetzt nicht ausprobiert da ich nicht weiß, ob das unter Umständen später zu Problemen führen kann. Falls ich mal meinen Record erweitern möchte (muss ja nicht dieser hier sein), dann müsste ich immer daran denken, die Strings auch wieder freizugeben. Sollte zwar in einem Zug mit dem Anfordern passieren, aber man vergisst ja gerne mal was.

Fritzew 10. Okt 2016 16:09

AW: DLL Programmierung - Wie übergebe ich am sinnvollsten meine Daten?
 
Nun ja die Frage ist halt immer, muss es dynamisch sein?

Ansonsten Die Records fest deklarieren:
in etwa

Delphi-Quellcode:
teventrec = record
 Size : integer; // Muss gesetzt werden und entspricht sizeof(teventrec)
 matname : Array[0..99] of wchar; // 100 Zeichen
 x, y : Double;
 nochn_int : integer;
 matText : Array[0..199] of wchar; // 200 Zeichen
end;
So sparst Du Dir das freigeben und durch das Sizefeld kann auf beiden Seiten geprüft werden ob das alles noch zusammenpasst

Aviator 10. Okt 2016 16:14

AW: DLL Programmierung - Wie übergebe ich am sinnvollsten meine Daten?
 
Zitat:

Zitat von Fritzew (Beitrag 1350393)
Nun ja die Frage ist halt immer, muss es dynamisch sein?

Hmm da hast du Recht. FirstName, LastName, E-Mail und Password haben in der Datenbank eine feste Länge von jeweils 50 oder 60 Zeichen. Die könnte ich mit einer festen Länge deklarieren.

Aber was mache ich mit dynamischen Längen? Die werden früher oder später auf jeden Fall kommen. Vielleicht nicht bei einem User aber bei bei einem Dokument. Oder geht es nur mit
Delphi-Quellcode:
StrDispose()
?

Fritzew 10. Okt 2016 16:47

AW: DLL Programmierung - Wie übergebe ich am sinnvollsten meine Daten?
 
Code:
Aber was mache ich mit dynamischen Längen? Die werden früher oder später auf jeden Fall kommen. Vielleicht nicht bei einem User aber bei bei einem Dokument.
Was Du machen kannst solange das alles bei Windows bleibt:

Verwende WideString in der Schnittstelle, der wird von Windows verwaltet.

Also anstatt Array[xx] of widechar ein
Name : Widestring;

Ist auf Delphi Seite sehr schön zu verwenden bei C++ etwas aufwendiger AKA BSTR* etc..

oder ein ganz anderer Ansatz übergebe Functions Pointer also so etwas wie

Delphi-Quellcode:
Type
  tAddDocument = function(ID : int64; const Value : PWidechar) : boolean;
in der Dll dann :

Delphi-Quellcode:
procedure FillDocument(ID : int64; afunc : TaddDocument);
  begin
    // Sehr vereinfacht
   if db.findid(id)
   afunc(id, db.getDocumantData(id));
  end;


in der exe:

Delphi-Quellcode:
 // Call to Dll
   FillDocument(ID, @myAddFunc);

himitsu 10. Okt 2016 17:44

AW: DLL Programmierung - Wie übergebe ich am sinnvollsten meine Daten?
 
Statt PWideChar einfach WideString verwenden.

WideString ist eine "Umleitung" (Kapselung) auf einen OLE-String (der OleAut32.dll), den eigentlich jede Programmiersprache versteht und der auch von Windows bei vielen Schnittstellen Verwendung findet.
MSDN-Library durchsuchenSysAllocStringLen MSDN-Library durchsuchenSysFreeString MSDN-Library durchsuchenSysStringLen
Die Speicherverwalung wird dann von der OLE32 vorgenommen, womit also Einer den String erstellen und der Andere ihn wieder freigeben kann, da beide Seiten den selben Speichermanager verwenden.

Wenn diese DLL in der Delphi-Welt bleibt, also nur bei Delphi-Anwendungen eingebunden wird, dann kann man auch Delphi-Strings (AnsiString/UnicodeString) verwenden, wenn man SimpleShareMem (früher ShareMem) verwendet.

Blup 11. Okt 2016 10:13

AW: DLL Programmierung - Wie übergebe ich am sinnvollsten meine Daten?
 
Ich würde an dieser Stelle auch nicht mehr mit Records arbeiten.
Wenn schon Interface, dann auch für die Datenobjekte.
Delphi-Quellcode:
IEventObject = interface(IInterface)
  [GUID]
  function GetMatName: WideString;
  function GetX: Double;
  function GetY: Double;
  function GetNochn_Int: Integer;
  function GetMatText: WideString;
  property MatName: WideString read GetMatName;
  property X: Double read GetX;
  property Y: Double read GetY
  property Nochn_Int: Integer read GetNochn_Int;
  property MatText: WideString read GetMatText;
end;

TEventObject = class(TInterfacedObject, IEventObject)
private
  FMatName: WideString;
  FX: Double;
  FY: Double;
  FNochn_Int: Integer;
  FMatText: WideString;
protected
  function GetMatName: WideString;
  function GetX: Double;
  function GetY: Double;
  function GetNochn_Int: Integer;
  function GetMatText: WideString;
public
  {...}
end;


function GetEventObject(ID: Int64): IEventObject;

Aviator 11. Okt 2016 15:44

AW: DLL Programmierung - Wie übergebe ich am sinnvollsten meine Daten?
 
Zitat:

Zitat von Blup (Beitrag 1350450)
Ich würde an dieser Stelle auch nicht mehr mit Records arbeiten.
Wenn schon Interface, dann auch für die Datenobjekte.

Das muss ich mir mal anschauen. Records sind in dem Fall zwar einfacher zu handlen wenn ich ein Array of Record benutze, aber Interfaces sind auch ganz schön. Dann müsste ich natürlich für jeden User eine eigene Interface Instanz erzeugen. :gruebel:

Wie gesagt, schaue ich mir mal an ob ich das so umsetzen kann. Wenn ich Rückfragen dazu habe, dann melde ich mich. :zwinker:

:coder: :coder: :coder:

Aviator 11. Okt 2016 18:01

AW: DLL Programmierung - Wie übergebe ich am sinnvollsten meine Daten?
 
Hallo Blup (und alle anderen Helfer),

da bin ich schon wieder. Funktioniert doch nicht so ganz wie ich das will. :roll:

Ich habe beim Erstellen des Interfaces Probleme mit den Datentypen. Ich wollte jetzt bei
Delphi-Quellcode:
PWideChar
bleiben und nicht wieder alles auf
Delphi-Quellcode:
WideString
o.ä. umstellen. Ich habe mir jetzt ein Interface erstellt, das wie folgt aussieht:

Delphi-Quellcode:
  IDMSUserExchange = interface(IInterface)
  [GUID]
    procedure SetChangePassAtNextLogon(const Value: Boolean);
    procedure SetDeactivated(const Value: Boolean);
    procedure SetEMail(const Value: PWideChar);
    procedure SetFirstName(const Value: PWideChar);
    procedure SetID(const Value: Int64);
    procedure SetLastName(const Value: PWideChar);
    procedure SetPassword(const Value: PWideChar);
    procedure SetPermissions(const Value: Int64);
    procedure SetUserName(const Value: PWideChar);

    function GetChangePassAtNextLogon: Boolean;
    function GetDeactivated: Boolean;
    function GetEMail: PWideChar;
    function GetFirstName: PWideChar;
    function GetID: Int64;
    function GetLastName: PWideChar;
    function GetPassword: PWideChar;
    function GetPermissions: Int64;
    function GetUserName: PWideChar;

    property ID: Int64 read GetID write SetID;
    property UserName: PWideChar read GetUserName write SetUserName;
    property FirstName: PWideChar read GetFirstName write SetFirstName;
    property LastName: PWideChar read GetLastName write SetLastName;
    property EMail: PWideChar read GetEMail write SetEMail;
    property Password: PWideChar read GetPassword write SetPassword;
    property Permissions: Int64 read GetPermissions write SetPermissions;
    property Deactivated: Boolean read GetDeactivated write SetDeactivated;
    property ChangePassAtNextLogon: Boolean read GetChangePassAtNextLogon write SetChangePassAtNextLogon;
  end;
Sobald ich jetzt aber eine Klasse erstelle welches das Interface implementiert, muss ich ja auch entsprechend mit
Delphi-Quellcode:
PWideChar
arbeiten. Innerhalb meines Programmes, also wenn die Daten von der DLL zurückgegeben wurden, würde ich aber ganz gerne mit
Delphi-Quellcode:
string
weiterarbeiten. Jetzt müsste ich mir ja theoretisch 2 Klassen erzeugen die das gleiche Interface implementieren und sich nur durch die internen Datentypen der privaten Felder unterscheiden würden.

Klasse, welche zum Austausch der Daten mit der DLL benutzt wird (da kein string nutzbar).
Delphi-Quellcode:
TDMSUserExchange = class(TInterfacedObject, IDMSUserExchange)
private
  FUserName: PWideChar;
  FEMail: PWideChar;
  [...]
  FDeactivated: Boolean;
public
  [Alle Interface Proceduren]
end;
Interne Klasse die zur Weiterverarbeitung benutzt werden soll:
Delphi-Quellcode:
TDMSInternalUserData = class(TInterfacedObject, IDMSUserExchange)
private
  FUserName: string; // <-- Hier string
  FEMail: string;    // <-- Hier string
  [...]
  FDeactivated: Boolean;
public
  [Alle Interface Proceduren]
end;
Ist das jetzt übers Ziel hinaus geschossen oder muss das genau so gemacht werden? Wenn nicht, wie macht man das richtig? Dann müsste ja theoretisch wie es aktuell mit den Records auch der Fall ist die Objekte von
Delphi-Quellcode:
TDMSUserExchange
nach
Delphi-Quellcode:
TDMSInternalUserData
übertragen werden.

Wenn ich Speicher mit
Delphi-Quellcode:
StrNew()
anfordern muss, dann kann ich den ja nicht einfach wieder freigeben wenn ich intern Strings verwende. Oder verstehe ich das falsch?
Oder brauche ich gar keinen Speicher mehr anzufordern, weil mein Interface zwar
Delphi-Quellcode:
PWideChar
erwartet, aber in der instanziierten Klasse ein
Delphi-Quellcode:
string
verwendet wird und somit schon wieder alles in einen eigenen Speicherbereich "kopiert" wird? (Das wäre mir persönlich am allerliebsten) :-D

Ich hoffe ihr habt meine Frage(n) verstanden und könnt mir nochmal weiterhelfen. :|

Ghostwalker 11. Okt 2016 19:10

AW: DLL Programmierung - Wie übergebe ich am sinnvollsten meine Daten?
 
Ähmmm....ich bin zwar schon einige Zeit raus aus Delphi (fang grad wieder damit an), aber ist das nicht etwas kompliziert und aufwendig ?

Ich würde banalerweise einfach eine Unit machen, die die entsprechenden Record deklarationen enthält,
die sowohl in der DLL, als auch im Programm mit einbinden und dann das ganze ist gelutscht ?


Beispiel:

Code:
Unit SharedTypes;

INTERFACE

TYPE

   pUser = ^TUser;
   TUser = record
              Username : widestring;
              Loginname: widerstring;
              {und was halt noch so rein soll} 
           end
Die Funktionen der DLL geben einfach pUser als Rückgabewert zurück. Und damit kannst du ganz
normal im Hauptprogramm arbeiten....


oder überseh ich da was ?

Aviator 11. Okt 2016 19:35

AW: DLL Programmierung - Wie übergebe ich am sinnvollsten meine Daten?
 
So habe ich es ja aktuell. Nur Blup meinte ja das, wenn man schon mit Interfaces arbeitet, auch gleich alles mit Interfaces machen sollte. Um das mal vereinfacht auszudrücken. So wollte ich das dann machen.

Aktuell habe ich ja alles mit Records aufgebaut. Das funktioniert auch.

Nur würde mich jetzt interessieren wie es mit Interfaces funktioniert wenn ich schon den Vorschlag gemacht bekomme. So ganz habe ich das System noch nicht durchschaut.

Blup 12. Okt 2016 09:49

AW: DLL Programmierung - Wie übergebe ich am sinnvollsten meine Daten?
 
Mir ist nicht klar warum du mit PWideChar arbeiten willst, aber nicht mit WideString.
In neueren Delphiversionen ist String = UnicodeString definiert. Einer direkten Zuweisung im Setter oder Getter steht da nichts im Wege.
Um die Speicherverwaltung und Typkonvertierung sollte sich Delphi doch eigentlich selbst kümmern?
Delphi-Quellcode:
  IDMSUserExchange = interface(IInterface)
  [GUID]
    function GetUserName: WideString;
    procedure SetUserName(const AValue: WideString);
    property UserName: WideString read GetUserName write SetUserName;
  end;

   TDMSInternalUserData = class(TInterfacedObject, IDMSUserExchange)
   private
     FUserName: string; // <-- Hier string
   protected
{$REGION 'IDMSUserExchange'}
     function GetUserName: WideString;
     procedure SetUserName(const AValue: WideString);
{$ENDREGION}
   end;

implementation
   
function TDMSInternalUserData.GetUserName: WideString;
begin
  Result := FUserName;
end;
 
procedure TDMSInternalUserData.SetUserName(const AValue: WideString);
begin
  FUserName := AValue;
end;

procedure TuWasMitUserData(AUserData: IDMSUserExchange; const AValue: string);
begin
  AUserData.UserName := AValue;
end;
Wenn die Arbeit mit Objekten sein muss, kannst du aber für die Objekte Properties definieren, die andere Typen zur Verfügung stellen als das Interface.
Ich sehe dafür aber keine Notwendigkeit.
Delphi-Quellcode:
   TDMSInternalUserData = class(TInterfacedObject, IDMSUserExchange)
   private
     FUserName: string; // <-- Hier string
   protected
{$REGION 'IDMSUserExchange'}
     function GetUserName: WideString;
     procedure SetUserName(const AValue: WideString);
{$ENDREGION}
   public
     property UserName: string read FUserName write FUserName;
   end;


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