Delphi-PRAXiS
Seite 3 von 4     123 4      

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


Alle Zeitangaben in WEZ +1. Es ist jetzt 22:04 Uhr.
Seite 3 von 4     123 4      

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