Форум: "Основная";
Текущий архив: 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