Einzelnen Beitrag anzeigen

jbg

Registriert seit: 12. Jun 2002
3.483 Beiträge
 
Delphi 10.1 Berlin Professional
 
#4
  Alt 31. Mär 2003, 21:40
Zitat:
fGetSyn: function: TSynEdit;
Bist du dir da ganz sicher, dass du ein TSynEdit (bzw eine Komponente allgemein) der DLL übergeben willst? Da musst du dann die Unit ShareMem einbinden die wiederum die borlndmm.dll benötigt. Ein (V)C++ Programm hat wiederum seinen eigenen Speichermanager und somit geht das ganze nach hinten los.


Ich würde in deinem Fall auf Interfaces setzen. Diese sind sowohl in Delphi als auch in C++ nutzbar. Es besteht auch kein direkter Zugriff auf irgendwelche Daten, ohne dass das über eine Interface-Methode geregelt ist, die in deinem Hauptprogramm implementiert ist und somit dessen Speichermanager nutzt.

Hier mal ein kleines Beispiel für eine Plugin-Schnittstelle per Interfaces

Delphi-Quellcode:
// unit MyAppIntf.pas
IEditor = interface;
IApp = interface;
IPlugin = interface;

{ IPlugin wird von der Plugin DLL implementiert. Der Entrypoint "InitPlugin" liefert dieses Interface zurück. }
IPlugin = interface
  ['{8E5F6F87-BD7C-4F51-A2B1-36E437E20648}']
  { GetName liefert den Namen des Plugins, der im Menü verwedet wird }
  function GetName: WideString; stdcall;
  { GetVersion liefert die Versionsnummer: HiWord=Major, LoWord=Minor }
  function GetVersion: Integer; stdcall;
  { Execute wird von der Anwendung aufgerufen, wenn der Benutzer auf
    den Menüpunkt klickt. }

  function Execute(App: IApp): Integer; stdcall;
end;

{ IApp wird von der Anwendung implementiert und an IPlugin.Execute übergeben }
IApp = interface
  ['{8E5F6F87-BD7C-4F51-A2B1-36E437E20648}']
  { GetEditor liefert ein Interface auf den Editor }
  function GetEditor: IEditor; stdcall;
  { Terminate beendet das Programm }
  procedure Terminate; stdcall;
end;

{ IEditor bietet Zugriff auf den Editor und wird von IApp.GetEditor zurückgeliefert. }
IEditor = interface
  ['{2044641E-CF97-470B-9112-B0E28B0CB019}']
  function CaretX: Integer; stdcall;
  function CaretY: Integer; stdcall;

  procedure Clear; stdcall;
  procedure SetContent(const Value: WideString); stdcall;
  function GetContent: WideString; stdcall;
  procedure CopyToClipboard; stdcall;
  ...

  property Content: WideString read GetContent write SetContent;
end;
Der DLL Code:
Delphi-Quellcode:
uses MyAppIntf;
type
  TPlugin = class(TInterfacedObject, IPlugin)
    function GetName: WideString; // der Compiler fügt hier STDCALL autom. ein
    function GetVersion: Integer;
    function Execute(App: IApp): Integer;
  end;

{ TPlugin }
function TPlugin.GetName: WideString;
begin
  Result := 'Mein &Testplugin'; // "T" unterstrichen im Menü
end;

function TPlugin.GetVersion: Integer;
begin
  Result := (1 shl 16) or 0; // 1.0
end;

function TPlugin.Execute(App: IApp): Integer;
begin
  Result := 1; // True

  App.GetEditor.Content := 'Dieser Text erscheint im Editor.';
  App.GetEditor.CopyToClipboard;
end;


{ einzige exportierte Funktion }
function InitPlugin: IPlugin; stdcall; {export;}
begin
  Result := TPlugin.Create;
end;

exports
  InitPlugin;

end.

In der Anwendung:
Delphi-Quellcode:
type
  TApp = class(TInterfacedObject, IApp)
  private
    FEditor: IEditor;
  public
    constructor Create;

    function GetEditor: IEditor;
    procedure Terminate; stdcall;
  end;

  TEditorAdapter = class(TInterfacedObject, IEditor)
  private
    FSynEdit: TSynEdit;
  public
    constructor Create(ASynEdit: TSynEdit);

    procedure Clear;
    ...
  end;

{ TApp }
constructor TApp.Create;
begin
  inherited Create;
  FEditor := TEditorAdapter.Create(Form1.SynEdit);
end;

function TApp.GetEditor: IEditor;
begin
  Result := FEditor;
end;

procedure TApp.Terminate;
begin
  Form1.Close;
end;

{ TEditorAdapter }
constructor TEditorAdapter.Create(ASynEdit: TSynEdit);
begin
  inherited Create;
  FSynEdit := ASynEdit;
end;

procedure TEditorAdapter.Clear;
begin
  FSynEdit.Clear;
end;

Das ich hier WideString anstatt dem AnsiString (=string) verwendet habe liegt in der Tatsache, dass WideString unter Delphi keine Referenzzählung hat und somit nicht dem Problem mit unterschiedlichen Speichermanagern unterworfen ist, da der Speichermanager den WideString wieder freigibt, der ihn auch erzeugt hat. Und wenn mir jetzt einer mit dem Kommentar aus der Hilfe "Im Linux-Produkt von Borland wird für Wide-Strings eine Referenzzählung durchgeführt." kommt, dem muss ich sagen, dass Kylix den System-Speichermanager nutzt, was alle Linux-Programme machen. Und der ist nun mal ein und derselbe für jedes Modul.
Ein anderer Grund für WideString ist, dass auch (V)C++ diesen unterstützt und zwar als LPWSTR.

Aber zurück zu den Interfaces. Diese kann man auch in (V)C++ nutzen und hat somit eine sehr flexible Schnittstelle geschaffen. Durch die Verwendung von GUIDs ist es sogar möglich bei einer Erweiterung der Funktionalität alte Plugins am laufen zu halten.
  Mit Zitat antworten Zitat