AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Objekt an Interface zuweisen

Ein Thema von profmaster · begonnen am 21. Aug 2005 · letzter Beitrag vom 23. Aug 2005
Antwort Antwort
profmaster

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

Objekt an Interface zuweisen

  Alt 21. Aug 2005, 19:54
Hallo!

Delphi-Quellcode:
type
  TApp = class(TInterfacedObject, IApp)
  private
    FEditor: IEditor; //Ein Interface


constructor TApp.Create;
begin
  inherited Create;
  FEditor := TEditorAdapter.Create(Form1.SynEdit);
  //Interface := Klasse;

  if FEditor is SynEdit then ShowMessage('Editor wurde zugewiesen');
  //gibt Compilerfehler : '.' erwartet aber THEN gefunden. Wie frage ich ab, ob Zuweisung
  //korrekt
end;
Bin grad dabei, das Beispiel zu studieren. Meine Frage. Wie kann ich prüfen, ob das Objekt korrekt an das Interface zugewiesen wurde. Das Beispiel ist aus einem Thread mit dem Titel:
"Dll wird nicht geladen...". Ich beschäftige mich grad mit Intefaces, weil ich das für mein nächste Projekt in der Firma benötige. Da kommt dieses Beispiel grad recht.

Danke im Voraus für Eure Hilfe

profmaster
  Mit Zitat antworten Zitat
Chewie

Registriert seit: 10. Jun 2002
Ort: Deidesheim
2.886 Beiträge
 
Turbo Delphi für Win32
 
#2

Re: Objekt an Interface zuweisen

  Alt 21. Aug 2005, 19:58
Was ist denn SynEdit? Eine Klasse? Wenn ja, würde der Fehler nicht kommen. Wenn nein, ist das der Fehler.
Martin Leim
Egal wie dumm man selbst ist, es gibt immer andere, die noch dümmer sind
  Mit Zitat antworten Zitat
jbg

Registriert seit: 12. Jun 2002
3.481 Beiträge
 
Delphi 10.1 Berlin Professional
 
#3

Re: Objekt an Interface zuweisen

  Alt 22. Aug 2005, 11:51
Zitat von profmaster:
Bin grad dabei, das Beispiel zu studieren. Meine Frage. Wie kann ich prüfen, ob das Objekt korrekt an das Interface zugewiesen wurde.
Warum sollte es nicht korrekt an das Interface zugewiesen worden sein? Der Compiler erlaubt dir schon mal nur Objekte zuzuweisen, die das entsprechende Interface unterstüzten. Als Alternative kann man da nur noch nil zuweisen.
Also wenn das Interface <> nil ist, dann ist das Objekte korrekt zugewiesen worden.

Ein Zugreifen auf das im Interface verpackte Objekt ist nicht möglich. Da muss man dann schon eine Methode schreiben, die Self zurückliefert:
Delphi-Quellcode:
Ibla = interface
  function GetObject: TObject;
end;

Tbla = class(TInterfacedObject, Ibla)
  function GetObject: TObject;
end;

function Tbla.GetObject: TObject;
begin
  Result := Self;
end;
Womit aber jedem die Tür zum fehlerhaften Zugriff auf das interne Objekt gestattet ist.
  Mit Zitat antworten Zitat
profmaster

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

Re: Objekt an Interface zuweisen

  Alt 22. Aug 2005, 13:14
Hallo jbg!

Zitat von jbg:
Ein Zugreifen auf das im Interface verpackte Objekt ist nicht möglich....
Aha, Danke. Da ist das also die Ursache dafür, das Content nicht ins Clipboard gelangt. Da muss also das Editorobjekt im Beispiel anders geholt werden. Da hab ich schon mal nen Anhaltspunkt, wo ich weiterarbeiten muss. Werd mal bissl damit experimentieren. Schließlich soll das Editorobjekt und später mein beliebiges Objekt im Plugin und NICHT, wie im Beispiel in der Anwendung zugewiwesen werden. Ich will ja von außen die Funktionalität meiner Anwendung erweitern.

freundliche Grüße von

profmaster
  Mit Zitat antworten Zitat
jbg

Registriert seit: 12. Jun 2002
3.481 Beiträge
 
Delphi 10.1 Berlin Professional
 
#5

Re: Objekt an Interface zuweisen

  Alt 22. Aug 2005, 14:57
Bei Interfaces ist es normalerweise nicht notwendig sich Gedanken um das dahinter stehende Objekt zu machen. Dieses Objekt unterstützt die Schnittstellen des Interfaces (gut das man Deutsch und Englisch mischen kann) und mehr braucht man nicht zu wissen. Denn wenn man auf das Objekt direkt zugereifen muss, dann sollte man sein Interface mal anschauen, ob man das nicht noch darein packen kann. Und damit ältere Plugins auch noch funktionieren macht man das dann so:

Altes Programm:
Delphi-Quellcode:
IMyIntf = interface
  [GUID-123]
  procedure Bla;
end;
Neues Programm:
Delphi-Quellcode:
IMyIntf10 = interface
  [GUID-123]
  procedure Bla;
end;

IMyIntf = interface(IMyIntf10)
  [GUID-987]
  procedure NeuBla;
end;
  Mit Zitat antworten Zitat
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, 15: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
jbg

Registriert seit: 12. Jun 2002
3.481 Beiträge
 
Delphi 10.1 Berlin Professional
 
#7

Re: Objekt an Interface zuweisen

  Alt 22. Aug 2005, 20:15
Zitat von profmaster:
Wie greife ich nun in meiner Anwendung auf mein Plugin zu?
Du musst dazu die IPlugin-Schnittstelle erweitern oder eine andere Schnittstelle aufbauen.

Zitat:
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
Das Design ist hierbei genau andersherum als du es willst. In dem Beispiel besitzt die Anwendung den Editor, und das Plugin greift auf diesen nur zu.

Zitat:
Warum IPlugin, wo doch der EditorAdapter da ist?
Der EditorAdapter ist nur dazu da, den bereits vorhandenen TSynEdit in ein Interface zu packen. Die alternative wäre eine Klasse von TSynEdit ableiten und die fehlenden Interface-Methode hinzuzufügen. Da aber in dem Beispiel der TSynEdit zur Designzeit auf das Formular gelegt wurde, müsste man dazu erstmal ein Package schreiben, die Komponente registrieren, ...
Der TEditorAdapter wird in der Anwendung implementiert, weil dort das Editor-Objekt liegt. Das IPlugin wird hingegen im Plugin implemetiert, und die Anwendung greift dann darauf über das vom InitPlugin zurückgelieferte IPlugin zu.

Zitat:
Warum dann noch IApp?
Vielleicht möchte man ja auch noch mehr als nur den Editor ansteuern. Und sowas wie IMenu, IToolBar, IWorkspace, ... passt recht schlecht in ein IEditor hinein (wenn nicht gerade das Programm "Editor" heißt).

Zitat:
Warum nicht nur der EditorAdapter und sonst nichts?
Das wäre dann etwas einseitig. Damit ist es nur möglich in eine Richtung zu kommunizieren. Wie will das Plugin denn auf die Anwendung zugreifen, wenn dafür keine Schnittstelle vorhanden ist. Einfach wild Speicheradressen aufrufen und hoffen, dass dahinter die passende Funktion liegt, wäre wohl in 1 von 2Mrd Versuchen funktionsfähig.

Zitat:
Gibt es Richtlinien zum Interface-Design im Sinne guten Programmierstils?
Guter Programmierstil ist immer das, was man selbst als gut erachtet und was andere auch lesen/verstehen können.

Zitat:
Wie greife ich nun in meiner Anwendung auf dieses Interface zu, ohne allzuviele fehlerträchtige Verrenkungen zu machen.
Dafür gibt es das IPlugin Interface. Es wird vom Plugin implementiert und von allem anderen (der Anwendung) aufgerufen. Genauso läuft es mit IApp ab. Es wird von der Anwendung implementiert und von allem anderen (dem Plugin) aufgerufen.

Zitat:
Im Beispiel ist mir die Methode TForm1.mCfgToolsClick(Sender: TObject); zu unübersichtlich.
Da stimme ich dir zu. Der OnClick-Handler ist nicht wirklich übersichtlich. Normalesweise lädt man die Plugins beim Start der Anwendung (oder On-Demand, aber dann auch etwas schöner).
Also die Plugins laden und die exportierte Funktion InitPlugin aufrufen. Das von dieser zurückgelieferte IPlugin Interface in eine TIinterfaceList (oder eine davon angeleitete, spezialisierte TPluginList) schieben. Und jetzt die Plugin-DLL auf keinen Fall freigeben, denn sonst hängen die Interfaces in der Luft.
Im OnClick-Handler muss man jetzt nur noch durch die PluginList iterieren und das passende finden. Das Suchen des passenden Interfaces lässt sich auch noch in eine eigene Funktion auslagern, die entweder das IPlugin oder nil zurückliefet (ggf. bei nil eine Exception auslöst).

Im OnDestroy dann einfach PluginList.Clear aufrufen und die DLLs entladen (oder das von Windows machen lassen).
  Mit Zitat antworten Zitat
profmaster

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

Re: Objekt an Interface zuweisen

  Alt 23. Aug 2005, 15:50
Hallo jbg!

JUHUUUUU, es klappt jetzt. Danke für Deine Hilfe! Jetzt kann ich weitermachen!

Programmierergrüße von

Profmaster
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 20:48 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