Waaaah, ich raffs immer noch nicht...
Ich habe noch nie (also, nicht direkt
) selbst interfaces implementieren müssen. (Durchgearbeitet:
Dit hier,
dit und
dit och. )
Also, janz langsam, damit ich das auch verstehe:
(ich wiederhole es quasi nochmal, mit ein paar Anmerkungen, wo ich was nicht verstanden habe...)
Ich komme weg von den Klassen mit ihren Methoden. (Klassen pöhse!
) Ich brauch die Klassen nur noch, um die Interfaces implementieren zu können. Zuerst baue ich mir für meine benötigen Funktionen Interfaces. Jetzt kann ich das Kind ja bei Namen nennen: Es geht natürlich um
SmallTune. Daher auch meine Abneigung gegenüber Closed Source, Sysutils, Classes usw...
Was kann der Audioplayer? Er kann Mediadateien wiedergeben (Playback), und er stellt ein
TNA-Menü bereit (ContextMenu). Nach langem Überlegen beim Schreiben dieses Beitrags bin ich zu der Überzeugung gekommen, dass das die Grundfunktionen sind.
Es gibt dann ja auch noch ein "Display", welches der Player zum Anzeigen nutzt. Ich wüsste aber jetzt nicht, ob es gut wäre, die Klasse in ein Interface umzuwandeln, da ich zum Einen dran mit-, sie aber nicht selbst geschrieben habe, und zum Anderen vielleicht auch gar nicht möchte, das Plugins auf das Design zugreifen können...
Ich bräuchte also (mindestens) zwei EventHandler:
Delphi-Quellcode:
(* Playback State *)
TPlaybackState = ( stPlaying, stStopped, stNewTrack, stPreviousTrack, stNextTrack );
IPlaybackEventHandler = interface
['{00000000-0000-0000-0000-000000000000}']
procedure Invoke(SomeState: TPlaybackState);
end;
IPlaybackEvent = interface
['{00000000-0000-0000-0000-000000000001}']
procedure AddEvent(const Ev: IPlaybackEventHandler);
procedure DeleteEvent(const Ev: IPlaybackEventHandler);
end;
IPlaybackProvider = interface
procedure Play; safecall;
procedure Stop; safecall;
procedure Next; safecall;
{...}
property StateChanges : IPlaybackEvent {read ...}; // der Manager
property StateChanged : IPlaybackEvent {read ...}; // der Manager
end;
(* ContextMenu *)
type
TMenuItem = packed record
ID : Cardinal;
Caption : WideString;
end;
IContextMenu = interface
(* Getter *)
function GetMenuHandle: HMENU;
(* Setter *)
procedure SetMenuHandle(Value: HMENU );
procedure AppendMenuItem(Itm : TMenuItem );
procedure DeleteMenuItem(ItmID : Cardinal );
property MenuHandle: HMENU read GetMenuHandle write SetMenuHandle;
end;
IContextMenuEventHandler = interface
['{00000000-0000-0000-0000-000000000002}']
procedure Invoke({?}); //Event wird ausgelöst, wenn ein Item angeklickt wird.
end;
IContextMenuEvent = interface
['{00000000-0000-0000-0000-000000000003}']
procedure AddEvent(const Ev: IContextMenuEventHandler);
procedure DeleteEvent(const Ev: IContextMenuEventHandler);
end;
Ein paar Verständnisfragen:
Angenommen, ich möchte jetzt das Menü erstellen. Wenn ich das richtig verstanden habe, darf ich das nicht in der Anwendung im Abschnitt WM_CREATE direkt tun, sondern sollte eine Eigenschaft der Klasse TContextMenu ( die ich von IContextMenu ableite ) nutzen, oder?
Alle Plugins, die entsprechende EventHandler für das ContextMenu registriert haben, sollen benachrichtigt werden, sobald ein Eintrag angeklickt wird. Ziehe ich das System so durch, muss die Anwendung selbst auch Events registrieren, oder? Denn Punkte wie "Schließen", "Info", "Hilfe" sollen ja nicht durch Plugins eingetragen werden, sondern durch die Anwendung.
Die Nachricht, dass ein Eintrag angeklickt wurde, bekommt ja immer das Parent des Menüs. Das wäre in diesem Fall meine Anwendung, also alles wie bisher. Die Anwendung würde dann alle WM_COMMAND-Nachrichten vom Typ WM_BUTTON an das IContextMenu weiterleiten, welches seinerseits ein Event an alle registrierten Plugins auslösen würde. Die müssten dann gucken, ob das Event für sie gedacht ist.
Damit alle Plugins angesprochen werden können, bastel ich mir einen EventManager. Hier melden sich alle Plugins an. Dafür müssen die Plugins von sich aus ein Interface bereitstellen, bspw. IPlugin:
Delphi-Quellcode:
Type
TPluginType = (ptPlayback, ptContextMenu (* usw? *) );
IPlugin = interface
['{00000000-0000-0000-0000-000000000004}']
(* Getter *)
function GetPluginMajorVersion: Cardinal;
function GetPluginMinorVersion: Cardinal;
function GetName: WideString;
function GetAuthor: WideString;
function GetAuthorURL: WideString;
function GetPluginType: WideString;
property PluginMajorVersion : Cardinal read GetPluginMajorVersion;
property PluginMinorVersion : Cardinal read GetPluginMinorVersion;
property Name : WideString read GetName;
property Author : WideString read GetAuthor;
property AuthorURL : WideString read GetAuthorURL;
property PluginType : TPluginType read GetPluginType;
end;
Die Frage ist, wie bekomme ich es hin, eine Interface Liste aufzubauen, ohne die Classes.pas zu nutzen?
Die Plugins können ja jetzt daherkommen, und die Interfaces, die sie brauchen, implementieren. Und dann bräuchte ich ja auch noch so eine Art "Handshake", oder? Denn irgendwie muss ja bspw. auf die Instanz des Playback Services zugreifen, den die Anwendung erstellt hat, damit das Plugin "Play" aufrufen kann, oder?
Sorry, aber ich betrete gerade komplettes Neuland und fühl mich etwas unsicher dabei. Daher kommt das alles etwas "stackselig" daher.
//Edit: Oder mache ich mir das Leben gerade unnötig schwer?