Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "KOL";
Текущий архив: 2003.07.21;
Скачать: [xml.tar.bz2];

Вниз

KOLActiveX   Найти похожие ветки 

 
Fantasist   (2002-11-17 08:12) [0]

В связи с возникшим вопросом, предлагаю свое решение для обеспечения поддержки COM. Пока только основа.


unit KolActiveX;

interface
uses
windows,kol;

const
IUnknown_IID:TGUID="{00000000-0000-0000-C000-000000000046}";
IClassFactory_IID:TGUID="{00000001-0000-0000-C000-000000000046}";

type

//Interfaces

IUnknown = object
public
QueryInterface:function(const IID: TGUID; out Obj): HResult; stdcall;
AddRef:function:HResult; stdcall;
Release:function:HResult; stdcall;
end;
PIUnknown=^IUnknown;

InterfaceSet=packed record
_vmt:pointer; //таблица виртуальных методов для интерфейса
this:pointer; //указатель на экземпляр класса, которому интерфейс принадлежит
end;
PInterfaceSet=^InterfaceSet;


IClassFactory = object(IUnknown)
CreateInstance:function(const unkOuter: pointer; const iid: TGUID; out obj): HResult; stdcall;
LockServer:function(fLock: boolean): HResult; stdcall;
end;
PIClassFactory=^IClassFactory;

TInterfaceEntry=packed record
GUID:TGUID;
p:PInterfaceSet;
end;
PInterfaceEntry=^TInterfaceEntry;


TInterfacedObj=object(TObj)
private
//таблица интерфейсов. Фактически, динамический массив из TInterfaceEntry
fInterfaceTable:PInterfaceEntry;
fInterfaceCount:Integer;
//таблица для интерфейса IUnknown
fIUnknown:IUnknown;
fRefCount:Integer;
private
//IUnknown
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
function AddRef:Longint; stdcall;
function Release:Longint; stdcall;
protected
procedure SetInterfaceTable; virtual;
public
procedure Init; virtual;
procedure AddInterface(IID:TGUID; Ifc:PIUnknown);
function GetInterface(IID:TGUID; out Obj):boolean;
end;
PInterfacedObj=^TInterfacedObj;



implementation

function GUIDComp(GUID1,GUID2:TGUID):boolean;
var
i:integer;
begin
for i:=0 to 7 do
if GUID1.D4[i]<>GUID2.D4[i] then
begin
Result:=false;
exit;
end;
Result:=True and (GUID1.D1=GUID2.D1) and (GUID1.D2=GUID2.D2) and (GUID1.D3=GUID2.D3);
end;

{ TInterfacedObj }

procedure TInterfacedObj.AddInterface(IID: TGUID; Ifc: PIUnknown);
var
tmp:PInterfaceEntry;
begin
if GetInterface(IID,tmp) then
exit;

Inc(fInterfaceCount);
//выделяем новый блок памяти(на один болше) и копируем в него содержимое старого
GetMem(tmp,SizeOf(TInterfaceEntry)*fInterfaceCount);
if fInterfaceTable<>nil then
begin
Move(fInterfaceTable^,tmp^,SizeOf(TInterfaceEntry)*(fInterfaceCount-1));
FreeMem(fInterfaceTable);
end;
fInterfaceTable:=tmp;
//в новую выделенную запись записываем новый интерфейс
inc(tmp,fInterfaceCount-1);
tmp^.GUID:=IID;
New(tmp^.p);
tmp^.p^._vmt:=Ifc;
tmp^.p^.this:=@self;
end;

function TInterfacedObj.AddRef: Longint;
begin
asm
mov eax,ebp
add eax,$08
mov edx,[eax]
add edx,$04
mov edx,[edx]
mov [eax],edx
end;
Result := InterlockedIncrement(FRefCount);
end;

function TInterfacedObj.GetInterface(IID: TGUID; out Obj): boolean;
var
Entry:PInterfaceEntry;
i:integer;
begin
Result:=false;
Entry:=fInterfaceTable;
for i:=1 to fInterfaceCount do
begin
if GUIDComp(Entry^.GUID,IID) then
begin
Result:=True;
Pointer(Obj):=Entry^.p;
exit;
end;
inc(Entry);
end;
end;

procedure TInterfacedObj.Init;
begin
fInterfaceTable:=nil;
fInterfaceCount:=0;

fRefCount:=0;
SetInterfaceTable;
end;

function TInterfacedObj.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
asm
mov eax,ebp
add eax,$08
mov edx,[eax]
add edx,$04
mov edx,[edx]
mov [eax],edx
end;
if GetInterface(IID,Obj) then
begin
InterlockedIncrement(FRefCount);
Result:=S_OK;
end
else
Result:=E_NOINTERFACE;
end;

function TInterfacedObj.Release: Longint;
begin
asm
mov eax,ebp
add eax,$08
mov edx,[eax]
add edx,$04
mov edx,[edx]
mov [eax],edx
end;
Result := InterlockedDecrement(FRefCount);
if Result = 0 then
Dispose(@self);
end;

procedure TInterfacedObj.SetInterfaceTable;
begin
//заполняем таблицу
with fIUnknown do
begin
QueryInterface:=@TInterfacedObj.QueryInterface;
AddRef:=@TInterfacedObj.Addref;
Release:=@TInterfacedObj.Release;
end;
AddInterface(IUnknown_IID,@fIUnknown);
end;

end.


 
Fantasist   (2002-11-17 08:46) [1]

Итак. Идея такая: для каждого интерфейса, который мы хотим поддерживать, заводим переменную (пока у нас только IUnknown), в которую заносим указатели на методы нашего класса. Метод SetInterfaceTable служит для этого. Он сделан виртуальным, чтобы каждый потомок мог проинициализировать свои интерфейсы. С помощью метода AddInterface адрес переменной заноситься в таблицу интерфейсов, вместе с ассоциированным с ним GUID. Метод GetInterface находит соответсвующую таблицу по GUID.

В чем ключевая проблема? Проблема в том, что по стандарту интерфейс должен выглядить так:

<Объект>
_vmt -> Method1
Method2
Method3

Клиент получает указатель на <Объект>.Положим, что он вызывает
IUnknown.AddRef. Метод вызывается так:


var
vmt:pointer;
AddRef:function (Self:pointer):HResult;
begin
vmt:=IUnknown^; //получаем значение указателя на таблицу методов
AddRef:=Pointer(Integer(vmt^)+4)) //+4 смещение для метода AddRef
AddRef(IUnknown); //в качестве указателя на экземпляр передаем указатель на интерфейс.
end;


В моем методе, в качестве указателя на интерфейс мы передаем указатель на объект-подставу, искуственно созданный объект в котором мы сами сделали указатель на таблицу виртуальных методов (этот обект у меня типа TInterfaceEntry). Понятно, что когда вызовется объект AddRef, ему в качестве self передастся указатель на этот фиктивный объект. Тут-то и хитрость, что нам надо перезаписать self правильным значением. Для этого, фиктивный объект содержит содержит переменную this, в которую мы предусмотрительно записали правильное значение self. Код на асме в каждом методе как раз и занимается тем, что перезаписывает self этим значением, чтобы далее метод мог работать нормально.
Вот это самый кривой момент. В асме я ноль точка, еше много нулей с завершаю где-то еденицей, так что код наверняка на нем написан плохо. Но это не столь важно, главное то, что в каждом методе, который принадлежит интерфейсу, придется вставлять такой код. В паскале нет даже макроподстановок, чтобы можно было записать такую вещь в одну простую строчку. Более того, эти методы можно будет вызывать ТОЛЬКО через интерфейсы, иначе получиться непойми что.
Надеюсь, что талантливый народ сможет чего-нибудь придумать по этому поводу. :) У меня и у самого есть идейка, но не продуманная пока.


 
Fantasist   (2002-11-17 10:24) [2]

Тьфу! Совсем забыл записать освобождение памяти из под таблицы интерфейсов. Но это не тяжело, суть не в этом.



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

Форум: "KOL";
Текущий архив: 2003.07.21;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.47 MB
Время: 0.007 c
3-67684
SergeyI
2003-06-26 17:22
2003.07.21
Memo поле


1-67895
Pandemonium
2003-07-08 09:03
2003.07.21
Массив или запись объектов


14-68023
JB
2003-07-04 13:11
2003.07.21
Отчет в Word e


14-67981
Davinchi
2003-07-03 17:41
2003.07.21
Нуже компонент для архивации/разархивации файлов!!!


4-68108
Frankenstein
2003-05-14 12:58
2003.07.21
systray





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
Английский Французский Немецкий Итальянский Португальский Русский Испанский