Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi DLL Exportiert ein Interface mit Strings... (https://www.delphipraxis.net/180689-dll-exportiert-ein-interface-mit-strings.html)

Mavarik 9. Jun 2014 11:15

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:
type
    IFOO = Interface
     ['{BC0B635C-5296-4316-9559-0E8E9A32D460}']
      Procedure Test(Parameter:String);
    End;

Procedure GetI(out IOUT : IFOO);stdcall;
begin
...
end;

Exports
  GetI;
Mavarik

Sir Rufo 9. Jun 2014 11:20

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:
stdcall
oder whatever (Calling Convention) gekennzeichnet werden (sonst geht das nur mit Delphi)

Mavarik 9. Jun 2014 11:23

AW: DLL Exportiert ein Interface mit Strings...
 
Zitat:

Zitat von Sir Rufo (Beitrag 1261753)
Ja, es sein denn du nimmst
Delphi-Quellcode:
WideString
.

WideString <> AnsiString... Natürlich, ich meine im Bezug auf die Übergabe...?

Sir Rufo 9. Jun 2014 11:29

AW: DLL Exportiert ein Interface mit Strings...
 
Zitat:

Zitat von Mavarik (Beitrag 1261754)
Zitat:

Zitat von Sir Rufo (Beitrag 1261753)
Ja, es sein denn du nimmst
Delphi-Quellcode:
WideString
.

WideString <> AnsiString... Natürlich, ich meine im Bezug auf die Übergabe...?

Ähm, welche Übergabe denn jetzt? Und welche Rolle spielt das?

Wenn du einen Typen verwendest, der nur von Delphi gemanaged wird (z.B.
Delphi-Quellcode:
string
) dann musst du
Delphi-Quellcode:
ShareMem
verwenden.

Nimmst du stattdessen einen Typen der nicht von Delphi gemanaged wird (statt
Delphi-Quellcode:
string
eben
Delphi-Quellcode:
WideString
) dann brauchst du eben kein
Delphi-Quellcode:
ShareMem
.

himitsu 9. Jun 2014 11:39

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.

Mavarik 9. Jun 2014 11:58

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

himitsu 9. Jun 2014 12:07

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.

Sir Rufo 9. Jun 2014 12:26

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.

Mavarik 9. Jun 2014 13:04

AW: DLL Exportiert ein Interface mit Strings...
 
Zitat:

Zitat von Sir Rufo (Beitrag 1261762)
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.

OK...
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:
Bla.SavetoFile(Filename); // String übergeben
S := Bla.GetStr(100);            // String zurück erhalten
Also eigentlich immer nur Strings mal eben weiterreichen, verarbeiten und zurück geben.

Kann man das "Riskieren" oder doch lieber auf WideString gehen?

Mavarik

Sir Rufo 9. Jun 2014 13:22

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:
PAnsiChar
statt
Delphi-Quellcode:
WideString
und die neue Klasse benutzt als Parameter
Delphi-Quellcode:
AnsiString
statt
Delphi-Quellcode:
string
.

Mavarik 9. Jun 2014 13:24

AW: DLL Exportiert ein Interface mit Strings...
 
Zitat:

Zitat von Sir Rufo (Beitrag 1261766)
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:
PAnsiChar
statt
Delphi-Quellcode:
WideString
und die neue Klasse benutzt als Parameter
Delphi-Quellcode:
AnsiString
statt
Delphi-Quellcode:
string
.

Unicode ist kein Thema... Geht von XE5 -> XE6. Die DLL soll aber auch mit Delphi 2007 Funktionieren..

himitsu 9. Jun 2014 14:13

AW: DLL Exportiert ein Interface mit Strings...
 
Zitat:

Zitat von Sir Rufo (Beitrag 1261766)
und die neue Klasse benutzt als Parameter
Delphi-Quellcode:
AnsiString
statt
Delphi-Quellcode:
string
.

Achtung: AnsiString ist nicht gleich AnsiString.

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.

Mavarik 9. Jun 2014 14:57

AW: DLL Exportiert ein Interface mit Strings...
 
OK..

Was ist mit einem TStream?

Mavarik

himitsu 9. Jun 2014 15:52

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.

Mavarik 9. Jun 2014 16:27

AW: DLL Exportiert ein Interface mit Strings...
 
Zitat:

Zitat von himitsu (Beitrag 1261772)
Es gibt allerdings den IStream, aber der ist nicht kompatibel mit TStream's, wobei es dafür aber eine Wrapper-Klasse gibt.

Wrapper-Klasse?

himitsu 9. Jun 2014 16:40

AW: DLL Exportiert ein Interface mit Strings...
 
TStreamAdapter

Mavarik 10. Jun 2014 09:51

AW: DLL Exportiert ein Interface mit Strings...
 
Zitat:

Zitat von Mavarik (Beitrag 1261774)
Wrapper-Klasse?

OK. :stupid: War vielleicht ein bisschen kurz...

Zitat:

Zitat von himitsu (Beitrag 1261775)
TStreamAdapter

Also habe ich keine bessere Antwort verdient...:stupid:
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

himitsu 10. Jun 2014 10:22

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;

Mavarik 10. Jun 2014 11:25

AW: DLL Exportiert ein Interface mit Strings...
 
Zitat:

Zitat von himitsu (Beitrag 1261804)
Eigentlich Recht einfach.

Man nimmt also irgendeinen Stream, packt ihn in den TStreamAdapter und überlegt sich dann noch, wer für die Freigabe sorgen soll.

OK

Delphi-Quellcode:
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;
Wenn die DLL_MachWas mit den Streams arbeitet? Wie kommt der Inhalt wieder zurück in A & B
oder arbeitet die DLL dann auf A & B oder einer Kopie?

Mavarik

himitsu 10. Jun 2014 12:01

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.

Mavarik 10. Jun 2014 14:32

AW: DLL Exportiert ein Interface mit Strings...
 
Zitat:

Zitat von himitsu (Beitrag 1261818)
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.

Bedeutet?

Blup 10. Jun 2014 16:57

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

Mavarik 10. Jun 2014 20:04

AW: DLL Exportiert ein Interface mit Strings...
 
Zitat:

Zitat von Blup (Beitrag 1261851)
Die DLL muss mit den Methoden des IStream auskommen,

OK Vielleicht stehe ich auf dem Schlauch...

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

Sir Rufo 10. Jun 2014 20:56

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

Mavarik 11. Jun 2014 09:50

AW: DLL Exportiert ein Interface mit Strings...
 
Zitat:

Zitat von Sir Rufo (Beitrag 1261873)
Ansonsten bau dir einen IStream-Wrapper (abgeleitet von TStream) und übergib den in der DLL an die procedure

ok... Wo ist der unterschied? Ich verstehe es gerade echt nicht...

Was "kann" mein IStream-Wrapper "besser" bei der Übergabe?

himitsu 11. Jun 2014 10:16

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:
type
  TMyClass = class
    FStr: string;
    FInt: Integer;
    FXyz: Integer;
  end;
Jetzt greifst du in einem der beiden Kompilate nicht auf FStr zu, weswegen der Compiler das wegoptimiert.
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

Blup 11. Jun 2014 12:08

AW: DLL Exportiert ein Interface mit Strings...
 
Zitat:

Zitat von Mavarik (Beitrag 1261868)
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.

Weiter oben hies es, du möchtest die Prozeduren einer DCU über eine DLL kapseln.
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;

Sir Rufo 11. Jun 2014 13:25

AW: DLL Exportiert ein Interface mit Strings...
 
Und hier mal komplett zusammengebaut:
  • DLL
    Delphi-Quellcode:
    library dp_180689_lib;

    uses
      System.SysUtils,
      ExternalClassLibWrapper in 'ExternalClassLibWrapper.pas',
      ExternalClassIntf in 'ExternalClassIntf.pas',
      ExternalClass in 'lib\ExternalClass.pas';

    {$R *.res}

    function GetExternalClassWrapper : IExternalClassWrapper; safecall;
    begin
      Result := TExternalClassWrapper.Create;
    end;

    exports
      GetExternalClassWrapper;

    begin

    end.
  • die originale Unit (liegt nur als DCU vor)
    Delphi-Quellcode:
    unit ExternalClass;

    // originale Unit, die nicht geändert werden kann,
    // da diese nur als DCU vorliegt

    interface

    uses
      Classes;

    type
      TExternalClass = class
      public
        procedure DoSomething( AStream : TStream );
      end;

    implementation

    { TExternalClass }

    procedure TExternalClass.DoSomething( AStream : TStream );
    var
      LWriter : TStreamWriter;
    begin
      LWriter := TStreamWriter.Create( AStream );
      try
        LWriter.Write( 'Written by ExternalClass' );
      finally
        LWriter.Free;
      end;
    end;

    end.
  • Der Wrapper für die Klasse und das Interface
    Delphi-Quellcode:
    unit ExternalClassLibWrapper;

    interface

    uses
      ActiveX, Classes, Vcl.AxCtrls,
      ExternalClassIntf, ExternalClass;

    type
      TExternalClassWrapper = class( TInterfacedObject, IExternalClassWrapper )
      private
        FInstance : TExternalClass;
      public
        constructor Create;
        destructor Destroy; override;

        procedure DoSomething( AStream : IStream ); safecall;
      end;

    implementation

    { TExternalClassWrapper }

    constructor TExternalClassWrapper.Create;
    begin
      inherited Create;
      FInstance := TExternalClass.Create;
    end;

    destructor TExternalClassWrapper.Destroy;
    begin
      FInstance.Free;
      inherited;
    end;

    procedure TExternalClassWrapper.DoSomething( AStream : IStream );
    var
      LStream : TStream;
    begin
      LStream := TOleStream.Create( AStream );
      try
        FInstance.DoSomething( LStream );
      finally
        LStream.Free;
      end;
    end;

    end.
  • Das Interface (das teilen sich sowohl DLL als auch die Anwendung)
    Delphi-Quellcode:
    unit ExternalClassIntf;

    interface

    uses
      ActiveX;

    type
      IExternalClassWrapper = interface
        ['{9AC01751-00EB-49D8-9FBA-3BDB1EA5F2D9}']
        procedure DoSomething( AStream : IStream ); safecall;
      end;

    implementation

    end.
  • Testprogramm
    Delphi-Quellcode:
    program dp_180689_app;

    {$APPTYPE CONSOLE}
    {$R *.res}

    uses
      System.SysUtils,
      System.Classes,
      ExternalClassIntf in 'ExternalClassIntf.pas',
      ExternalClass in 'ExternalClass.pas';

    procedure Main;
    var
      LStream : TStream;
      LExternal : TExternalClass;
      LReader : TStreamReader;
    begin
      LStream := TMemoryStream.Create;
      try

        LExternal := TExternalClass.Create;
        try
          LExternal.DoSomething( LStream );
        finally
          LExternal.Free;
        end;

        LStream.Seek( 0, soFromBeginning );

        LReader := TStreamReader.Create( LStream );
        try
          Writeln( LReader.ReadToEnd );
        finally
          LReader.Free;
        end;

      finally
        LStream.Free;
      end;
    end;

    begin
      ReportMemoryLeaksOnShutdown := True;
      try
        Main;
      except
        on E : Exception do
          Writeln( E.ClassName, ': ', E.Message );
      end;
      ReadLn;
    end.
  • Wrapper (Unit und Klasse folgen der selben Namenskonvention, dadurch spart man sich Tipparbeit)
    Delphi-Quellcode:
    unit ExternalClass;

    interface

    uses
      Classes,
      ExternalClassIntf;

    type
      TExternalClass = class
      private
        FInstance : IExternalClassWrapper;
      public
        constructor Create;
        procedure DoSomething( AStream : TStream );
      end;

    implementation

    uses
      ActiveX;

    function GetExternalClassWrapper : IExternalClassWrapper; safecall; external 'dp_180689_lib.dll' delayed;

    { TExternalClass }

    constructor TExternalClass.Create;
    begin
      inherited Create;
      FInstance := GetExternalClassWrapper;
    end;

    procedure TExternalClass.DoSomething( AStream : TStream );
    begin
      FInstance.DoSomething( TStreamAdapter.Create( AStream, soReference ) );
    end;

    end.

mkinzler 11. Jun 2014 14:11

AW: DLL Exportiert ein Interface mit Strings...
 
:thumb:

wschrabi 9. Apr 2017 18:22

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;
}

HolgerX 9. Apr 2017 18:26

AW: DLL Exportiert ein Interface mit Strings...
 
Hmm..

Schaue Dir mal die Deklarationen von deiner App und der DLL an:

Zitat:

Zitat von wschrabi (Beitrag 1366920)

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';

...


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;

}
[/DELPHI]


Ich tippe auf das 'cdecl' statt des 'stdcall' in der DLL ;)

jaenicke 9. Apr 2017 18:56

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.

wschrabi 9. Apr 2017 19:10

AW: DLL Exportiert ein Interface mit Strings...
 
Zitat:

Ich tippe auf das 'cdecl' statt des 'stdcall' in der DLL ;)
Nein hab ich schon alles probiert.
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

hoika 9. Apr 2017 19:33

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.

mensch72 9. Apr 2017 21:13

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

wschrabi 9. Apr 2017 21:20

AW: DLL Exportiert ein Interface mit Strings...
 
danke dir.
Meinst du so?
Delphi-Quellcode:
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;
und in dll:
Code:
void __declspec(dllexport) __stdcall calcmaindummy(char* loesung){

strcpy(loesung,"TESTENDE:2ndLine"); //resultbuff;

}
oder muss ich in Delphi die Adresse übergeben?

Delphi-Quellcode:
  calcmaindummy(@loesung);
Dann meckert er beim compiliern.

Habs jetzt dyn geladen und da komm tauch nur Schrott zurück:
Delphi-Quellcode:
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;
Ich galub das hat mit dem MEMMGR.LIB zu tun. MUss man da was machen oder wie?

DANKE
mfg
ws

mensch72 9. Apr 2017 21:26

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:)

hoika 9. Apr 2017 21:30

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

mensch72 9. Apr 2017 21:33

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:)

wschrabi 9. Apr 2017 21:43

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:
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;
mit dem ohne __cdecl(..) geht es nicht.

Da findet er die function mit dem dyn laden nicht mehr.
Code:
void __stdcall calcmaindummy(int loesung){
   char* demotest = "KTestende:2ndline";

   //strncpy(loesung,demotest,5); //resultbuff;
   loesung = 3;
   return 0;

}
also doch so:
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