Как в ATL клиенте подписываться на COM-события

Odi$$ey Odi$$ey
OE>>Как в ATL клиенте подписываться на события надо объяснять?
E>ДА!

Ok, например в клиенте есть диалог, в котором нужно ловить события от COM-сервера:

class CMainDlg : public CAxDialogImpl<CMainDlg>
               , public CDialogResize<CMainDlg>
{


добавляем туда следующие вещи (см. выделенное):

#import "MyCOM.dll" no_namespace, named_guids

// произвольное число, удобно когда подключение идет к событиям сразу нескольких компонент
#define IMYINTRFID 1

class CMainDlg : public CAxDialogImpl<CMainDlg>
               , public CDialogResize<CMainDlg>
               , public IDispEventImpl<IMYINTRFID, CMainDlg, &DIID___IMyIntrfEvent, &LIBID_MYCOMLib, 1, 0>
{
  
    IMyIntrfPtr pMyIntrfPtr;

  // номер метода-события (0x1) смотрим в idl сервера или в 
    // mycom.tli, который создаст #import
    BEGIN_SINK_MAP( CMainDlg )
        SINK_ENTRY_EX( IMYINTRFID, DIID___IMyIntrfEvent, 0x1,  OnFirstMethod )
    END_SINK_MAP()

  // этот метод будет вызван при возникновении события
    HRESULT __stdcall OnFirstMethod( BSTR str )
    {
       // получили строку str
       return S_OK;
    }


  // функция для подписки на события, вызывается где удобно, например
    // в OnInitDialog() после загрузки pMyIntrfPtr
  BOOL Advise2MyCOMEvent()
  {
    _ASSERTE( pMyIntrfPtr != NULL );

    if ( pMyIntrfPtr )
    {
        HRESULT hr = _IDispEventLocator<IMYINTRFID, &DIID___IMyIntrfEvent>::DispEventAdvise( pMyIntrfPtr, &DIID___IMyIntrfEvent );

        if ( FAILED( hr ) )
        {
            // ошибка, код в hr
            return FALSE;
        }
        else
            return TRUE;
    }
    else
    {
        // ошибка - компонент не загружен, подписка невозможна
        return FALSE;
    }
  }

  
    // функция для отписки от событий, вызывается где удобно,
    // например в OnDestroy()
  BOOL UnAdvise2MyCOMEvent()
  {
    if ( pMyIntrfPtr )
    {
        HRESULT hr = _IDispEventLocator<IMYINTRFID, &DIID___IMyIntrfEvent>::DispEventUnadvise( pMyIntrfPtr, &DIID___IMyIntrfEvent );
                
        if ( FAILED( hr ) )
        {
            // ошибка, код в hr
            return FALSE;
        }
        else
            return TRUE;
    }
        else
            return FALSE;
  }


    LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
    {
         ...
             
       HRESULT hr = pMyIntrfPtr.CreateInstance( "MyCom.MyIntrf" );

       if ( SUCCEEDED( hr ) )
       {
              Advise2MyCOMEvent();
       }
       else
       {
              // ошибка
       }
       
             ...
    } 

    LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
    {
      ...
        
          UnAdvise2MyCOMEvent();

          pMyIntrfPtr = NULL;

        ...
    }

};
Esperar
Esperar connection point, проблеммы
21.09.2004 05:57
Здравствуйте, Odi$$ey, Вы писали:

OE>Здравствуйте, Esperar, Вы писали:


OE>>>Как в ATL клиенте подписываться на события надо объяснять?

E>>ДА!

OE>Ok, например в клиенте есть диалог, в котором нужно ловить события от COM-сервера:


если нет в клиенте диалога, если клиент — это плагин, то где там ловить события?

OE>
OE>class CMainDlg : public CAxDialogImpl<CMainDlg>
OE>               , public CDialogResize<CMainDlg>
OE>{
OE>


OE>добавляем туда следующие вещи (см. выделенное):


OE>
OE>#import "MyCOM.dll" no_namespace, named_guids

OE>// произвольное число, удобно когда подключение идет к событиям сразу нескольких компонент
OE>#define IMYINTRFID 1

OE>class CMainDlg : public CAxDialogImpl<CMainDlg>
OE>               , public CDialogResize<CMainDlg>
OE>               , public IDispEventImpl<IMYINTRFID, CMainDlg, &DIID___IMyIntrfEvent, &LIBID_MYCOMLib, 1, 0>
OE>{
  
OE>    IMyIntrfPtr pMyIntrfPtr;

OE>  // номер метода-события (0x1) смотрим в idl сервера или в 
OE>    // mycom.tli, который создаст #import
OE>    BEGIN_SINK_MAP( CMainDlg )
OE>        SINK_ENTRY_EX( IMYINTRFID, DIID___IMyIntrfEvent, 0x1,  OnFirstMethod )
OE>    END_SINK_MAP()

OE>  // этот метод будет вызван при возникновении события
OE>    HRESULT __stdcall OnFirstMethod( BSTR str )
OE>    {
OE>       // получили строку str
OE>       return S_OK;
OE>    }
OE>

OE>  // функция для подписки на события, вызывается где удобно, например
OE>    // в OnInitDialog() после загрузки pMyIntrfPtr
OE>  BOOL Advise2MyCOMEvent()
OE>  {
OE>    _ASSERTE( pMyIntrfPtr != NULL );

OE>    if ( pMyIntrfPtr )
OE>    {
OE>        HRESULT hr = _IDispEventLocator<IMYINTRFID, &DIID___IMyIntrfEvent>::DispEventAdvise( pMyIntrfPtr, &DIID___IMyIntrfEvent );

OE>        if ( FAILED( hr ) )
OE>        {
OE>            // ошибка, код в hr
OE>            return FALSE;
OE>        }
OE>        else
OE>            return TRUE;
OE>    }
OE>    else
OE>    {
OE>        // ошибка - компонент не загружен, подписка невозможна
OE>        return FALSE;
OE>    }
OE>  }
OE>
  
OE>    // функция для отписки от событий, вызывается где удобно,
OE>    // например в OnDestroy()
OE>  BOOL UnAdvise2MyCOMEvent()
OE>  {
OE>    if ( pMyIntrfPtr )
OE>    {
OE>        HRESULT hr = _IDispEventLocator<IMYINTRFID, &DIID___IMyIntrfEvent>::DispEventUnadvise( pMyIntrfPtr, &DIID___IMyIntrfEvent );
                
OE>        if ( FAILED( hr ) )
OE>        {
OE>            // ошибка, код в hr
OE>            return FALSE;
OE>        }
OE>        else
OE>            return TRUE;
OE>    }
OE>        else
OE>            return FALSE;
OE>  }
OE>

OE>    LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
OE>    {
OE>         ...
             
OE>       HRESULT hr = pMyIntrfPtr.CreateInstance( "MyCom.MyIntrf" );

OE>       if ( SUCCEEDED( hr ) )
OE>       {
OE>              Advise2MyCOMEvent();
OE>       }
OE>       else
OE>       {
OE>              // ошибка
OE>       }
       
OE>             ...
OE>    } 

OE>    LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
OE>    {
OE>      ...
        
OE>          UnAdvise2MyCOMEvent();

OE>          pMyIntrfPtr = NULL;

OE>        ...
OE>    }

OE>};
OE>
Odi$$ey
Odi$$ey
01.03.2006 03:58
Здравствуйте, Esperar, Вы писали:

E>если нет в клиенте диалога, если клиент — это плагин, то где там ловить события?


по-барабану, совершенно аналогично прикручивается к любому классу
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Axil
Axil Как в ATL клиенте подписываться на события
28.02.2006 04:06
Здравствуйте, Odi$$ey, Вы писали:

OE>Ok, например в клиенте есть диалог, в котором нужно ловить события от COM-сервера:


OE>
OE>class CMainDlg : public CAxDialogImpl<CMainDlg>
OE>               , public CDialogResize<CMainDlg>
OE>{
OE>


Кажется (не уверен что прав) тут есть одна ошибка, класс CMainDlg не наследник IDispatch.
И вызов Invoke
IDispatch* pConnection = static_cast<IDispatch *>(punkConnection.p);
pConnection->Invoke(...);
Не совсем правильный...
Odi$$ey
Odi$$ey
01.03.2006 03:58
Здравствуйте, Axil, Вы писали:

A>Кажется (не уверен что прав) тут есть одна ошибка, класс CMainDlg не наследник IDispatch.


и?

A>И вызов Invoke

A> IDispatch* pConnection = static_cast<IDispatch *>(punkConnection.p);
A> pConnection->Invoke(...);
A>Не совсем правильный...

а это где такой? я такого не писал
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Axil
Axil
01.03.2006 02:41
Здравствуйте, Odi$$ey, Вы писали:

A>>Кажется (не уверен что прав) тут есть одна ошибка, класс CMainDlg не наследник IDispatch.


OE>и?


A>>И вызов Invoke

A>> IDispatch* pConnection = static_cast<IDispatch *>(punkConnection.p);
A>> pConnection->Invoke(...);
A>>Не совсем правильный...

OE>а это где такой? я такого не писал


А его никто не писал, его визард сам напишет
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccore/html/_atl_Adding_Connection_Points_to_an_Object.asp

И класс который он сгенерирует будет выглядеть примерно так —

template<class T>
class CProxy_ISomeEvents : public IConnectionPointImpl<T, &__uuidof(_ISomeEvents)>
{
public:
    HRESULT Fire_OnAction(void) {
        HRESULT hr = S_OK;
        T* pThis = static_cast<T*>(this);
        int cConnections = m_vec.GetSize();

        for (int iConnection = 0; iConnection < cConnections; iConnection++) {
            pThis->Lock();
            CComPtr<IUnknown> punkConnection = m_vec.GetAt(iConnection);
            pThis->Unlock();

            IDispatch* pConnection = static_cast<IDispatch*>(punkConnection.p);

            if (pConnection) {
                hr = pConnection->Invoke(...);
            }
        }
        return hr;
    }
};


Вот тут и есть тот самый каст к IDispatch.
IDispatch* pConnection = static_cast<IDispatch*>(punkConnection.p); // punkConnection.p содержит IUnknown от объекта класса CMainDlg

Имхо имеет смысл добавить к классу CMainDlg в базы такую туповатую строчку —
, public IDispatchImpl<IDispatch, &__uuidof(IDispatch), &LIBID_....>
Тогда этот каст будет логически обоснован, хоть както .

Но может я и ошибаюсь и гдето чего то просмотрел...