Оформление класса в виде 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);
это позволит вызывать метод , более красиво. Пример на VBMyObject.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 нет автоматизации позволяющей создавать пользовательские типы поэтом у все придется делать ручками. |