Главная | Новости | Статьи | Книги | Ссылки |
Допустим у вас есть некоторое приложение, написанное на C++(VC++ если быть корректным). Как оно у вас появилось не суть важно, может быть это ваша старая разработка, может быть вы решили сначала отладить предметную часть. Важно то что вы горите желанием вынести часть классов в объектные модули и оформить их в виде ActiveX, COM и ATL объектов. Есть несколько типовых проблем связанных с таким переносом.
Оформление класса в виде COM объекта.
Допустим у вас есть некоторое приложение, написанное на C++(VC++ если быть корректным). Как оно у вас появилось не суть важно, может быть это ваша старая разработка, может быть вы решили сначала отладить предметную часть. Важно то что вы горите желанием вынести часть классов в объектные модули и оформить их в виде ActiveX, COM и ATL объектов. Есть несколько типовых проблем связанных с таким переносом.
Множественные конструкторы.
class MyCOM { MyCOM(); MyCOM(long id); MyCOM(long id,LPCSTR Name); : }
Знакомо и очень удобно, но в COM правила создания объекта строго определены и ни одна из функции для созданий объектов не позволяет передавать параметры конструктору класса.
Настройку объекта придется вынести в отдельный метод например Init.
// IMyCOM cтандартная обертка наследник от 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 нет автоматизации позволяющей создавать пользовательские типы поэтом у все придется делать ручками.
Пока все дальше будет больше.
Источник: http://shelek.com