Всем привет!
Столкнулся с проблемой реализации события ActiveX'ого элемента в WTL'ом проекте.
Точно такая же проблема была обозначена
здесьАвтор: Брыскин Игорь Вадимович
Дата: 11.08.02
.
При чем у меня WTL 3.0, так что это не баг семерки. Теперь объясню почему она возникает и как я ее решил.
По большому счету возникает она из-за глюкавости мастера, добавляющего IDispEventImpl и sink-карту к вашему классу.
Делает он так:
...
public IDispEventImpl<IDC_MSHFLEXGRID1, CEditLinksDlg>
...
//карта
BEGIN_SINK_MAP(CEditLinksDlg)
//Make sure the Event Handlers have __stdcall calling convention
SINK_ENTRY(IDC_MSHFLEXGRID1, DISPID_DBLCLICK, OnDblClickMshflexgrid1)
END_SINK_MAP()
Что в корне не правильно и приводит к известному ассерту. Он возникает в процедуре
inline HRESULT CComTypeInfoHolder::GetTI(LCID lcid)
{
//If this assert occurs then most likely didn't initialize properly
ATLASSERT(m_plibid != NULL && m_pguid != NULL);
ATLASSERT(!InlineIsEqualGUID(*m_plibid, GUID_NULL) && "Did you forget to pass the LIBID to CComM
Давайте разберемся при чем здесь класс CComTypeInfoHolder.
Определение IDispEventImpl такое:
template <UINT nID, class T, const IID* pdiid = &IID_NULL, const GUID* plibid = &GUID_NULL,
WORD wMajor = 0, WORD wMinor = 0, class tihclass = CComTypeInfoHolder>
class ATL_NO_VTABLE IDispEventImpl : public IDispEventSimpleImpl<nID, T, pdiid>
{
public:
typedef tihclass _tihclass;
...
Как мы видим в качестве параметра шаблона фигурирует этот класс, который переименовывается в _tihclass.
Где он определен? В самом конце объявляется статический мембер этого типа:
...
protected:
static _tihclass _tih;
static HRESULT GetTI(LCID lcid, ITypeInfo** ppInfo)
{return _tih.GetTI(lcid, ppInfo);}
};
template <UINT nID, class T, const IID* piid, const GUID* plibid, WORD wMajor, WORD wMinor, class tihclass>
IDispEventImpl<nID, T, piid, plibid, wMajor, wMinor, tihclass>::_tihclass
IDispEventImpl<nID, T, piid, plibid, wMajor, wMinor, tihclass>::_tih =
{piid, plibid, wMajor, wMinor, NULL, 0, NULL, 0};
Как видим он тут же определяется и инициализируется. Важно отметить тот факт,
что инициализируется он параметрами шаблона IDispEventImpl.
Хорошо, теперь мы знаем как инициализировать IDispEventImpl для того,
чтобы не возникало злополучного ассерта. (Переменные m_plibid и m_pguid уже не будут равны GUID_NULL и
IID_NULL соответственно.
Замечательно! Исправляем визард так:
...
public IDispEventImpl<IDC_MSHFLEXGRID1, CEditLinksDlg,
&DIID_DMSHFlexGridEvents,&LIBID_MSHierarchicalFlexGridLib,6>
Версию указываем обязательно!
Компилируем... Не компилируется
При чем на строчке sink-карты. Смотрим макрос SINK_ENTRY
#define SINK_ENTRY_INFO(id, iid, dispid, fn, info) {id, &iid, (int)(static_cast<_IDispEventLocator<id, &iid>*>((_atl_event_classtype*)8))-8, dispid, (void (__stdcall _atl_event_classtype::*)())fn, info},
#define SINK_ENTRY_EX(id, iid, dispid, fn) SINK_ENTRY_INFO(id, iid, dispid, fn, NULL)
#define SINK_ENTRY(id, dispid, fn) SINK_ENTRY_EX(id, IID_NULL, dispid, fn)
А вот в чем проблема! SINK_ENTRY подставляет IID_NULL вместо нужного событийного дисп-интерфейса. Ок. Переписываем:
SINK_ENTRY_EX(IDC_MSHFLEXGRID1, DIID_DMSHFlexGridEvents,DISPID_DBLCLICK, OnDblClickMshflexgrid1)
Компилируем, запускаем, все работает! Ура!
З.Ы. Для адвайза даже не нужно указывать событийный интервейс. Достаточно простого:
hr = DispEventAdvise(grid);
if (FAILED(hr))
MessageBox(_T("Error advising"),_T("Error"));