IClassFactory
IClassFactory
Итак, IClassFactory предназначен для того, чтобы создавать экземпляры соответствующего класса. То есть строчкой:
CoGetClassObject(Calc_CLSID, dwClsContext, nil, IClassFactory,p); //Calc_CLSID - GUID нашего калькулятора
мы должны получить интерфейс, с помощью которого мы сможем создавать сколь угодно много наших калькуляторов (конкретнее: экземпляров нашего класса MyCalc). Для этого вызывается метод этого интерфейса CreateInstance. Параметры у него до боли знакомые - они точно такие же как три последних параметра у СoCreateInstance или CoGetClassObject. CLSID уже не нужен, так как данный интерфейс принадлежит классу, который создает только объекты определенного класса - того CLSID которого мы указали в СoCreateInstance, который потом передался в CoGetClassObject и который наконец попал в DllGetClassObject.
Видете, тут довольно забавно получается - мы просим создать объект и выдать для этого объекта интерфейс IClassFactory, с помощью которого мы будем создавать эти же объекты. В принципе, мы совершаем лишнее действие, если собираемся создать только один объект, однако если мы хотим создать множество объектов, то такой путь более эффективен, чем многократный вызов CoCreateInstance или CoGetClassObject, поэтому он и был утвержден.
Чисто теоретически, мы можем сделать так (для нашего калькулятора):
var
p:IClassFactory;
Calc:ICalc;
begin
//создаем объект (MyCalc) и получаем для него интерфейс IClassFactory
CoGetClassObject(StringTOGUID('{2563AE40-AC27-11D6-A5C2-444553540000}'),nil,CLSCTX_INPROC_SERVER,IClassFactory,p);
//получаем интерфейс ICalc
p.QueryInterface(ICalcGUID,Calc);
end;
Ибо IClassFactory, как и любой интерфейс, является потомком IUnknown, и поддерживает метод QueryInterface (как AddRef и Release, который Delphi вызывает автоматически). Единственная загвоздка состоит в том, что несмотря на то, что этот интерфейс вроде должен пренадежать только что созданному объекту MyCalc, во многих реализациях он ему не пренадлежит. Ну у нас то, конечно, пока еще вообще никакой реализации нет, но если бы это делал кто-то другой, то возможно он бы реализовал DllGetClassObject так:
function DllGetClassObject(const CLSID, IID: TGUID; var Obj): HResult; stdcall;
var
Calc:TObject;
begin
if GUIDToString(CLSID)<>'{2563AE40-AC27-11D6-A5C2-444553540000}' {GUID нашего класса} then
begin
Result:=CLASS_E_CLASSNOTAVAILABLE;
exit;
end;
// если cпрашивается IClassFactory, то создаем класс-фабрику.
if IID=IClassFactory then
Calc:=CalcFactory.Create
else
Calc:=MyCalc.Create;
if not Calc.GetInterface(IID,Obj) then
begin
Result:=E_NOINTERFACE;
Calc.Free;
exit;
end;
Result:=S_OK;
end;
То есть создается один экземпляр маленького класса CalcFactory, который ничего больше не умеет, кроме как создавать калькуляторы (экземпляры класса MyCalc). Естесственно, он поддерживает интерфейс IClassFactory. Такая реализация не редка и попытка получить у такого класса-фабрики интерфейс настоящего класса может закончится ошибкой.
Мы же давайте пойдем другим путем, и просто дополним наш класс интерфейсом IClassFactory. Для этого мы можем сами создать интерфейс IClassFactory, как мы раньше создавали ICalc и ICalc2, а можем воспользоваться готовым описанием, включив в uses библиотеку ActiveX. Так оно выглядит там:
IClassFactory = interface(IUnknown)
['{00000001-0000-0000-C000-000000000046}']
function CreateInstance(const unkOuter: IUnknown; const iid: TIID;
out obj): HResult; stdcall;
function LockServer(fLock: BOOL): HResult; stdcall;
end;
Как видите, помимо CreateInstance здесь так же есть метод LockServer. Этот метод предназначен для того, чтобы гарантировать не уничтожение объекта. То есть поставили замок, и пока его не сняли, обект должен жить. Добавим и этот метод а наш класс.
MyCalc=class(TObject,ICalc,ICalc2, IClassFactory)
fx,fy:integer;
FRefCount:integer;
public
constructor Create;
procedure SetOperands(x,y:integer);
function Sum:integer;
function Diff:integer;
function Divide:integer;
function Mult:integer;
procedure Release;
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
function _AddRef:Longint; stdcall;
function _Release:Longint; stdcall;
//IClassFactory
function CreateInstance(const unkOuter: IUnknown; const iid: TIID;out obj): HResult; stdcall;
function LockServer(fLock: BOOL): HResult; stdcall;
end;
Реализация:
function MyCalc.CreateInstance(const unkOuter: IUnknown; const iid: TIID;out obj): HResult; stdcall;
var
Calc:MyCalc;
begin
Calc:=MyCalc.Create;
if not Calc.GetInterface(IID,Obj) then
begin
Result:=E_NOINTERFACE;
Calc.Free;
exit;
end;
Result:=S_OK;
end;
function MyCalc.LockServer(fLock: BOOL): HResult; stdcall;
begin
if fLock then
_AddRef
else
Release;
end;
Реализация CreateInstance полностью идентична последним восми строчкам функции DllGetClassObject - просто создаем объект и возвращаем интерфейс, если мы его поддерживаем. С LockServer тоже все просто: если fLock=true тогда увеличиваем счетчик вызовом _AddRef, иначе уменьшаем его вызывая Release.
Ну теперь еще раз. Компилируем dll, тестер менять не надо, и запускаем... Свершилось! Наш калькулятор был создан системной функцией CoCreateInstance!