Ich habe mir für ein Programm ein Klasse geschrieben, die erst auf die Interface-Referenzzählung "hört", wenn ich mittels einer eigenen Methode "Release" (unterschied zu _Release) als Freizugeben markiert habe. Damit kann ich das Objekt auch ohne Interface nutzen. Defekte Plugins zerschießen mir damit auch nicht das gesamte Programm.
Delphi-Quellcode:
type
TInterfaceNeutralObject =
class(TModifyObject)
private
FRefCount: Integer;
FAllowRelease: Boolean;
protected
procedure CleanUp;
virtual;
// hier den Destruktorcode einfügen
public
destructor Destroy;
override;
function QueryInterface(
const IID: TGUID;
out Obj): HRESULT;
stdcall;
function _AddRef: Integer;
stdcall;
function _Release: Integer;
stdcall;
procedure Release;
// Statt .Free muss Release aufgerufen werden
end;
{ TInterfaceNeutralObject }
function TInterfaceNeutralObject.QueryInterface(
const IID: TGUID;
out Obj): HRESULT;
begin
if GetInterface(IID, Obj)
then
Result := 0
else
Result := E_NOINTERFACE;
end;
procedure TInterfaceNeutralObject.Release;
begin
if (FRefCount = 0)
and not FAllowRelease
then
begin
CleanUp;
FAllowRelease := True;
Free;
end
else
begin
CleanUp;
FAllowRelease := True;
if FRefCount = 0
then
Free;
end;
end;
procedure TInterfaceNeutralObject.CleanUp;
begin
end;
function TInterfaceNeutralObject._AddRef: Integer;
begin
Result := -1;
Inc(FRefCount);
end;
function TInterfaceNeutralObject._Release: Integer;
begin
Result := -1;
Dec(FRefCount);
if (FRefCount <= 0)
and FAllowRelease
then
Free;
end;
destructor TInterfaceNeutralObject.Destroy;
begin
{$IFDEF INTFDEBUG}
if not FAllowRelease
then
//raise Exception.CreateFmt('InterfaceNeutralObject "%s" was not destroyed by Release', [ClassName]);
MessageBox(0, PChar(Format('
InterfaceNeutralObject "%s" was not destroyed by Release', [ClassName])), '
Error', MB_ICONHAND
or MB_OK);
{$ENDIF INTFDEBUG}
inherited Destroy;
end;
Aber wenn man es richtig macht, dann sollte man besser ein weiteres Interface deklarieren, dass dann den Zugriff auf die "versteckten" Eigenschaften erlaubt. Dem Plugin-System muss man dieses Interface ja nicht verraten. Alternativ kann man auch eine GetObject: TObject Methode dem Interface hinzufügen, dass dann das dahinterliegende Objekt zurückliefert.