IClassFactory

Previous  Top  Next

    
 

 

Итак, IClassFactory предназначен для того, чтобы создавать экземпляры соответствующего класса. То есть строчкой:

Code:

CoGetClassObject(Calc_CLSID, dwClsContext, nil, IClassFactory,p); //Calc_CLSID - GUID нашего калькулятора

 

 

 

мы должны получить интерфейс, с помощью которого мы сможем создавать сколь угодно много наших калькуляторов (конкретнее: экземпляров нашего класса MyCalc). Для этого вызывается метод этого интерфейса CreateInstance. Параметры у него до боли знакомые - они точно такие же как три последних параметра у СoCreateInstance или CoGetClassObject. CLSID уже не нужен, так как данный интерфейс принадлежит классу, который создает только объекты определенного класса - того CLSID которого мы указали в СoCreateInstance, который потом передался в CoGetClassObject и который наконец попал в DllGetClassObject.

 

Видете, тут довольно забавно получается - мы просим создать объект и выдать для этого объекта интерфейс IClassFactory, с помощью которого мы будем создавать эти же объекты. В принципе, мы совершаем лишнее действие, если собираемся создать только один объект, однако если мы хотим создать множество объектов, то такой путь более эффективен, чем многократный вызов CoCreateInstance или CoGetClassObject, поэтому он и был утвержден.

 

Чисто теоретически, мы можем сделать так (для нашего калькулятора):

Code:

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 так:

Code:

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. Так оно выглядит там:

Code:

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. Этот метод предназначен для того, чтобы гарантировать не уничтожение объекта. То есть поставили замок, и пока его не сняли, обект должен жить. Добавим и этот метод а наш класс.

Code:

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;

 

 

Реализация:

Code:

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!