Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 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]

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

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


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



Страницы: 1 вся ветка

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

Наверх





Память: 0.5 MB
Время: 0.006 c
1-57710
RightCost
2002-04-03 23:05
2002.04.18
Как программно проверить, активна ли фарма?


1-57759
Митрий
2002-04-05 16:32
2002.04.18
Помогите чайнику. Не разобраться с TExcelworksheet


1-57747
Дима2
2002-04-04 13:11
2002.04.18
Программа


1-57802
LazorenkoX
2002-04-04 22:00
2002.04.18
Консоль вопрос 2


1-57667
vlv
2002-04-04 21:32
2002.04.18
Проблема с MDIForm





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский