Also ich habs jetzt "einigermassen" hingekriegt
weiss zwar nicht wie effizient das ganze ist (und ausführlich getestet habs ich auch noch nicht) aber es funktioniert bis jetzt gut, darum hab ich gedacht ich share mal code ausschnitte für diejenigen die ein ähnliches problem haben:
Kurzfassung:
Man erstellt ein Automations objekt für den Server, in diesem Beispiel LogServer (bzw. ILogServer) PLUS ein Interface ILogServerPlugin.
In meinem beispiel enthält ILogServerPlugin ein readonly property "PluginName" und eine methode "OnLog(AMessage:WideString);
Um ein eigenes Plugin zu erstellen müssen Automations objekte das Interface "ILogServerPlugin" implementieren und ihre ProgID im Schlüsse des Server objekts in der Registry eintragen.
( ich habe die Registry gewählt, man kann natürlich auch ein IniFile nutzen oder sonstwas, wichtig ist, dass Server die ProgID der Plugins von irgendwo her bekommt)
Die Interfaces sehen grob so aus (aufs minimum reduziert):
Delphi-Quellcode:
ILogServer = interface(IDispatch)
procedure Log(const AMessage: WideString); safecall;
function Get_InstalledPlugIns: WideString; safecall;
property InstalledPlugIns: WideString read Get_InstalledPlugIns;
end;
ILogServerPlugin = interface(IDispatch)
function Get_PluginName: WideString; safecall;
procedure OnLog(const AMessage: WideString); safecall;
property PluginName: WideString read Get_PluginName;
end;
Die Implementierung des Server Objekts sieht grob so aus:
Delphi-Quellcode:
TLogServer = class(TAutoObject, ILogServer)
private
FPlugIns: TInterfaceList;
procedure UnloadPlugIns();
protected
procedure Log(AMessage: WideString); safecall;
function Get_InstalledPlugIns: WideString; safecall;
public
procedure Initialize; override;
destructor Destroy; override;
end;
Bei der Server implementierung habe ich eine eigene Factory von TAutoObjectFactory abgeleitet um die UpdateRegistry methode zu überschreiben.
Zweck davon ist, in der Registry einen neuen Schlüssel anzulegen der die ProgID's der installierten Plugins enthält.
Delphi-Quellcode:
type
TLogServerObjectFactory = class(TAutoObjectFactory)
public
procedure UpdateRegistry(Register: Boolean); override;
end;
Die implementierung sieht so aus:
Delphi-Quellcode:
procedure TLogServerObjectFactory.UpdateRegistry(
Register: Boolean);
var
R: TRegistry;
begin
inherited;
if Register then // beim registrieren des DCOM Servers schlüssel anlegen
begin
R := TRegistry.Create;
try
R.CreateKey('
\Software\LogServer\PlugIns');
finally
R.Free;
end;
end else
begin // schlüssel löschen beim "unregistrieren"
R := TRegistry.Create;
try
R.DeleteKey('
\Software\LogServer\PlugIns');
finally
R.Free;
end;
end;
end;
Im Initalization teil muss man nun die Standard Factory mit der eigenen Klasse ersetzen:
Delphi-Quellcode:
initialization
TLogServerObjectFactory.Create(ComServer, TLogger, CLASS_Logger,
ciMultiInstance, tmApartment);
Um die PlugIns zu laden iteriere ich durch die Registry schlüssel, kreiere ein Objekt anhand des Schlüsselnamens(=ProgID),
und lege das Objekt in eine TInterfaceList (=FPlugIns).
(LoadPlugins wird im Create bzw. Initialization Aufruf des ServerObjekte aufgerufen),
Delphi-Quellcode:
procedure TLogServer.LoadPlugIns;
var
i: integer;
r: TRegistry;
pList: TStringList;
P: IInterface;
begin
r := TRegistry.Create;
try
if r.OpenKeyReadOnly('\Software\LogServer\PlugIns') then
begin
if r.HasSubKeys then
begin
pList := TStringList.Create;
r.GetKeyNames(pList);
for I:=0 to pList.Count-1 do
begin
P := CreateOleObject(pList[I]); // kreiere Plugin Objekt anhand der ProgID (bsp: 'MyPlugin.MyPlugin')
FPlugIns.Add(P);
end;
end;
end;
finally
R.Free;
end;
end;
Um die Funktionen der Plugins aufzurufen iteriere ich durch meine Interface liste, speichere das Interface in einem OleVariant und rufe die Funktion auf
( Dieser Teil ist zugegebenermassen nicht so elegant, da ich späte bindung nutze. Habe es mit casten des Interfaces zu ILogServerPlugin versucht, aber kriegte immer Fehlermeldung mit
"Schnittstelle nicht unterstützt", vielleicht hat einer von euch ja einen Tip?)
In dieser Funktion gebe ich die namen aller installierten plugins zurück. Die Methode OnLog funktioniert auf die gleiche weise (iterieren, aufrufen).
Delphi-Quellcode:
function TLogServer.Get_InstalledPlugIns: WideString;
var
I: Integer;
PlugInObj: OleVariant;
begin
if FPlugIns.Count > 0 then
begin
for I:=0 to FPlugIns.Count-1 do
begin
try
PlugInObj := FPlugIns.Items[I] as IDispatch;
Result := Result + PlugInObj.PlugInName+#13;
VarClear(PlugInObj);
except
//
end;
end;
end;
end;
// CLIENT bzw. PLUGIN
Die Implementierung des Plugins ist ziemlich einfach. Neues Automationsobjekt erstellen das von ILogServerPlugin ableitet, es registrieren (dem Server bekannt machen) und Methoden implementieren.
Dazu auch hier eine neue Klasse von TAutoObjectFactory (TPluginFactory) ableiten und UpdateRegistry überschreiben:
Delphi-Quellcode:
procedure TPlugInFactory.UpdateRegistry(
Register: Boolean);
var
R: TRegistry;
begin
inherited;
if Register then // beim registrieren der activex library progid dem Server bekanntmachen
begin
R := TRegistry.Create;
R.OpenKey('
\Software\LogServer\PlugIns\'+ProgID),True);
R.Free;
end else
begin
R := TRegistry.Create;
// schlüssel löschen bei "unregistrierung"
try
R.DeleteKey('
\Software\LogServer\PlugIns\'+ProgID));
finally
R.Free;
end;
end;
end;
...
initialization
TPlugInFactory.Create(ComServer, TFileLogPlugin, Class_FileLogPlugin,
ciMultiInstance, tmApartment);
Ich hoffe ich habs einigermassen verständlich rübergebracht. Vielleicht hilft es ja jemandem.
Gruss