Статья
Версия для печати
Обсудить на форуме (3)
Оформление класса в виде COM объекта



Допустим у вас есть некоторое приложение, написанное на C++(VC++ если быть корректным). Как оно у вас появилось не суть важно, может быть это ваша старая разработка, может быть,  вы решили сначала  отладить предметную часть. Важно то что вы горите желанием  вынести часть классов в объектные модули и оформить  их в виде ActiveX, COM и ATL объектов.  Есть несколько типовых проблем связанных с таким переносом.

Множественные конструкторы.
Код:
class MyCOM
{
   MyCOM();
   MyCOM(long id);
   MyCOM(long id, LPCSTR Name);
}

Знакомо и очень удобно, но в COM правила создания объекта строго определены и  ни одна из функции  для созданий объектов не позволяет передавать параметры конструктору класса.

Настройку объекта придется  вынести в отдельный метод, например Init.
Код:
  // IMyCOM стандартная обертка наследник от COleDispatchDriver
   IMyCOM * d=new IMyCOM;
   COleException pErr;
   CString SSS="Mylib.MyCOM";
   d->CreateDispatch( SSS,&pErr);
   d->Init(15,Матрица); // Инициализируем


В принципе вы можете создать свою фабрику объектов. Это позволит создавать объекты  вот так.
Код:
  IMyOF * d=new IMyOF;
   COleException pErr;
   CString SSS="MyLib.MyOF";
   d->CreateDispatch( SSS,&pErr);
   IMyCOM  Ob1(d->CreateEmpty());
   IMyCOM  Ob2(d->CreateId(15));
   IMyCOM  Ob3(d->CreateFull(15,SSS ));

Но зачем вам лишний промежуточный объект если можно обойтись без него.  

Перегруженные методы.
Код:
class MyCOM
{
   LPCSTR GetMyRec(long id);
   LPCSTR GetMyRec(LPCSTR Name);
   AddRec ();
   AddRec (long id);
   AddRec (long id, LPCSTR Name);
}

Это вполне законный код С++, но COM не разрешит вам в  интерфейсе объявить  два метода с одним именем. Это противоречит концепции.
Решение: Можно связать функции с разными методами интерфейса для этого в odl пишим
Код:
  [id(1)] BSTR AddRecName(BSTR ID);
   [id(2)] BSTR AddRecID(long ID);
а в cpp осуществляем привязку.
Код:
  BEGIN_DISPATCH_MAP(.)
   DISP_FUNCTION(CPSDG, "AddRecName", AddRec, VTS_BSTR, VTS_BSTR)
   DISP_FUNCTION(CPSDG, "AddRecId", AddRec, VTS_BSTR, VTS_I2)

   DISP_FUNCTION_ID(.)
   END_DISPATCH_MAP()

Можно написать  прокси функции. Например, для  GetMyRec прототип может выглядеть так
Код:
LPCSTR GetMyRec (VARIANT id)
{
   switch id.vt
   {
    case VT_I4:
       return GetMyRec(id.lVal);
    case VT_BSTR:
       return GetMyRec(id.bstrVal);
   }
   return S_OK;
}

Для функции AddRec  можно сделать вот так
Код:
HRESULT  AddRec (VARIANT id, VARIANT Name)
{
   if ((id.vt==VT_EMPTY) && (Name.vt==VT_EMPTY))
   {
      AddRec() ;
      return S_OK;
   }
   if ((id.vt==VT_I4) && (Name.vt==VT_EMPTY))
   {
       AddRec(id.lVal) ;
       return S_OK;
   }
   if ((id.vt==VT_I4) && (Name.vt== VT_BSTR))
   {
       AddRec(id.lVal, Name. bstrVal ) ;
       return S_OK;
   }
}

 Этого вполне достаточно, но можно еще изменить объявление метода интерфейса в odl вот так
Код:
  HRESULT Add(VARIANT [optional, in]id, [optional,in]VARIANT S);

это позволит вызывать метод, более красиво.
Пример на  VB
Код:
  MyObject.Add  // Любой из вариантов должен работать
   MyObject.Add 15
   MyObject.Add 15, Var

Пользовательские типы данных
В сложном проекте полно собственных констант, структур, множеств используемых в качестве параметров.
Код:
#define IDL_NEXT 5
#define IDL_STOP 6

struct UDT
 {
      unsigned long X;
      unsigned long Y;

      BSTR pbstr;
  } UDT;

typedef enum EnumType
{
First=1,
Seond=4,
Last =10
};

class MyCOM
{
   / .
   void SetType (EnumType  T);
   void  Do(UDT  * Dat);
   void SetMove (int val);
   / .
}

// а где-то все это вызывается
   SetType(First);
   UDT  Dat,Dat1;

   Do (&Dat,Dat1);
   SetMove (IDL_NEXT);

Понятно что, для того чтобы подобным образом можно было вызывать методы COM объекта, служебные структуры, множества и константы должны быть доступны из вне. Для этого нужно  включить их описание в ODL файл. Множества описываются так.
Код:
[
    uuid(...),
    version(1.0),
    helpstring("...")
]
library LibraryName
{
    importlib("stdole32.tlb");
    importlib("stdole2.tlb");

    typedef enum
    {
        valueName1    = 0,
        valueName2    = 1,
        ...
        valueNameN    = N
    } EnumType;
..
}

Передавать в качестве параметров  структуры тоже можно. Такие структуры называются UDT - User Defined Type. В IDL  описываются так:
Код:
typedef [uuid(C1D3A8C0-A4AA-11D0-819C-00A0C90FFFC3)] struct UDT
{
   unsigned long X;
   unsigned long Y;
   BSTR pbstr;
}  UDT;

Описывать параметры метода можно как VARIANT но тогда придется работать с интерфейсом IRecordInfo или как UDT:
Код:
  Do([in]UDT* pIn, [in,out] pOut);

Передать UDT в такой метод проще простого:
Код:

UDT some_data, some_returned_data;
   p->Do(&some_data, some_returned_data);

Членами UDT могут быть другие UDT или oleautomation-совместимые типы. Увы в VC нет автоматизации позволяющей создавать пользовательские типы поэтом у все придется делать ручками.

Пока все дальше будет больше.


Автор: PSD
Версия для печати
Обсудить на форуме (3)