Форум: "Основная";
Поиск по всему сайту: delphimaster.net;
Текущий архив: 2002.04.18;
Скачать: [xml.tar.bz2];




Вниз

Interface и указатель 


NDelphist   (2002-04-03 22:52) [0]

Есть такая вещь:


IMyInterface=interface
["{2D67E340-4705-11D6-A1F0-444553540000}"]
function Funct:integer;
end;

TMyClass=class(TInterfacedObject,IMyInterface)
function Funct:integer;
end;

var
Form1: TForm1;
QI:function (IID:TGUID; out Obj):Longint;;

implementation

{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
cl:TMyClass;
p,p1:pointer;
begin
cl:=TMyClass.Create;
cl.GetInterface(IMyInterface,p);
QI:=p;
QI(IMyInterface,p1); //Invalid pointer operation
cl._Release;
end;

function TMyClass.Funct:integer;
begin
Result:=3;
end;



Почему я не могу вызвать эту функцию по адресу? По идее, GetInterface возвращает указатель на vtable и соответсвенно должна быть возможность вызывать методы интерфейса по их адресам. Почему же здесь это не работает?



BBt   (2002-04-04 00:35) [1]

???



Digitman   (2002-04-04 08:13) [2]

Начнем с того, что вот эта строчка QI:=p; - нонсенс.
QI - переменная процедурного типа,
в то время как P содержит на момент выполнения строчки содержит отнюдь не адрес некоей ф-ции, а указатель на интерфейсный объект



Dimka Maslov   (2002-04-04 10:06) [3]


IMyInterface=interface
["{2D67E340-4705-11D6-A1F0-444553540000}"]
function Funct:integer;
end;

TMyClass=class(TInterfacedObject,IMyInterface)
function Funct:integer;
end;

var
Form1: TForm1;

implementation

{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
p: IMyInterface;
begin
TMyClass.Create.GetInterface(IMyInterface, p);

// Здесь ты вызываешь методы интерфейса

ShowMessageFmt("IMyInterface.Funct returned: %d", [p.Funct]);

// Free или _Release вызывать не надо, всё вызовется автоматически

end;

function TMyClass.Funct:integer;
begin
Result:=3;
end;



NDelphist   (2002-04-04 10:27) [4]

>Начнем с того, что вот эта строчка QI:=p; - нонсенс.
>QI - переменная процедурного типа,
>в то время как P содержит на момент выполнения строчки содержит >отнюдь не адрес некоей ф-ции, а указатель на интерфейсный объект

Так, отлично, а что по вашему такое "переменная процедурного типа" и "указатель на интерфейсный объект объект"?
Что по вашему происходит, когда вы делаете так:

var
proc:procedure;
begin
...
proc:=GetProcAddress(_Mod,"SomeProc");
end;

GetProcAddress возвращает то адрес(целое 4 байта)!
Попробуйте так:

var
proc:procedure;
p:pointer;
begin
...
p:=GetProcAddress(_Mod,"SomeProc");
proc;
end;

К вашему удивлению, такая вещь заработает! Хотя ладно, положим это вы и так знали. Тогда далее...

Нет никаких интерфейсных объектов. Интерфейс - это vtable(таблица методов), иначе было бы невозможно обеспечить унифицированный стандарт. По стандарту, такая вещь должна работать, но по видимому как-то не так.



NDelphist   (2002-04-04 10:32) [5]

To: Dimka Maslov © (04.04.02 10:06)

Ну знаете, как работать правильно я знаю, мне интересно, почему так не работает.
А вот такие вещи:


begin
TMyClass.Create.GetInterface(IMyInterface, p);

// Free или _Release вызывать не надо, всё вызовется автоматически
end;

Мне вообще не нравятся - это похоже на сборку мусора. Я предпочитаю, чтобы все было под моим контролем - я создаю объект, я его и уничтожаю, когда мне этого захочеться.

Ну?? Где обещанные Мастера Delphi? Ответье мне!



Dimka Maslov   (2002-04-04 11:05) [6]

если ты предпочитаешь, чтобы все было под твоим контролем - ты создаёшь объект, ты его и уничтожаю, когда тебе этого захочется, то наследуй объект, реализующий интерфейс не от TInterfacedObject, а от TObject, и твой код должен выглядеть так

IMyInterface=interface
["{2D67E340-4705-11D6-A1F0-444553540000}"]
function Funct:integer;
end;

TMyClass=class(TObject, IMyInterface)
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
function Funct:integer;
end;

function TMyClass._AddRef: Integer;
begin
Result:=-1;
end;

function TMyClass._Release: Integer;
begin
Result:=-1;
end;

function TMyClass.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
if GetInterface(IID, Obj) then Result:=S_OK else Result:=E_NOINTERFACE;
end;

function TMyClass.Funct;
begin
Result:=3;
end;

function TForm1.Button1Click(Sender: TObject);
var
Obj: TMyClass;
Ifc: IMyInterface;
begin
Obj:=TMyClass.Create;
try
Obj.GetInterface(IMyInterface, Ifc);

// Делаешь все что захочешь с Ifc, но всё равно вызывать Ifc._Release не надо

finally
Obj.Free;
end;
end;


А теперь разберёмся понятиями "переменная процедурного типа" "указатель на интерфейс" и просто "указатель".

Как известно, указатель есть переменная содержая адрес ячейки памяти, безотносительно тому, что содержится в этой ячейке.

Переменная процедурного типа - тоже указатель, но указывает он на точку входа в конкретную процедуру.

Указатель на интерфейс - есть указатель на таблицу методов интерфейса, перым членом которой является указатель на метод QueryInterface.

Разберёмся теперь с почему не работает твой первый пример и работает второй...

procedure TForm1.Button1Click(Sender: TObject);
var
cl:TMyClass;
p,p1:pointer;
begin
cl:=TMyClass.Create;
// Создание объекта
cl.GetInterface(IMyInterface, p);
// получение указателя на интерфейс и запись его в нетипизированный указатель.
QI:=p;
// связывание переменной продедурного типа с нетипизированным указателем, указывающим на таблицу методов, первым из которых стоит метод function (Self: IMyInterface; const IID: TGUID; out Obj): HResult stdcall; ты же присваиваешь адрес этой процедуры переменной, которая ссылается на процедуру function (IID: TGUID; out Obj): LongInt register;. Имеющую разные аргументы и разный формат вызова, что неминуемо приведет к ошибке при вызове такой процедуры через указатель...
QI(IMyInterface,p1); //Invalid pointer operation
// что и происходит
cl._Release;
end;

Во втором случае ты связываешь procedure с procedure по этому всё работает.



reonid   (2002-04-04 12:21) [7]

Вообще говоря, указатель на интерфейс - это не указатель на
vtable, а указатель на поле объекта, в котором хранится адрес
vtable и корорый идёт в методы интерфейса в качестве Self.



Digitman   (2002-04-04 12:47) [8]

>NDelphist

Ну ты и ты намудрил, однако !)
Я так до конца и не сообразил, чего же ты хотел добиться выполнением приведенного фр-та кода.

Вот простейший - классический ! - пример создания интерфейсного объекта, запроса у объекта необходимого интерфейса, работа с интерфейсом и разрушение интерфейсного объекта :


var
MyIntf: IMyInterface;
i: Integer;
...
MyIntf :=TMyClass.Create as IMyInterface;
i:= MyIntf.Funct;
MyIntf:= nil;// необязательно, если MyIntf - локальная пер-я



NDelphist   (2002-04-04 19:57) [9]

Aга, вижу что-то похожее на правду, наконец! :)

>Как известно, указатель есть переменная содержая адрес ячейки
> памяти, безотносительно тому, что содержится в этой ячейке.

>Переменная процедурного типа - тоже указатель, но указывает он
>на точку входа в конкретную процедуру.

>Указатель на интерфейс - есть указатель на таблицу методов
>интерфейса, перым членом которой является указатель на метод >QueryInterface.

Спасибо, но это я, конечно же, знаю, иначе бы я так и не
пробывал бы делать.

>метод function (Self: IMyInterface; const IID: TGUID; out Obj):
>HResult stdcall; ты же присваиваешь адрес этой процедуры
>переменной, которая ссылается на процедуру function (IID:
>TGUID; out Obj): LongInt register;.

А вот это вот уже похоже на правду. Ну, допустим, stdcall нам подставить не тяжело - это не причина. Логично, что первым аргументом пытается передаться указатель на экземпляр вызывающей стороны. Тогда как начет этого:

type

SomeI=packed record
QI:function (IID:TGUID; out Obj):Longint; stdcall;
AddRef:function:LongInt; stdcall;
Release:function:LongInt; stdcall;
end;

var
p:^SomeI;
begin
...
<>.QueryInterface(IID,p);
p^.QI(IID,p1);

?

А во! Дошло. Надо вместо record класс использовать. А то record"ы в паскале не такие развитые как в С++, тут он наверняка просто разиминуется и получится тоже, что и в начале.

Obj:=TMyClass.Create;
try
Obj.GetInterface(IMyInterface, Ifc);

// Делаешь все что захочешь с Ifc, но всё равно вызывать Ifc._Release не надо

finally
Obj.Free;
end;


Вот это вот мне все равно не нравиться. То, что Delphi вызывает Realese за меня - это конечно очень любезно с его стороны, однако может выроботать плохую привычку. Во некоторых распространненых средах (VC++) такие вещи, понятно что, не делаются.

Спасибо Dimka! А то я возможно бы так еще долго тормозил. :)

P.S. renoid вроде как правильно сказал, но как-то не понятно, то ли он имеет ввиду.



NDelphist   (2002-04-04 20:10) [10]

Заработала! Хе-хе.


TIClass=class
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
function _AddRef: Integer; virtual; stdcall;
function _Release: Integer; virtual; stdcall;
function Funct:integer; virtual;
end;

...
var
i:integer;
cl:TMyClass;
p:TIClass;
begin
i:=0;
cl:=TMyClass.Create;

cl.GetInterface(IMyInterface,p);
p.QueryInterface(IMyInterface,p1);

i:=p.Funct;
...
end;



Как я и предпологал. Еще раз спасибо Dimka! :)



Dimka Maslov   (2002-04-05 09:41) [11]

Если не хочешь, чтобы Delphi за тебя вызывало _Release, то воспользуйся таким приёмом:

P._Release;
Integer(P):=0;


И ещё зачем ты два раза запрашиваешь указатель на интерфейс?
Достаточно одного вызова cl.GetInterface.



NDelphist   (2002-04-05 10:00) [12]


> Если не хочешь, чтобы Delphi за тебя вызывало _Release,
> то воспользуйся таким приёмом:
>
> P._Release;
> Integer(P):=0;
>
> И ещё зачем ты два раза запрашиваешь указатель на интерфейс?
> Достаточно одного вызова cl.GetInterface.


Да ладно уж, как от этого избавиться придумать не тяжело, не в этом смысл. Проще как я выше сделал - класс вместо интерфейса подставлять.

А низачем. Для проверки личности. А тож нам interface подсунули и пользуйся им, а я хотел убедиться, что все по стандарту. Вначале я серьезно начал тормозить по поводу невозможности выызвать функцию интерфейса по ее адресу, а ведь должна же она именно так вызываться! Но с твоей подсказки до меня все дошло, в чем я и убедился. :)



reonid   (2002-04-05 10:22) [13]

Я имел в виду только то, что ты два разыменования потерял:

TQI = function(Self: IUnknown; const IID: TGUID; out Obj): HResult; stdcall;
QI := Pointer(Pointer(Pointer(p)^)^);
QI(p, IMyInterface, p1);

(для понимания, как бензин по проводам течёт, а не для того, чтобы так на самом деле делать)



NDelphist   (2002-04-05 19:44) [14]

Ага. Я так и подумал. Было у меня такое подозрение, я даже так и пробывал, только тип функции у меня все-таки был не правильный поэтому все равно не сработало. А потом(с правильным типом) я этого не проверял

> (для понимания, как бензин по проводам течёт, а не для того,
> чтобы так на самом деле делать)


Хе. :)
Ну это то понятно.




Форум: "Основная";
Поиск по всему сайту: delphimaster.net;
Текущий архив: 2002.04.18;
Скачать: [xml.tar.bz2];




Наверх





Память: 0.77 MB
Время: 0.032 c
14-57843          Sandra                2002-03-07 20:54  2002.04.18  
Даме на 8-е марта :-) нужен текст из из ячейки DBGrid (Стандартный компонент)


14-57869          dima_                 2002-03-11 13:35  2002.04.18  
Подскажите как перенести текст из Тmemo в memo ячейку таблицы...


3-57586           TriNeT                2002-03-25 06:36  2002.04.18  
Вопрос по ADO+ODBC+Paradox


1-57734           Sergey Karagodin      2002-04-05 09:21  2002.04.18  
Какое событие


1-57727           новенький в Делфи     2002-04-07 22:22  2002.04.18  
Закрытие формы