Направления и технологии - Оформление класса в виде COM объекта - PRCY⮭net
Допустим у вас есть некоторое приложение, написанное на 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->CraeteEmpty());
IMyCOM Ob2(d->CraeteId(15));
IMyCOM Ob3(d->CraeteFull(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
Information
  • Posted on 31.01.2010 22:00
  • Просмотры: 2246