powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Delphi [игнор отключен] [закрыт для гостей] / Изменился способ вызова (экземпляр класса) для QueryInterface
27 сообщений из 27, показаны все 2 страниц
Изменился способ вызова (экземпляр класса) для QueryInterface
    #39004384
Фотография _Vasilisk_
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Я уже дважды сталкивался с несовместимостью кода на XE. Это работа с отрицательным TDateTime и реализацией интерфейсов через property implements. В обоих случаях эмбаркадера исправила старые ошибки, но это были те баги, которые уже воспринимались как фичи. В обоих случаях мне понадобился день на рихтовку всего своего кода. Я не знаю, как нужно делать правильно, но один день - это не повод рыдать, что "все пропало"
...
Рейтинг: 0 / 0
Изменился способ вызова (экземпляр класса) для QueryInterface
    #39004510
Kazantsev Alexey
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
_Vasilisk_реализацией интерфейсов через property implements. В обоих случаях эмбаркадера исправила старые ошибки, но это были те баги, которые уже воспринимались как фичи
Можно поподробнее?
...
Рейтинг: 0 / 0
Изменился способ вызова (экземпляр класса) для QueryInterface
    #39004581
Фотография _Vasilisk_
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Kazantsev Alexey_Vasilisk_реализацией интерфейсов через property implements. В обоих случаях эмбаркадера исправила старые ошибки, но это были те баги, которые уже воспринимались как фичи
Можно поподробнее?Раньше QueryInterface вызывался у того объекта, которые реализовывал интерфейс, сейчас ВСЕГДА вызывается у основного объекта
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
TClassChild = class(.., IIntf1, IIntf2)
  .......
end;

TClassMain = class(..., IntfMain, Intf1)
  property Child: TClassChild implements Intf1;
  .........
end;

var
  Main: IIntfMain;
  Child2: IIntf1;
  Child2: IIntf2;
begin
  Main := TClassMain.Create;
  OleCheck(Main.QueryInterface(Intf1, Child1));
  OleCheck(Child1.QueryInterface(Intf2, Child2));
end;

В 2007 выделенная строка работала, в ХЕ3 перестала

Я перешел с 2007 на XE3, поэтому не могу сказать в какой именно версии это изменилось
...
Рейтинг: 0 / 0
Изменился способ вызова (экземпляр класса) для QueryInterface
    #39004661
Kazantsev Alexey
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
_Vasilisk_Раньше QueryInterface вызывался у того объекта, которые реализовывал интерфейс, сейчас ВСЕГДА вызывается у основного объекта
Прикольная ситуация. Сейчас потрейсил 2007 и XE3 на предмет работы Implements. Там действительно довольно странно формируются vtbl's (кому интересно трейсите GetInterface, прикол в том, что ветка InvokeImplGetter не отрабатывает ни разу, т.е. ни в 2007 ни в XE3 implements не работает, как было задумано изначально. Правда, на первый взгляд, в 2007 ещё и крайне не эффективно), что в 2007, что в XE3. Однако, если использовать для агрегируемых интерфейсов в качестве родителя базовый TAggregatedObject, то внешне код работает одинаково.

Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils;

type
iintfmain = interface
['{8DC225A7-F163-491B-9A38-08D053796A70}']
end;
Iintf1 = interface
['{FA0DD32B-2441-44A7-A0F0-1BE458ABFD6E}']
end;
Iintf2 = interface
['{74C7DE0E-2044-4608-9301-01578022F1F2}']
end;

TClassChild = class(taggregatedobject, IIntf1, IIntf2)
end;

TClassMain = class(TInterfacedObject, IIntfMain, IIntf1)
 function getcc : TClassChild;
 property Child: TClassChild read getcc implements IIntf1;

end;

{ TClassMain }

function TClassMain.getcc: TClassChild;
begin
 result := TClassChild.Create(self);
end;

var
  Main: IIntfMain;
  Child1: IIntf1;
  Child2: IIntf2;

begin

  Main := TClassMain.Create;
  writeln(Main.QueryInterface(IIntf1, Child1));
  writeln(Child1.QueryInterface(IIntf2, Child2));

  readln;

end.

...
Рейтинг: 0 / 0
Изменился способ вызова (экземпляр класса) для QueryInterface
    #39004754
Фотография _Vasilisk_
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Kazantsev AlexeyОднако, если использовать для агрегируемых интерфейсов в качестве родителя базовый TAggregatedObject, то внешне код работает одинаково.Ну дык
Код: pascal
1.
2.
3.
4.
function TAggregatedObject.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
  Result := IInterface(FController).QueryInterface(IID, Obj);
end;

Если мне не изменяет память, то с _AddRef и _Release то же самое. Т.е. стал использоваться счетчик ссылок родителя. Иными словами, все интерфейсы стали агрегируемые
Kazantsev Alexeyimplements не работает, как было задумано изначальноА как было задумано?
...
Рейтинг: 0 / 0
Изменился способ вызова (экземпляр класса) для QueryInterface
    #39004782
Kazantsev Alexey
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
_Vasilisk_Ну дык
Это понятно. Тут другой вопрос, почему у тебя не от TAggregatedObject наследование было сделано, ведь он для целей аггрегации и предназначен.

_Vasilisk_А как было задумано?
Следуя логике GetInterface, если в таблице интерфейсов класса найдена запись в которой нет ссылки на vtbl (т.е. это те самые интерфейсы implemets...), то должен быть вызван метод InvokeImplGetter, который, в свою очередь, основываясь на флагах получит vtbl из поля объекта или через вызов геттера. Сейчас этот механизм не работает. На примере 2007 это очень хорошо видно - она на каждый чих с аггрегируемым интерфейсом зовёт геттер. О чём это говорит? О том, что компилятором формируется специальная vtbl, которая имеет ссылки на методы в, грубо говоря, таком виде: _AddRef - > InvokeGetter + _AddRef; В XE3 немного по другому, там IUnknown ссылается на родительский класс, а для кастомных методов работает механизм аналогичный 2007. Дурдом.
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils;

type
iintfmain = interface
['{8DC225A7-F163-491B-9A38-08D053796A70}']
end;
Iintf1 = interface
['{FA0DD32B-2441-44A7-A0F0-1BE458ABFD6E}']
 procedure test1;
 procedure test2;
end;
Iintf2 = interface
['{74C7DE0E-2044-4608-9301-01578022F1F2}']
end;

TClassChild = class(taggregatedobject, IIntf1, IIntf2)
 procedure test1;
 procedure test2;
end;

TClassMain = class(TInterfacedObject, IIntfMain, IIntf1)
 function getcc : TClassChild; virtual;
 property Child: TClassChild read getcc implements IIntf1;

end;

{ TClassMain }

function TClassMain.getcc: TClassChild;
begin
 result := TClassChild.Create(self);
 writeln('getter invoked');
end;

var
  Main: IIntfMain;
  Child1: IIntf1;
  Child2: IIntf2;

{ TClassChild }

procedure TClassChild.test1;
begin

end;

procedure TClassChild.test2;
begin

end;

begin

  Main := TClassMain.Create;
  writeln(Main.QueryInterface(IIntf1, Child1));
   child1.test1;
   child1.test2;
  writeln(Child1.QueryInterface(IIntf2, Child2));

  readln;

end.

...
Рейтинг: 0 / 0
Изменился способ вызова (экземпляр класса) для QueryInterface
    #39004788
Kazantsev Alexey
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Сейчас проверил FPC 3.1.1 c этим кодом и логированием. Всё работает ровно так, как оно ожидается.
Код для FPC
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils;

type
iintfmain = interface
['{8DC225A7-F163-491B-9A38-08D053796A70}']
end;
Iintf1 = interface
['{FA0DD32B-2441-44A7-A0F0-1BE458ABFD6E}']
 procedure test1;
 procedure test2;
end;
Iintf2 = interface
['{74C7DE0E-2044-4608-9301-01578022F1F2}']
end;

{ TClassChild }

TClassChild = class(taggregatedobject, iinterface, IIntf1, IIntf2)
 procedure test1;
 procedure test2;
 function QueryInterface(constref iid : tguid;out obj) : longint;stdcall;


end;

{ TClassMain }

TClassMain = class(TInterfacedObject, iunknown, IIntfMain, IIntf1)
 function getcc : TClassChild; virtual;
 property Child: TClassChild read getcc implements IIntf1;
 function QueryInterface(constref iid : tguid;out obj) : longint;stdcall;

end;

{ TClassMain }

function TClassMain.getcc: TClassChild;
begin
  writeln('getter invoked');
 result := TClassChild.Create(self);
end;

function TClassMain.QueryInterface(constref iid: tguid; out obj): longint;
  stdcall;
begin
  writeln('main queryinterface');
  result := inherited;
end;

var
  Main: IIntfMain;
  Child1: IIntf1;
  Child2: IIntf2;

{ TClassChild }

procedure TClassChild.test1;
begin

end;

procedure TClassChild.test2;
begin

end;

function TClassChild.QueryInterface(constref iid: tguid; out obj): longint;
  stdcall;
begin
  writeln('child queryinterface');
  result := inherited;
end;

begin

  Main := TClassMain.Create;
  writeln(Main.QueryInterface(IIntf1, Child1));
   child1.test1;
   child1.test2;
  writeln(Child1.QueryInterface(IIntf2, Child2));

  readln;

end.


Вывод:
Код: plaintext
1.
2.
3.
4.
5.
main queryinterface
getter invoked
0
child queryinterface
main queryinterface
-2147467262

Ну что за криворучки пилять дульфу...
...
Рейтинг: 0 / 0
Изменился способ вызова (экземпляр класса) для QueryInterface
    #39004797
Kazantsev Alexey
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
А это вывод 2007:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
main queryinterface
getter invoked
0
getter invoked
getter invoked
getter invoked
child queryinterface
main queryinterface
-2147467262

XE3:
Код: plaintext
1.
2.
3.
4.
5.
6.
main queryinterface
0
getter invoked
getter invoked
main queryinterface
-2147467262

XE8:
Код: plaintext
1.
2.
3.
4.
5.
6.
main queryinterface
0
getter invoked
getter invoked
main queryinterface
-2147467262
...
Рейтинг: 0 / 0
Изменился способ вызова (экземпляр класса) для QueryInterface
    #39004838
Фотография _Vasilisk_
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Kazantsev AlexeyТут другой вопрос, почему у тебя не от TAggregatedObject наследование было сделаноНе будем о плохом. Проекту больше 10 лет. Видел бы ты, какие там костыли используются...
Kazantsev AlexeyА это вывод 2007:Но это же полный сюр. Ты хочешь сказать, что каждый раз при вызове метода дочернего интерфейса создается новый объект?
...
Рейтинг: 0 / 0
Изменился способ вызова (экземпляр класса) для QueryInterface
    #39004842
Kazantsev Alexey
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
_Vasilisk_Но это же полный сюр. Ты хочешь сказать, что каждый раз при вызове метода дочернего интерфейса создается новый объект?
Всякий раз дёргается геттер. Ну а если в геттере создаётся объект... Это и правда полный сюр
...
Рейтинг: 0 / 0
Изменился способ вызова (экземпляр класса) для QueryInterface
    #39005155
Фотография _Vasilisk_
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ха-ха-ха!
Delphi XE3
Test1 Source
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
program TestIntf;

{$APPTYPE CONSOLE}

type
  IMain = interface
    ['{17D5931B-CF56-4492-B325-89BD7F29BD60}']
  end;

  IChild = interface
    ['{5A05EB4B-8F1F-4BE0-8559-313BA835C0B3}']
    procedure Test;
  end;

  TChild = class(TAggregatedObject, IChild)
    FIdx: Integer;
  public
    procedure Test;
    constructor Create(const AOwner: IInterface);
    destructor Destroy; override;
  end;

  TMain = class(TInterfacedObject, IMain, IChild)
    function GetChild: TChild;
    property Child: TChild read GetChild implements IChild;
  end;

var
  GCreatedChild: Integer;
  GExistingChild: Integer;

procedure PrintInfo;
begin
  Writeln('Created: ', GCreatedChild);
  Writeln('Exists : ', GExistingChild);
end;

procedure Test;
var
  LMain: IMain;
  LChild: IChild;
begin
  LMain := TMain.Create;
  LMain.QueryInterface(IChild, LChild);
  LChild.Test;
  LChild.Test;
  LChild.Test;
end;

{ TChild }

constructor TChild.Create(const AOwner: IInterface);
begin
  inherited Create(AOwner);
  Writeln('TChild.Create');
  Inc(GCreatedChild);
  Inc(GExistingChild);
end;

destructor TChild.Destroy;
begin
  Dec(GExistingChild);
  inherited Destroy;
end;

procedure TChild.Test;
begin
  Inc(FIdx);
  Writeln;
  Writeln('Idx: ', FIdx);
  PrintInfo;
end;

{ TMain }

function TMain.GetChild: TChild;
begin
  Writeln('TMain.GetChild');
  Result := TChild.Create(Self);
end;

begin
  Test;
  Writeln;
  Writeln('Summary');
  PrintInfo;
  Readln;
end.

Test1 Output
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
TMain.GetChild
TChild.Create
TMain.GetChild
TChild.Create

Idx: 1
Created: 2
Exists : 2
TMain.GetChild
TChild.Create

Idx: 1
Created: 3
Exists : 3
TMain.GetChild
TChild.Create

Idx: 1
Created: 4
Exists : 4
TMain.GetChild
TChild.Create

Summary
Created: 5
Exists : 5

Test2 Source
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
program TestIntf;

{$APPTYPE CONSOLE}

type
  IMain = interface
    ['{17D5931B-CF56-4492-B325-89BD7F29BD60}']
  end;

  IChild = interface
    ['{5A05EB4B-8F1F-4BE0-8559-313BA835C0B3}']
    procedure Test;
  end;

  TChild = class(TAggregatedObject, IChild)
    FIdx: Integer;
  public
    procedure Test;
    constructor Create(const AOwner: IInterface);
    destructor Destroy; override;
  end;

  TMain = class(TInterfacedObject, IMain, IChild)
    function GetChild: IChild;
    property Child: IChild read GetChild implements IChild;
  end;

var
  GCreatedChild: Integer;
  GExistingChild: Integer;

procedure PrintInfo;
begin
  Writeln('Created: ', GCreatedChild);
  Writeln('Exists : ', GExistingChild);
end;

procedure Test;
var
  LMain: IMain;
  LChild: IChild;
begin
  LMain := TMain.Create;
  LMain.QueryInterface(IChild, LChild);
  LChild.Test;
  LChild.Test;
  LChild.Test;
end;

{ TChild }

constructor TChild.Create(const AOwner: IInterface);
begin
  inherited Create(AOwner);
  Writeln('TChild.Create');
  Inc(GCreatedChild);
  Inc(GExistingChild);
end;

destructor TChild.Destroy;
begin
  Dec(GExistingChild);
  inherited Destroy;
end;

procedure TChild.Test;
begin
  Inc(FIdx);
  Writeln;
  Writeln('Idx: ', FIdx);
  PrintInfo;
end;

{ TMain }

function TMain.GetChild: IChild;
begin
  Writeln('TMain.GetChild');
  Result := TChild.Create(Self);
end;

begin
  Test;
  Writeln;
  Writeln('Summary');
  PrintInfo;
  Readln;
end.

Test2 Output
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
TMain.GetChild
TChild.Create

Idx: 1
Created: 1
Exists : 1

Idx: 2
Created: 1
Exists : 1

Idx: 3
Created: 1
Exists : 1

Summary
Created: 1
Exists : 1
мораль: во втором случае геттер вызывается один раз, но утечки есть и там и там
Test3 source
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
  TChild = class(TInterfacedObject, IChild)
    FIdx: Integer;
  public
    procedure Test;
    constructor Create;
    destructor Destroy; override;
  end;

  TMain = class(TInterfacedObject, IMain, IChild)
    function GetChild: TChild;
    property Child: TChild read GetChild implements IChild;
  end;

Test3 Output
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
TMain.GetChild
TChild.Create
TMain.GetChild
TChild.Create

Idx: 1
Created: 2
Exists : 2
TMain.GetChild
TChild.Create

Idx: 1
Created: 3
Exists : 3
TMain.GetChild
TChild.Create

Idx: 1
Created: 4
Exists : 4
TMain.GetChild
TChild.Create

Summary
Created: 5
Exists : 5
никаких отличий от Test1
Test4 Source
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
  TChild = class(TInterfacedObject, IChild)
    FIdx: Integer;
  public
    procedure Test;
    constructor Create;
    destructor Destroy; override;
  end;

  TMain = class(TInterfacedObject, IMain, IChild)
    function GetChild: IChild;
    property Child: IChild read GetChild implements IChild;
  end;

Test4 Output
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
TMain.GetChild
TChild.Create

Idx: 1
Created: 1
Exists : 1

Idx: 2
Created: 1
Exists : 1

Idx: 3
Created: 1
Exists : 1

Summary
Created: 1
 Exists : 0 
Один вызов геттера и без утечек.

Все тесты специально обернуты в процедуру Test, чтобы освободились все интерфейсные ссылки

Справедливости ради, следует отметить, что, обычно, геттеры пишутся так
Код: pascal
1.
2.
3.
4.
5.
6.
function TMain.GetChild: IChild;
begin
  if FChild = nil then
    FChild := TChild.Create;
  Result := FChild;
end;
...
Рейтинг: 0 / 0
Изменился способ вызова (экземпляр класса) для QueryInterface
    #39005694
Kazantsev Alexey
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
_Vasilisk_Я перешел с 2007 на XE3, поэтому не могу сказать в какой именно версии это изменилось
Я проверил, поведение изменилось именно в XE3.

_Vasilisk_
Test4 Source
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
  TChild = class(TInterfacedObject, IChild)
    FIdx: Integer;
  public
    procedure Test;
    constructor Create;
    destructor Destroy; override;
  end;

  TMain = class(TInterfacedObject, IMain, IChild)
    function GetChild: IChild;
    property Child: IChild read GetChild implements IChild;
  end;

Test4 Output
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
TMain.GetChild
TChild.Create

Idx: 1
Created: 1
Exists : 1

Idx: 2
Created: 1
Exists : 1

Idx: 3
Created: 1
Exists : 1

Summary
Created: 1
 Exists : 0 
Один вызов геттера и без утечек.
И что ещё прикольно, в случае прямого указания использования интерфейсных типов отрабатывает InvokeImplGetter. Кстати, утечка при использовании TAggregatedObject это не утечка на самом деле, просто агрегируемые объекты считаются частью агрегирующего класса, а потому должны быть освобождены явно, в деструкторе.

_Vasilisk_Справедливости ради, следует отметить, что, обычно, геттеры пишутся так
Код: pascal
1.
2.
3.
4.
5.
6.
function TMain.GetChild: IChild;
begin
  if FChild = nil then
    FChild := TChild.Create;
  Result := FChild;
end;


Зависит от. Легко можно представить ситуацию, где делегируемый интерфейс реализуется свободно отчуждаемым кодом т.е. объект реализующий его не является частью состояния делегирующего объекта. У меня, кстати, такой код есть, правда автоматизированное делегирование (implements) там не используется (сделано через QueryInterface).
...
Рейтинг: 0 / 0
Изменился способ вызова (экземпляр класса) для QueryInterface
    #39005846
Фотография _Vasilisk_
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Kazantsev Alexeyутечка при использовании TAggregatedObject это не утечка на самом деле, просто агрегируемые объекты считаются частью агрегирующего класса, а потому должны быть освобождены явно, в деструкторе.Да. Но вот утечка с TInterfacedObject не объяснима
Kazantsev AlexeyЛегко можно представить ситуацию, где делегируемый интерфейс реализуется свободно отчуждаемым кодом т.е. объект реализующий его не является частью состояния делегирующего объекта.Не знаю. Я считаю такой подход ошибкой в архитектуре. Это лучше делать через Get-метод интерфейса
...
Рейтинг: 0 / 0
Изменился способ вызова (экземпляр класса) для QueryInterface
    #39005933
Kazantsev Alexey
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
_Vasilisk_Да. Но вот утечка с TInterfacedObject не объяснима
Если ты об утечке, когда геттер возвращает объект, а не интерфейс, то она объясняется перенаправлением IUnknown на делегирующий объект. Сделали, видимо, для уменьшения различий в поведении с ситуацией, когда делегируются объекты вообще не поддерживающие интерфейсы. То есть, если нет явного получения интерфейса, то считается, что делегируется просто объект (т.е. его методы для реализации интерфейса), даже в том случае если он имеет собственную реализацию IUnknown. Однако, поломали обратную совместимость.


_Vasilisk_Не знаю. Я считаю такой подход ошибкой в архитектуре. Это лучше делать через Get-метод интерфейса
Случаи, опять же, разные бывают. А семантика методов Get и Query очень различается.
...
Рейтинг: 0 / 0
Период между сообщениями больше года.
Изменился способ вызова (экземпляр класса) для QueryInterface
    #39572258
Фотография _Vasilisk_
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
И еще на одни грабли наступил.

Два совершенно разных интерфейса, но с одинаковым методом

Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
  IIntf1 = interface
    ['{09B51B7A-5314-4EB9-9DFE-8362F6960052}']
    function GetData: string;
  end;

  IIntf2 = interface
    ['{79D578C4-5444-45CF-865C-E818060D22D8}']
    function GetData: string;
  end;

(полный список методов может быть произвольным)

Реализация
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
  TIntf1 = class(TInterfacedObject, IIntf1)
    function GetData: string;
  end;

  TIntf2 = class(TInterfacedObject, IIntf1, IIntf2)
  strict private
    FIntf1: TIntf1;
  public
    constructor Create;
    destructor Destroy; override;
    function GetData: string;
  public
    property Intf1: TIntf1 read FIntf1 implements IIntf1;
  end;

{ TIntf1 }

function TIntf1.GetData: string;
begin
  Result := ClassName;
end;

{ TIntf2 }

constructor TIntf2.Create;
begin
  inherited Create;
  FIntf1 := TIntf1.Create;
  FIntf1._AddRef;
end;

destructor TIntf2.Destroy;
begin
  FIntf1._Release;
  inherited;
end;

function TIntf2.GetData: string;
begin
  Result := ClassName;
end;


Тест
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
procedure TestCase;
var
  LIntf1: IIntf1;
  LIntf2: IIntf2;
begin
  LIntf2 := TIntf2.Create;
  Writeln(LIntf2.GetData);
  LIntf2.QueryInterface(IIntf1, LIntf1);
  Writeln(LIntf1.GetData);
end;


вывод
D2007TIntf2
TIntf1DXE3TIntf2
TIntf2

При этом, если методы будут различаться по входным параметрам или возвращаемому результату, то все будет работать правильно
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
  IIntf1 = interface
    ['{09B51B7A-5314-4EB9-9DFE-8362F6960052}']
    function GetData: Integer;
  end;

  IIntf2 = interface
    ['{79D578C4-5444-45CF-865C-E818060D22D8}']
    function GetData: string;
  end;

function TIntf1.GetData: Integer;
begin
  Result := 1;
end;

DXE3TIntf2
1Даже изменение соглашения о вызове заставляет систему работать адекватно
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
  IIntf1 = interface
    ['{09B51B7A-5314-4EB9-9DFE-8362F6960052}']
    function GetData: string; stdcall;
  end;

  IIntf2 = interface
    ['{79D578C4-5444-45CF-865C-E818060D22D8}']
    function GetData: string;
  end;

DXE3TIntf2
TIntf1
...
Рейтинг: 0 / 0
Изменился способ вызова (экземпляр класса) для QueryInterface
    #39572337
white_nigger
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ну здесь какбэ ССЗБ. Нэймрезолвинг для интерфейсов для того и придуман, чтоб такой путаницы не было.
...
Рейтинг: 0 / 0
Изменился способ вызова (экземпляр класса) для QueryInterface
    #39572403
GunSmoker
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
А это не баг, случаем? Мож на QP его?
...
Рейтинг: 0 / 0
Изменился способ вызова (экземпляр класса) для QueryInterface
    #39572755
Фотография _Vasilisk_
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
white_niggerНэймрезолвинг для интерфейсов для того и придуман, чтоб такой путаницы не было.Ничего не понял. Какой неймрезолвинг? Я обращаюсь к конкретному методу конкретного интерфейса.
GunSmokerМож на QP его?Проверить бы его на Токио
...
Рейтинг: 0 / 0
Изменился способ вызова (экземпляр класса) для QueryInterface
    #39572950
GunSmoker
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
_Vasilisk_Проверить бы его на Токио

Да, на последнем билде (25.0.29039.2004) воспроизводится.
...
Рейтинг: 0 / 0
Изменился способ вызова (экземпляр класса) для QueryInterface
    #39573049
white_nigger
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
_Vasilisk_Ничего не понял. Какой неймрезолвинг? Я обращаюсь к конкретному методу конкретного интерфейсаСорри, просмотрел тонкости. Кстати вопрос остаётся, если для реализации использовать нэймрезолвинг, что-нибудь изменится? Завтра на Токио гляну...
...
Рейтинг: 0 / 0
Изменился способ вызова (экземпляр класса) для QueryInterface
    #39573268
kealon(Ruslan)
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
GunSmokerА это не баг, случаем? Мож на QP его?
это не баг, это правильно

владеющий объект может переопределить любой метод implements, собственно это и исправлено в XE3
...
Рейтинг: 0 / 0
Изменился способ вызова (экземпляр класса) для QueryInterface
    #39573502
GunSmoker
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
kealon(Ruslan)владеющий объект может переопределить любой метод implements, собственно это и исправлено в XE3

Да, похоже разное поведение в зависимости от того, как описано поле: как класс или как интерфейс:

implements :
автор Delegating to an Interface-Type Property
The delegate property must return an object whose class completely implements the interface specified by the implements directive, and which does so without method resolution clauses.

Delegating to a Class-Type Property
If the delegate property is of a class type, that class and its ancestors are searched for methods implementing the specified interface before the enclosing class and its ancestors are searched. Thus it is possible to implement some methods in the class specified by the property, and others in the class where the property is declared. Method resolution clauses can be used in the usual way to resolve ambiguities or specify a particular method.
...
Рейтинг: 0 / 0
Изменился способ вызова (экземпляр класса) для QueryInterface
    #39573950
Фотография _Vasilisk_
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
kealon(Ruslan)владеющий объект может переопределить любой метод implementsМдя. Спасибо, буду знать
kealon(Ruslan)собственно это и исправлено в XE3И опять на тему, что некоторые баги лучше не исправлять
...
Рейтинг: 0 / 0
Изменился способ вызова (экземпляр класса) для QueryInterface
    #39573980
GunSmoker
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
_Vasilisk_, поменяй FIntf1: TIntf1; на FIntf1: IIntf1; Не?
...
Рейтинг: 0 / 0
Изменился способ вызова (экземпляр класса) для QueryInterface
    #39573993
kealon(Ruslan)
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
_Vasilisk_kealon(Ruslan)собственно это и исправлено в XE3И опять на тему, что некоторые баги лучше не исправлятьэто серьёзный баг был
Фактически старое поведение, после исправления, я могу повторить довольно легко (зачем только непонятно)
Для обхода этого бага на XE2 придётся для вмещающего интерфейса вручную писать стабы к каждому методу, а для объекта reintroduce-ить методы, которые я хочу заменить, на пустые заглушки.
Пусть лучше это компилятор делает.
...
Рейтинг: 0 / 0
Изменился способ вызова (экземпляр класса) для QueryInterface
    #39573996
Фотография _Vasilisk_
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
GunSmokerпоменяй FIntf1: TIntf1; на FIntf1: IIntf1; Не?Да я понял уже. Тем более, что с учетом этого 17880935 придется для всех implents свойств писать геттер, который преобразует внутренний объект класса в интерфейс.

В общем крайне полезная информация выяснилась.

Спасибо большое
...
Рейтинг: 0 / 0
Изменился способ вызова (экземпляр класса) для QueryInterface
    #39574768
AX-Class
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
_Vasilisk_,

Вот тут некто советует юзать интерфейсы вместо объектов в implements, т.к. на объектах глюки.
17333895
...
Рейтинг: 0 / 0
27 сообщений из 27, показаны все 2 страниц
Форумы / Delphi [игнор отключен] [закрыт для гостей] / Изменился способ вызова (экземпляр класса) для QueryInterface
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]