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