Delphi-Version: XE5
DLL Exportiert ein Interface mit Strings...
Hallo!
Wir alle kennen den Kommentar...
Delphi-Quellcode:
{ Wichtiger Hinweis zur DLL-Speicherverwaltung: ShareMem muss die erste...
Das gilt wahrscheinlich auch für ein Interface, oder?
Delphi-Quellcode:
Mavarik
type
IFOO = Interface ['{BC0B635C-5296-4316-9559-0E8E9A32D460}'] Procedure Test(Parameter:String); End; Procedure GetI(out IOUT : IFOO);stdcall; begin ... end; Exports GetI; |
AW: DLL Exportiert ein Interface mit Strings...
Ja, es sein denn du nimmst
Delphi-Quellcode:
.
WideString
Und alle Methoden des Interfaces sollten auch mit
Delphi-Quellcode:
oder whatever (Calling Convention) gekennzeichnet werden (sonst geht das nur mit Delphi)
stdcall
|
AW: DLL Exportiert ein Interface mit Strings...
Zitat:
|
AW: DLL Exportiert ein Interface mit Strings...
Zitat:
Wenn du einen Typen verwendest, der nur von Delphi gemanaged wird (z.B.
Delphi-Quellcode:
) dann musst du
string
Delphi-Quellcode:
verwenden.
ShareMem
Nimmst du stattdessen einen Typen der nicht von Delphi gemanaged wird (statt
Delphi-Quellcode:
eben
string
Delphi-Quellcode:
) dann brauchst du eben kein
WideString
Delphi-Quellcode:
.
ShareMem
|
AW: DLL Exportiert ein Interface mit Strings...
String, AnsiString und UnicodeString sind Typen, welche über den Speichermanager des Delphi verwaltet werden.
WideString wird über die OleAut32.dll verwaltet. Und der ShortString ist ein Record und wird von keinem Speichermanager verwaltet, da er meistens direkt auf dem Stack liegt. Alles was über den Delphi-Speichermanager verwaltet wird, muß z.B. via ShareMem verbunden werden. Und wenn man z.B. Klassen übergeben will, oder andersweilig Typen teilt, dann muß auch noch die RTTI geshared werden -> BPLs. |
AW: DLL Exportiert ein Interface mit Strings...
OK Verstanden...
Habe gerade ein Testprojekt für die DLL geschrieben und habe in meinen Interface eine Funktion die einen String zurück gibt. Test funktioniert... OHNE SHAREMEM und mit "normalen" String typen. Zufall? Oder habe ich doch noch etwas nicht verstanden... Mavarik |
AW: DLL Exportiert ein Interface mit Strings...
Geplanter Zufall.
Probleme gibt es vorallem dann, wenn die Stringreferenzen auf der "falschen" Seite verändert, kopiert oder längere Zeit gespeichert werden. Beispiel: Erzeug dir einen String, z.B. über IntToStr (Achtung, es darf keine Konstante sein), übergib ihn einer Interface-Methode und speichere ihn auf der anderen Seite in einer globalen Variable und nun beende dein Programm oder entlade zumindestens die DLL. |
AW: DLL Exportiert ein Interface mit Strings...
Vor allem zwischen einer Delphi-erzeugten DLL und einer Delphi-Anwendung ticken auch die Uhren "synchroner" und trotz "nicht korrekter" Implementierung der DLL funktioniert es trotzdem irgendwie (mehr durch Zufall).
Sobald aber eine nicht Delphi-Anwendung diese DLL benutzt, dann rappelt es im Karton. |
AW: DLL Exportiert ein Interface mit Strings...
Zitat:
Die Frage ist dann unter welchen Umständen kann man das so machen... Ich muss eine Unit von einem Dritthertseller (nur DCU vorhanden) in eine DLL Kapseln, damit ich diese auch mit zukünftigen Delphi Version benutzen kann. Beispiel:
Delphi-Quellcode:
Also eigentlich immer nur Strings mal eben weiterreichen, verarbeiten und zurück geben.
Bla.SavetoFile(Filename); // String übergeben
S := Bla.GetStr(100); // String zurück erhalten Kann man das "Riskieren" oder doch lieber auf WideString gehen? Mavarik |
AW: DLL Exportiert ein Interface mit Strings...
Hauptproblem wird wohl sein, dass die DCU nicht für Unicode funktioniert.
Wenn du die Definition der Klasse kennst, dann erstelle dir diese Klasse neu (für die neuen Versionen). Die Klasse selber holt sich eine Interface-Instanz von der DLL und reicht alle Anfragen darüber an die DLL durch und holt auch die Daten darüber zurück. Am sinnvollsten wäre auch die Verwendung von
Delphi-Quellcode:
statt
PAnsiChar
Delphi-Quellcode:
und die neue Klasse benutzt als Parameter
WideString
Delphi-Quellcode:
statt
AnsiString
Delphi-Quellcode:
.
string
|
AW: DLL Exportiert ein Interface mit Strings...
Zitat:
|
AW: DLL Exportiert ein Interface mit Strings...
Zitat:
Mit Umstellung auf Unicode wurde das AnsiString erweitert und ist daher nicht mehr wirkltich kompatibel zueinander. Die DLL mit einer alten Delphiversion kompiliert, kommt mit der alten DCU klar, aber das Interface, welches die DLL veröffentlicht sollte daher maximal WideString oder PAnsiChar verwenden. |
AW: DLL Exportiert ein Interface mit Strings...
OK..
Was ist mit einem TStream? Mavarik |
AW: DLL Exportiert ein Interface mit Strings...
Das ist eine Klasse? Siehe oben bei RTTI.
Und TMemoryStream, TStringStream, TBytesStream usw. geht auch wieder auf SharedMem hinaus. Es gibt allerdings den IStream, aber der ist nicht kompatibel mit TStream's, wobei es dafür aber eine Wrapper-Klasse gibt. |
AW: DLL Exportiert ein Interface mit Strings...
Zitat:
|
AW: DLL Exportiert ein Interface mit Strings...
TStreamAdapter
|
AW: DLL Exportiert ein Interface mit Strings...
Zitat:
Zitat:
ok dann nochmal: Danke für die Info zum IStream. Die Wrapper Klasse hier für habe ich gefunden. Hast Du ein Stück Beispiel-Code, wie man das für eine bidirektionale Übergabe an eine DLL verwenden muss? Grüsse Mavarik |
AW: DLL Exportiert ein Interface mit Strings...
Eigentlich Recht einfach.
- TStream-Nachfolger können nicht direkt an IStream übergeben werden. > TStream ist halt ein Delphi-Typ und ISteam kommt irgendwo aus dem OLE32, oder so. > Gut, man hätte natürlich auf die Idee kommen können, die Methoden von IStream direkt in TStream einzubauen, damit man auch direkt darüber das IStream hätte supporten können, aber so schlau war wohl Keiner. :stupid: Man nimmt also irgendeinen Stream, packt ihn in den TStreamAdapter und überlegt sich dann noch, wer für die Freigabe sorgen soll.
Delphi-Quellcode:
type
IMyIntf = interface function GetStream: IStream; procedure PutStream(S: IStream); end; function TMyClass.GetStream: IStream; begin Result := TStreamAdapter.Create(FStream, soReference); end; function TMyClass.GetStream: IStream; begin Result := TStreamAdapter.Create(TFileStream.Create('Test.dat', 0), soOwned); end; begin MyIntf.PutStream(TStreamAdapter.Create(FStream, soReference)); end; begin MyIntf.PutStream(TStreamAdapter.Create(TFileStream.Create('Test.dat', 0), soOwned)); end; |
AW: DLL Exportiert ein Interface mit Strings...
Zitat:
Delphi-Quellcode:
Wenn die DLL_MachWas mit den Streams arbeitet? Wie kommt der Inhalt wieder zurück in A & B
Procedure Machwas(A,B:TStream);
var StreamA, StreamB : IStream; begin StreamA := TStreamAdapter.Create(A,soReference); StreamB := TStreamAdapter.Create(B,soReference); DLL_MachWas(StreamA,StreamB); // Soweit richtig? // Und dann? end; oder arbeitet die DLL dann auf A & B oder einer Kopie? Mavarik |
AW: DLL Exportiert ein Interface mit Strings...
Das bleibt die ganze Zeit da drin.
Der Stream-Adapter leitert nur alle Zugriffe auf die TStream-Klasse um, welche über seine Methoden gemacht werden. |
AW: DLL Exportiert ein Interface mit Strings...
Zitat:
|
AW: DLL Exportiert ein Interface mit Strings...
Die DLL muss mit den Methoden des IStream auskommen, kann aber damit den Inhalt der dahinter versteckten Klasse aus der Hauptanwendung verändern.
Beispiel |
AW: DLL Exportiert ein Interface mit Strings...
Zitat:
Ich muss an eine DLL 1-3 TStreams übergeben und diese an einen Procedure in der DLL weiterleiten... Was die Procedure damit macht kann ich nicht sagen. Mavarik |
AW: DLL Exportiert ein Interface mit Strings...
Wenn die Streams quasi "fertig" übergeben werden (gefüllt), dann pack die in einen IStream und in die DLL damit. In der DLL packst du die aus dem IStream in einen Stream und ab in die procedure.
Das wäre so die Holzhammer-Methode (sollte aber tun) Ansonsten bau dir einen IStream-Wrapper (abgeleitet von TStream) und übergib den in der DLL an die procedure |
AW: DLL Exportiert ein Interface mit Strings...
Zitat:
Was "kann" mein IStream-Wrapper "besser" bei der Übergabe? |
AW: DLL Exportiert ein Interface mit Strings...
Das Interface trennt den Code und Speichermanager an der Grenze von DLL und EXE,
also die enthaltene TStream-Instanz bleibt immer auf der Seite, wo das Interface erstellt wurde, selbst wenn das Interface in der anderen DLL/EXE verwendet wird. Klasseninstanzen lassen sich nunmal nicht im Bereich einer anderen RTTI (ohne Shared-RTTI aka BPLs) oder eines anderen Speichermanagers (ohne SharedMM) verwenden. Einfaches Beispiel: Du definierst in EXE und DLL eine Klasse. (egal, ob das aus ein und der selben Unit stammt, solange das jeweils einzeln einkompiliert wurde)
Delphi-Quellcode:
Jetzt greifst du in einem der beiden Kompilate nicht auf FStr zu, weswegen der Compiler das wegoptimiert.
type
TMyClass = class FStr: string; FInt: Integer; FXyz: Integer; end; Somit steht in dem einem Kompilat an Adresse X das FStr, während im anderem Kompilat an der selben Adresse das FInt steht, da das FStr dort ja weggelassen wurde. Greift man nun von der einen Seite auf das Objekt der anderen EXE/DLL zu, dann erwischt man z.B. FInt, anstatt FStr, welches dann natürlich ein falsches Ergebnis liefert. Das ist so, als wenn ich auf einer Straße ab der Hausnummer 6 alle Hausnummernschilder abschraube, die 6 weglasse und alles wieder um 1 versetzt anschraube. Wenn du jetzt aus einem Paralleluniversum kommst und wie gewohnt in Hausnummer 13 rein willst, dann stehst du bei uns im falschen Haus. :zwinker: (OK, der Vergleich hinkt ... eigentlich müsste ich das ganze Haus klauen und alle nachfolgenden Häuser entsprechend verschieben :angel:) [edit] Oder anders erklärt: - in einer der DLL/EXE wurde das FStr wegoptimiert - greift man nun auf FInt zu, dann liest man in Wirklichkeit das FXyz aus |
AW: DLL Exportiert ein Interface mit Strings...
Zitat:
Der DLL kannst du IStream übergeben, die macht daraus mit ihrem eigenen Speichermanager wieder TStream und ruft damit die Procedure der DCU auf. Das Beispiel weiter oben zeigt dabei alle Möglichkeiten auf, hier noch mal vereinfacht:
Delphi-Quellcode:
{dll-Funktion kapselt dcu}
function DllFunction(const Input, Output: IStream); var InputStream: TStream; OutputStream: TStream; begin InputStream := TOleStream.Create(Input); OutputStream := TOleStream.Create(Output); try {dcu-Funktion aufrufen} DcuFunction(InputStream, OutputStream); // <- erwartet TStream als Parameter finally InputStream.Free; OutputStream.Free; end; end; |
AW: DLL Exportiert ein Interface mit Strings...
Und hier mal komplett zusammengebaut:
|
AW: DLL Exportiert ein Interface mit Strings...
:thumb:
|
AW: DLL Exportiert ein Interface mit Strings...
HI,
ähnlichesn PROBLEM mit dll call von delphi aus: Bekomme nur Schrott in meiner Rückgabevariablen: Kann mir wer helfen was ich falsch mache? Calcsum ist ok, aber der String-Rückgabewert ist Schrott.
Delphi-Quellcode:
unit QMC_Unit;
interface uses ShareMem, Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls; type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private { Private-Deklarationen } public { Public-Deklarationen } end; var Form1: TForm1; implementation {$R *.dfm} function calcsum(a: double; b: double):integer ; cdecl; external 'qmc_dll_Project1.dll'; procedure calcmain( var loesung: Pansichar) ; cdecl; external 'qmc_dll_Project1.dll'; procedure calcmaindummy( var loesung: PansiChar) ; cdecl; external 'qmc_dll_Project1.dll'; procedure TForm1.Button1Click(Sender: TObject); var loesung: PAnsiChar; begin ShowMessage(inttostr(calcsum(1,2))); calcmaindummy(loesung); ShowMessage(String(loesung)); end; end.
Delphi-Quellcode:
// Wichtiger Hinweis zur DLL-Speicherverwaltung, wenn Ihre DLL die statische
// Version der Laufzeitbibliothek verwendet: // // Wenn Ihre DLL Funktionen exportiert, die String-Objekte (oder Strukturen/Klassen // mit verschachtelten Strings) als Parameter oder Funktionsergebnisse übergeben, // müssen Sie die Bibliothek MEMMGR.LIB dem DLL-Projekt und allen anderen Projekten, // die die DLL verwenden, hinzufügen. Außerdem müssen Sie die MEMMGR.LIB verwenden, // wenn andere Projekte, die die DLL einsetzen, Neu- oder Löschen-Operationen für nicht // von TObject abgeleitete Klassen durchführen, die aus der DLL exportiert // wurden. Das Hinzufügen von MEMMGR.LIB zu Ihrem Projekt ändert die DLL und ihre aufrufenden // EXE-Dateien, damit diese BORLNDMM.DLL als Speicherverwaltung verwenden. In diesen Fällen // muss die Datei BORLNDMM.DLL zusammen mit Ihrer DLL weitergegeben werden. // // Übergeben Sie String-Informationen mit "char *" oder ShortString-Parametern, // um die Verwendung von BORLNDMM.DLL zu vermeiden. // // Wenn Ihre DLL die dynamische Version der RTL verwendet, müssen Sie MEMMGR.LIB // nicht explizit hinzufügen, weil dies implizit ausgeführt wird. #pragma argsused extern int __declspec(dllexport) __stdcall calcsum(double a, double b){ return a + b; } extern void __declspec(dllexport) __stdcall calcmaindummy(char* loesungdummy) { loesungdummy = "TESTENDE:2ndLine"; //resultbuff; } int _libmain(unsigned long reason) { return 1; } |
AW: DLL Exportiert ein Interface mit Strings...
Hmm..
Schaue Dir mal die Deklarationen von deiner App und der DLL an: Zitat:
Ich tippe auf das 'cdecl' statt des 'stdcall' in der DLL ;) |
AW: DLL Exportiert ein Interface mit Strings...
Außerdem wäre es sauberer, wenn die DLL den Speicher nicht selbst reservieren würde, sondern die Hauptanwendung den Speicher reserviert und den Pointer plus die Größe des Bereiches an die DLL übergibt.
Dazu gegebenenfalls vorher noch die Anfrage an die DLL wie viel Speicher benötigt wird. Also analog zur API von Windows. |
AW: DLL Exportiert ein Interface mit Strings...
Zitat:
Hab gelesen dass man da memmgr.lib dazu linken muss. WIe muss ich das machen? Hab schon Bei den Programmoptionen "Mit Laufzeitbibliothek linken" auf true gesetzt. Kann mir einfach ein Bsp geben wie man einen String zurückgibt? Ich sitz jetzt schon Stunden hier und komm nicht weiter.... DANKE WS |
AW: DLL Exportiert ein Interface mit Strings...
Hallo,
wie bereits geschrieben, muss der Aufrufer der Dll den Speicher reservieren (GetMem) Die DLL-Funktion muss strncpy benutzen. |
AW: DLL Exportiert ein Interface mit Strings...
ganz allgemein: in DLLs gib im WIN-API es keine "void"/"procedure", man verwende besser immer "function" und gebe IMMER mindestens einen "integer" zurück
wenn es unbedingt var sein soll, dann so: function calcmaindummy( var loesung: PansiChar):integer ; cdecl; external 'qmc_dll_Project1.dll'; extern integer __declspec(dllexport) __stdcall calcmaindummy(char** loesungdummy) *loesungdummy="irgendwas"; // die Rückgabe MUSS ein konstanter oder globaler statischer Speicherbereich sein, keine lokale Variable, weil die ist weg wenn die Funktion zu Ende!!! return(0); oder einfacher: function calcmaindummy(): PAnsiChar ; cdecl; external 'qmc_dll_Project1.dll'; extern char* __declspec(dllexport) __stdcall calcmaindummy(void) return("irgendwas"); // die Rückgabe MUSS ein konstanter oder globaler statischer Speicherbereich sein, keine lokale Variable, weil die ist weg wenn die Funktion zu Ende!!! etwas tricky, aber Delphi seitig sehr sicher: im DelphiProgramm einen PAnsiChar auf einen ShortString übergeben und den in der C-DLL per "strcpy" füllen function calcmaindummy(loesung: PansiChar):integer ; cdecl; external 'qmc_dll_Project1.dll'; extern integer __declspec(dllexport) __stdcall calcmaindummy(char* loesungdummy) strcpy(&loesungdummy[1],"irgendwas"); loesungdummy[0]=strlen(&loesungdummy[1]); return(0); delphi: var loesung:shortstring; calcmaindummy(@loesung); // blind getippt, aber des sollte nach Gefühl ohne Cast so gehen |
AW: DLL Exportiert ein Interface mit Strings...
danke dir.
Meinst du so?
Delphi-Quellcode:
und in dll:
function calcsum(a: double; b: double):integer ; stdcall; external 'qmc_dll_Project1.dll';
procedure calcmain( var loesung: Pansichar) ; stdcall; external 'qmc_dll_Project1.dll'; procedure calcmaindummy( var loesung: PANsiChar) ; stdcall; external 'qmc_dll_Project1.dll'; procedure TForm1.Button1Click(Sender: TObject); var loesung: PAnsiChar; begin ShowMessage(inttostr(calcsum(1,2))); getmem(loesung,300); calcmaindummy(loesung); ShowMessage(String(loesung)); end;
Code:
oder muss ich in Delphi die Adresse übergeben?
void __declspec(dllexport) __stdcall calcmaindummy(char* loesung){
strcpy(loesung,"TESTENDE:2ndLine"); //resultbuff; }
Delphi-Quellcode:
Dann meckert er beim compiliern.
calcmaindummy(@loesung);
Habs jetzt dyn geladen und da komm tauch nur Schrott zurück:
Delphi-Quellcode:
Ich galub das hat mit dem MEMMGR.LIB zu tun. MUss man da was machen oder wie?
procedure TForm1.Button2Click(Sender: TObject);
var hmod : THandle; GetReturn : procedure ( var loesung: PANsiChar) ; stdcall; RetStr : PAnsichar; begin hmod := LoadLibrary('qmc_dll_Project1.dll'); if (hmod <> 0) then begin GetReturn := GetProcAddress(hmod, 'calcmaindummy'); if (@GetReturn <> nil) then begin GETMEM(RetStr,20); GetReturn(RetStr); ShowMessage('Drive Type is : ' + string(RetStr)); end else ShowMessage('GetProcAddress failed'); FreeLibrary(hmod); end else ShowMessage('LoadLibrary Failed!'); end; DANKE mfg ws |
AW: DLL Exportiert ein Interface mit Strings...
für deine Variante mit GetMem wäre der C teil so:
strcpy(loesungdummy,"irgendwas"); return(0); Ich bleibe Dabei: man macht in DLLs keine proceduren/void Rückgaben... wenn MicroSoft das gewollt hätte, hätten die das im WinApi selbst auch gemacht... haben sie aber nicht... also geben man eben auch immer etwas ala integer zurück:) |
AW: DLL Exportiert ein Interface mit Strings...
Hallo,
char* = PAnsiChar Das var kannst du also weglassen. strncpy und nicht strcpy benutzen Was soll eigentlich des decl? siehe auch hier http://stackoverflow.com/questions/2...need-to-use-it |
AW: DLL Exportiert ein Interface mit Strings...
wenn schon "strncpy", dann bitte aber auch die mögliche MaxLänge mit als Parameter übergeben, sonst weiß das C Programm ja nicht wieviel Speicher wirklich der Aufrufer zu Verfügung gestellt hat... dann wird es erst sicherer:)
|
AW: DLL Exportiert ein Interface mit Strings...
Liste der Anhänge anzeigen (Anzahl: 1)
es klappt einfach nicht.
Es kommt Schrott.. (sieh bild) Jetzt hab ich mal eine int via return (= summe) das klappt. ABer die integer via return in den formalen paramters klappt nicht.
Delphi-Quellcode:
mit dem ohne __cdecl(..) geht es nicht.
procedure TForm1.Button2Click(Sender: TObject);
var hmod : THandle; GetReturn : procedure ( var loesung: integer) ; stdcall; Getsum: function ( a: double; b: double; c: double):integer ; stdcall; RetStr : PAnsichar; RetINT: integer; begin hmod := LoadLibrary('qmc_dll_Project1v2.dll'); if (hmod <> 0) then begin GetReturn := GetProcAddress(hmod, 'calcmaindummy'); if (@GetReturn <> nil) then begin GETMEM(RetStr,20); //GetReturn(RetStr); GetReturn(Retint); ShowMessage('Loeung des QMC ist : ' + inttostr(Retint)); end else ShowMessage('GetProcAddress failed'); GetSUM := GetProcAddress(hmod, 'calcsum'); if (@GetSUM <> nil) then begin ShowMessage('Summe ist : ' + inttostr(GetSUM(9,2,1))); end else ShowMessage('GetProcAddress failed'); FreeLibrary(hmod); end else ShowMessage('LoadLibrary Failed!'); end; Da findet er die function mit dem dyn laden nicht mehr.
Code:
also doch so:
void __stdcall calcmaindummy(int loesung){
char* demotest = "KTestende:2ndline"; //strncpy(loesung,demotest,5); //resultbuff; loesung = 3; return 0; }
Code:
void __declspec(dllexport)__stdcall calcmaindummy(int loesung){
char* demotest = "KTestende:2ndline"; //strncpy(loesung,demotest,5); //resultbuff; loesung = 3; return 0; } int __declspec(dllexport) __stdcall calcsum(double a, double b, double c){ return a +b +c; } |
Alle Zeitangaben in WEZ +1. Es ist jetzt 23:35 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