SafePointerToInterface
От: Karluha  
Дата: 10.11.05 12:33
Оценка:
Есть такая задача. Необходимо преобразовывать IUnknown->Pointer и обратно. Проблема состоит в том, что при преобразовании Pointer->IUnknown экземпляр уже вполне может быть уничтожен. Для безопасного преобразования была написана функция:

function SafePointerToInterface(var APointer: Pointer;
  ARequiredIID: TGUID): IInterface;
begin
  Result := nil;
  if (not Assigned(APointer)) then
  begin
    Exit;
  end;
  try
    IInterface(APointer).QueryInterface(ARequiredIID, Result);
    CheckSupports(Result, ARequiredIID);
  except
    on E: Exception do
    begin
      APointer := nil;
      Result := nil;
    end;
  end;
end;


Все было бы хорошо, но вот почемуто иногда, после уничтожения экземпляра, она приводит к зависанию приложения. Может кто нибудь предолжет более корректную реализацию данной функции?
Re: SafePointerToInterface
От: Danchik Украина  
Дата: 10.11.05 13:35
Оценка:
Здравствуйте, Karluha, Вы писали:

K>Есть такая задача. Необходимо преобразовывать IUnknown->Pointer и обратно. Проблема состоит в том, что при преобразовании Pointer->IUnknown экземпляр уже вполне может быть уничтожен. Для безопасного преобразования была написана функция:


[Skip]

K>Все было бы хорошо, но вот почемуто иногда, после уничтожения экземпляра, она приводит к зависанию приложения. Может кто нибудь предолжет более корректную реализацию данной функции?


Самое замечательное в твое проблеме, что надежного решения при привидении IUnknown в Pointer, а потом назад когда Instance может быть уничтожен, просто нету. Простой твой вызов QueryInterface может протереть память и запороть всю аппликацию.

Напрашивается вопрос: для чего была произведено приведение IUnknown в Pointer. Есть догадка — для того чтобы запихтуть эго в какую то структуру, которая поддерживает только указатели.

Напрашивается несколько выходов:

1. Во внутренней структуре сохранять интерфейсы а не указатели. Самое правильное но не всегда реализуемо.

2. При приведении в Pointer вызвать принудительно aIntf._AddRef. Таким образом мы заблокируем уничтожение обьекта. Потом когда внутрення структура будет чистится вызвать _Release. Так мы избавимся от ликов памяти.

3. Создать механизм нотификаци. Тоесть когда обьект уничтожается то он нотифицирует подписчиков что они дожны обновить свои данные.

4. Дальше полет фантазии и смекалки...
Re[2]: SafePointerToInterface
От: Karluha  
Дата: 10.11.05 13:50
Оценка:
D>Напрашивается вопрос: для чего была произведено приведение IUnknown в Pointer. Есть догадка — для того чтобы запихтуть эго в какую то структуру, которая поддерживает только указатели.

Необходимо это для организации взаимных ссылок между двумя экземплярами, например Parent-Child. Это возможно только через Pointer, особенно если экземпляры описаны в разных, ничего не знающих друг о друге Library. Это значит что и нотификаций быть никаких не может, т.к. я ссылаюсь на чужой экземпляр IUnknown и при этом не хочу его держать.
Re[3]: SafePointerToInterface
От: Danchik Украина  
Дата: 10.11.05 14:41
Оценка:
Здравствуйте, Karluha, Вы писали:

D>>Напрашивается вопрос: для чего была произведено приведение IUnknown в Pointer. Есть догадка — для того чтобы запихтуть эго в какую то структуру, которая поддерживает только указатели.


K>Необходимо это для организации взаимных ссылок между двумя экземплярами, например Parent-Child. Это возможно только через Pointer, особенно если экземпляры описаны в разных, ничего не знающих друг о друге Library. Это значит что и нотификаций быть никаких не может, т.к. я ссылаюсь на чужой экземпляр IUnknown и при этом не хочу его держать.


Тебе необходимо организовать механизм WeakReference.
Вот набросал за пару минут пример реализации такого взаимодействия (untested):

unit Unit1;

interface

uses
  Windows, SysUtils, Classes;

type

  TWeakReference = class;

  IParent = interface
  ['{1D408CF3-962A-4FAE-ABC4-1910F6F89205}']
    procedure AddWeakRefernce (aReference : TWeakReference);
    procedure RemoveWeakRefernce (aReference : TWeakReference);
  end;

  TAbstractParent = class(TInterfacedObject, IParent)
  private
    FReferences : TList;
  protected
    procedure AddWeakRefernce (aReference : TWeakReference);
    procedure CleanupWeakReferences;
    function NeedReferencesList: TList;
    procedure RemoveWeakRefernce (aReference : TWeakReference);
  public
    destructor Destroy; override;
  end;

  TWeakReference = class
  private
    FReference: IUnknown;
  protected
    function GetReference: IUnknown;
    procedure SetReference(Value: IUnknown);
  public
    constructor Create (aReference : IUnknown);
    destructor Destroy; override;
    property Reference: IUnknown read GetReference write SetReference;
  end;

  TAbstractChild = class
  private
    FParent : TWeakReference;
  protected
    function GetParent: IUnknown;
    procedure SetParent(Value: IUnknown);
  public
    constructor Create;
    destructor Destroy; override;
    property Parent: IUnknown read GetParent write SetParent;
  end;

implementation

//==================================================================================================
// class TWeakReference
//==================================================================================================

constructor TWeakReference.Create(aReference: IInterface);
begin
  inherited Create;
  Reference := aReference;
end;

destructor TWeakReference.Destroy;
begin
  Reference := nil;
  inherited;
end;

function TWeakReference.GetReference: IUnknown;
begin
  Result := FReference;
end;

procedure TWeakReference.SetReference(Value: IUnknown);
var
  aParent : IParent;
begin
  if FReference = Value then
    Exit;

  if Supports (FReference, IParent, aParent) then
    aParent.RemoveWeakRefernce(Self);

  if Supports (Value, IParent, aParent) then
    aParent.AddWeakRefernce(Self);

  Pointer (FReference) := Pointer (Value);
end;

//==================================================================================================
// class TAbstractParent
//==================================================================================================

destructor TAbstractParent.Destroy;
begin
  CleanupWeakReferences;
  inherited;
end;

procedure TAbstractParent.AddWeakRefernce(aReference: TWeakReference);
begin
  NeedReferencesList.Add (aReference);
end;

procedure TAbstractParent.CleanupWeakReferences;
var
  K: Integer;
begin
  if FReferences = nil then
    Exit;

  { In this place pefrormance can be increased }
  while (FReferences <> nil) and (FReferences.Count > 0) do
    TWeakReference (FReferences [FReferences.Count - 1]).Reference := nil;

  FreeAndNil (FReferences);
end;

function TAbstractParent.NeedReferencesList: TList;
begin
  if FReferences = nil then
    FReferences := TList.Create;
  Result := FReferences;
end;

procedure TAbstractParent.RemoveWeakRefernce(aReference: TWeakReference);
begin
  if FReferences = nil then
    Exit;

  FReferences.Remove(aReference);
  if FReferences.Count = 0 then
    FreeAndNil (FReferences);
end;

//==================================================================================================
// class TAbstractChild
//==================================================================================================

constructor TAbstractChild.Create;
begin
  FParent := TWeakReference.Create(nil);
end;

destructor TAbstractChild.Destroy;
begin
  FParent.Free;
  inherited;
end;

function TAbstractChild.GetParent: IUnknown;
begin
  Result := FParent.Reference;
end;

procedure TAbstractChild.SetParent(Value: IUnknown);
begin
  if Parent = Value then
    Exit;

  FParent.Reference := Value;
  { in this place you should add code that appends this object as a children to a Parent}
end;

end.

Удачи!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.