Einzelnen Beitrag anzeigen

profmaster

Registriert seit: 9. Feb 2005
Ort: Frakfurt/Oder
21 Beiträge
 
Delphi 7 Professional
 
#6

Re: Objekt an Interface zuweisen

  Alt 22. Aug 2005, 16:32
Hallo jbg!

Habe mal ausgehend von dem Thread-Beispiel folgende Dll gebaut:

Delphi-Quellcode:
library IntfDll;

{ Wichtiger Hinweis zur DLL-Speicherverwaltung: ShareMem muß die
  erste Unit im Uses-Anweisungsteil des Interface-Abschnitts Ihrer
  Unit sein, wenn Ihre DLL Prozeduren oder Funktionen exportiert, die
  String-Parameter oder Funktionsergebnisse übergeben. Dies gilt für
  alle Strings die an und von Ihrer DLL übergeben werden --  selbst
  für diese, die in Records oder Klassen verschachtelt sind. ShareMem
  ist die Schnittstellen-Unit zur DELPHIMM.DLL, welche Sie mit Ihrer
  DLL weitergeben müssen. Um die Verwendung von DELPHIMM.DLL zu
  vermeiden, übergeben Sie String-Parameter unter Verwendung von
  PChar- oder ShortString-Parametern. }


uses
  FastShareMem, SysUtils, Dialogs, MyAppIntf, SynEdit, Forms,
  Classes;

type
  TPlugin = class(TInterfacedObject, IPlugin)
    function GetName: WideString; stdcall; // der Compiler fügt hier STDCALL autom. ein
    function GetVersion: Integer; stdcall;
    function Execute(App: IApp): Integer; stdcall;
  end;

type
  TApp = class(TInterfacedObject, IApp)
  private
    FEditor: IEditor;
  public
    constructor Create(AEditor: IEditor);

    function GetEditor: IEditor; stdcall;
    procedure Terminate; stdcall;
    property Editor: IEditor read GetEditor;
  end;

  TEditorAdapter = class(TInterfacedObject, IEditor)
  private
    FContent: WideString;
    FSynEdit: TSynEdit;
  public
    constructor Create(ASynEdit: TSynEdit);
    procedure Clear; stdcall;
    function CaretX: Integer; stdcall;
    function CaretY: Integer; stdcall;

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

    property Content: WideString read GetContent write SetContent;
  end;

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

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

function TPlugin.Execute(App: IApp): Integer; stdcall;
begin
  Result := 1; // True
  ShowMessage('Execute wurde aufgerufen!');
  App.GetEditor.Content := 'Dieser Text erscheint im Editor.';
  App.GetEditor.CopyToClipboard;
end;

{ TApp }

constructor TApp.Create(AEditor: IEditor);
begin
  inherited Create;
  FEditor := AEditor;
  if Assigned(FEditor) then ShowMessage('Editor wurde zugewiesen');
end;

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

procedure TApp.Terminate;
begin
  Application.Terminate();
end;

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

function TEditorAdapter.CaretX: Integer; stdcall;
begin
   Result := 0;
end;

function TEditorAdapter.CaretY: Integer; stdcall;
begin
   Result := 0;
end;

procedure TEditorAdapter.Clear; stdcall;
begin
   FSynEdit.Free; FSynEdit := nil;
end;

procedure TEditorAdapter.SetContent(const Value: WideString); stdcall;
begin
   FContent := Value;
end;

function TEditorAdapter.GetContent: WideString; stdcall;
begin
   Result := FContent;
end;

procedure TEditorAdapter.CopyToClipboard; stdcall;
begin
   FSynEdit.CopyToClipBoard;
end;

function InitApp: IApp; stdcall;
begin
   Result := TApp.Create(TEditorAdapter.Create(TSynEdit.Create(Application)));
end;

function InitPlugin: IPlugin; stdcall;
begin
   Result := TPlugin.Create;
end;

function InitEditor: IEditor;
begin
   Result := TEditorAdapter.Create(TSynEdit.Create(Application));
end;

exports
 InitApp,
 InitPlugin; //Die Initialisierung meines Plugin

begin
end.
Wie greife ich nun in meiner Anwendung auf mein Plugin zu?

Mich irretiert hier, das neben dem Editoradapter ein TPlugin und noch ein TApp existiert. Nur der EditorAdapter wäre übersichtlicher. Oder ein TPlugin/IPlugin, das alle Zuweisungen und sonstigen Zugriffe abdeckt. Irgendwie verstehe ich das Design noch nicht. Ich habe folgende Fragen:

Warum IPlugin, wo doch der EditorAdapter da ist?

Warum dann noch IApp?

Warum nicht nur der EditorAdapter und sonst nichts?

Gibt es Richtlinien zum Interface-Design im Sinne guten Programmierstils?

Wie greife ich nun in meiner Anwendung auf dieses Interface zu, ohne allzuviele fehlerträchtige Verrenkungen zu machen. Im Beispiel ist mir die Methode TForm1.mCfgToolsClick(Sender: TObject); zu unübersichtlich.

Hier sei sie noch mal zur Erinnerung:

Delphi-Quellcode:
procedure TForm1.mCfgToolsClick(Sender: TObject);
type
  TProcInitPlg = function: IPlugin; stdcall;
var
  fName: string;
  iPlg: IPlugin;
  aProc: TProcInitPlg;
  hDLL: HWND;
  fProc: TFarProc;
  mApp: TApp;
begin
 ShowMessage(GetCurrentDir);

 with TOpenDialog.Create(nil) do begin
    InitialDir := GetCurrentDir;
    if Execute then
      fName := FileName
    else begin ShowMessage('Datei existiert nicht!'); Exit; end;
  end;
  (*
  fName := GetCurrentDir;
  if fName[Length(fName)]<>'\' then fName := fName + '\';
  fName := fName + 'plugin.dll';
  *)

  SetLastError(0);
  hDll := LoadLibrary(@fName[1]);
   if hDll = 0 then
    ShowMessage(SysErrorMessage(GetLastError));
  fProc := GetProcAddress(hDLL,'InitPlugin');
  if fProc <> nil then begin
    @aProc := fProc; //InitPlugin ausführen und Interface an aProc
  end else begin ShowMessage('DLL konnte nicht geladen werden!'); Exit; end;
  iPlg := nil;
  iPlg := aProc;
  //TApp und TPlugin war erst in Anwendung implementiert. Da klappte das so
  //Wie kann ich jetzt das gleiche erreichen? Da muß ich doch sicher analog der obigen
  //Vorgehensweise für alle vorhandenen Interfaces eine ProcedureAddress laden, Interface
  //initialisieren und dann irgendwie drauf zugreifen. Das scheint mir etwas umständlich und
  //fehlerträchtig, wenn ich eine wirklich große Anwendung duch Plugins erweitern will. Geht
  //das nicht auch einfacher? Leider hab ich momentan keine Idee wie?



  // --- Hier wird die Execute Routine aufgerufen --- hinzu am 19.08.05 23:34
  mApp := TApp.Create;
  //funktioniert so nicht, weil TApp jetzt in Dll imlementiert

  if Assigned(iPlg) then
  begin
    iPlg.Execute(mApp);
    //Plugin ausführen. Problem: Content wird nicht zugewiesen => Warum nicht?
  end else ShowMessage('Plugin konnte nicht initialisiert werden!');

  showMessage(iPlg.GetName); //Prüfen, ob Routine gefunden
  iPlg := nil; //Plugin-Zeiger löschen
  FreeLibrary(hDLL); //Klar, Dll am Ende wieder freigeben
 end;
Ich habe die Implementation der Interfaces in die Dll gepackt, weil ich ja meine Anwendung mit Hilfe der Interfaces um Funktionalität erweitern will, die ja dann in meiner Dll stecken muß, mit der ich meine Anwendung erweitere.

Bitte heft mir. Ich bin bezüglich der Implementation etwas ratlos.

profmaster
  Mit Zitat antworten Zitat