События
От: Алекс Россия http://wise-orm.com
Дата: 21.10.02 07:14
Оценка: 69 (7)
Всем привет!

Столкнулся с проблемой реализации события 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"));
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.