connection point, проблеммы
От: atrofa Украина  
Дата: 18.09.04 11:35
Оценка:
Привет всем,

нужно сделать нотификацию метода используя connection point.
Создаю COM обьект на ATL, при воздании инерфейса выбираю connection point. А дальше как? Искал по поиску ничего толком не нашел.. есть описания только с использованием MFC, а с ATL как?

Если у меня есть метод Func(...), я передаю некоторые параметры и хочу узнать когда эти данные обработаються.

Подскажите, если можно с примером. Или дайте ссылки, где можно почитать..

Заранне благодарен
Re: connection point, проблеммы
От: Odi$$ey Россия http://malgarr.blogspot.com/
Дата: 18.09.04 15:04
Оценка: 2 (1)
Здравствуйте, atrofa, Вы писали:

A>нужно сделать нотификацию метода используя connection point.

A>Создаю COM обьект на ATL, при воздании инерфейса выбираю connection point. А дальше как?

— раз при создании поставил галочку на connection point у тебя в ClassView вместе с твоим интерфейсом IMyIntrf должен появится _IMyIntrfEvent
— вызываешь для него контекстное меню, потом Add-Add Method и добавляешь метод, например FirstMethod( BSTR str )
— компилируешь проект
— вызываешь контекстное меню в том же ClassView, только уже для класса (CMyIntrf)
— Add — Add Connection Point
— в форме увидишь слева свой _IMyIntrfEvent, перенесешь его кнопкой > в правое поле, Finish
— в CProxy_IMyIntrfEvent добавится метод Fire_FirstMethod( BSTR str )

A>Если у меня есть метод Func(...), я передаю некоторые параметры и хочу узнать когда эти данные обработаються.


вызывай в этом месте Fire_FirstMethod

Как в ATL клиенте подписываться на события надо объяснять?
Re[2]: connection point, проблеммы
От: atrofa Украина  
Дата: 20.09.04 07:29
Оценка:
Здравствуйте, Odi$$ey, Вы писали:

OE>- раз при создании поставил галочку на connection point у тебя в ClassView вместе с твоим интерфейсом IMyIntrf должен появится _IMyIntrfEvent

OE>- вызываешь для него контекстное меню, потом Add-Add Method и добавляешь метод, например FirstMethod( BSTR str )
OE>- компилируешь проект
OE>- вызываешь контекстное меню в том же ClassView, только уже для класса (CMyIntrf)
OE>- Add — Add Connection Point
OE>- в форме увидишь слева свой _IMyIntrfEvent, перенесешь его кнопкой > в правое поле, Finish
OE>- в CProxy_IMyIntrfEvent добавится метод Fire_FirstMethod( BSTR str )

A>>Если у меня есть метод Func(...), я передаю некоторые параметры и хочу узнать когда эти данные обработаються.


OE>вызывай в этом месте Fire_FirstMethod


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

Спасибо , с этим уже разобрался.. работает.. только мне нужно в клиент воза\вращать структуру RequestData:

typedef struct Patient {
BSTR MRN;
BSTR beforDate;
BSTR afterDate;
DWORD dwNumVisitID;
BSTR* visitIDs;
} Patient;

typedef struct RequestData {
BSTR siteID;
BSTR doctorID;
DWORD dwNumPatient;
Patient* patientData;
} RequestData;

Сейчас пишу так:

dispinterface _IDataEvents
{
properties:
methods:
[id(1), helpstring("method OnSynchronize")] HRESULT OnSynchronize([out] RequestData* respData);
};

Метод вызываеться а вот в структуре находиться какойто мусор.
Да и еще, мне нужно реализовать только С интерефейс, так что переводить структуру в SAFEARRAY не обязательно.

Почему не передаеться структура?

Метод Fire_XXXX не менял вообще, а клиент вот:


class CRequestEvSink : public CComObjectRoot,
public IDispEventImpl<0, CRequestEvSink, &__uuidof(_IDataEvents)>

{
public:
    CRequestEvSink() {}
    ~CRequestEvSink(){}

    BEGIN_COM_MAP(CRequestEvSink)
        COM_INTERFACE_ENTRY_IID(__uuidof(_IDataEvents),_IDispEvent)
    END_COM_MAP()

    DECLARE_PROTECT_FINAL_CONSTRUCT()
    DECLARE_NOT_AGGREGATABLE(CRequestEvSink)
    DECLARE_NO_REGISTRY()
    
    BEGIN_SINK_MAP(CRequestEvSink)
        SINK_ENTRY_INFO(0, __uuidof(_IDataEvents), 0x1,    voidFunc, infoFunc())
    END_SINK_MAP()

    static _ATL_FUNC_INFO* infoFunc()
    {
        static _ATL_FUNC_INFO _infoFunc = {CC_STDCALL, VT_EMPTY, 1, {VT_ARRAY|VT_BYREF} };
        return &_infoFunc;
    }
    
    void STDMETHODCALLTYPE voidFunc(RequestData* reqData)
    {
        reqData->siteID;
        MessageBox(NULL, "test", "", MB_OK);    
    }

};


и вызов:

    HRESULT hr;
    CComPtr<IData> ppData;
         DWORD dwCookie;

    hr = CoInitialize(NULL);

    hr = CoCreateInstance( __uuidof(Data), NULL, CLSCTX_INPROC_SERVER, __uuidof(IData), (void**)&ppData );
    
    CComObject<CRequestEvSink>* pSink;
    hr = pSink->CreateInstance(&pSink);
    hr = AtlAdvise(ppData, pSink->GetUnknown(), __uuidof(_IDataEvents), &dwCookie);

    RequestData data;
    hr = ppData->Synchronize(&data);

    hr = AtlUnadvise(ppData, __uuidof(_IDataEvents), dwCookie);

    CoUninitialize();
Re[3]: connection point, проблеммы
От: atrofa Украина  
Дата: 20.09.04 08:22
Оценка:
я тут подумал.. можно возвращать не структуру, а массив BSTR, только у меня всеравно не работает.. а использовать SAFEARRAY не желательно.
Можно вернуть массив BSTR??

типа так:
[id(1), helpstring("method SynchronizeClear")] HRESULT SynchronizeClear([out] BSTR* respData);
Re[2]: connection point, проблеммы
От: Esperar  
Дата: 20.09.04 15:12
Оценка: :)
Здравствуйте, Odi$$ey, Вы писали:

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


A>>нужно сделать нотификацию метода используя connection point.

A>>Создаю COM обьект на ATL, при воздании инерфейса выбираю connection point. А дальше как?

OE>- раз при создании поставил галочку на connection point у тебя в ClassView вместе с твоим интерфейсом IMyIntrf должен появится _IMyIntrfEvent

OE>- вызываешь для него контекстное меню, потом Add-Add Method и добавляешь метод, например FirstMethod( BSTR str )
OE>- компилируешь проект
OE>- вызываешь контекстное меню в том же ClassView, только уже для класса (CMyIntrf)
OE>- Add — Add Connection Point
OE>- в форме увидишь слева свой _IMyIntrfEvent, перенесешь его кнопкой > в правое поле, Finish
OE>- в CProxy_IMyIntrfEvent добавится метод Fire_FirstMethod( BSTR str )

A>>Если у меня есть метод Func(...), я передаю некоторые параметры и хочу узнать когда эти данные обработаються.


OE>вызывай в этом месте Fire_FirstMethod


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

ДА!
Re[3]: connection point, проблеммы
От: Bork СССР  
Дата: 20.09.04 22:03
Оценка:
Здравствуйте, atrofa, Вы писали:

A>Спасибо , с этим уже разобрался.. работает.. только мне нужно в клиент воза\вращать структуру RequestData:


A>Сейчас пишу так:


A> dispinterface _IDataEvents

A> {
A> properties:
A> methods:
A> [id(1), helpstring("method OnSynchronize")] HRESULT OnSynchronize([out] RequestData* respData);
A> };

A>Метод вызываеться а вот в структуре находиться какойто мусор.

A>Да и еще, мне нужно реализовать только С интерефейс, так что переводить структуру в SAFEARRAY не обязательно.

A>Почему не передаеться структура?


Структура передается, только не в ту сторону.
Для твоего СОМ объекта интерфейс _IDataEvents является внешним, следовательно параметры передаются от СОМ объекта к клиенту.
А для клиента параметр будет входящим, т.е. [in].

[id(1), helpstring("method OnSynchronize")] HRESULT OnSynchronize([in] RequestData* respData);
Как в ATL клиенте подписываться на события
От: Odi$$ey Россия http://malgarr.blogspot.com/
Дата: 21.09.04 04:29
Оценка: 1 (1) +1
#Имя: FAQ.atl.eventconnect
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;

        ...
    }

};
Re[4]: connection point, проблеммы
От: Esperar  
Дата: 21.09.04 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>
Re: Как в ATL клиенте подписываться на события
От: Axil Германия  
Дата: 28.02.06 16: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(...);
Не совсем правильный...
Re[5]: connection point, проблеммы
От: Odi$$ey Россия http://malgarr.blogspot.com/
Дата: 01.03.06 03:58
Оценка:
Здравствуйте, Esperar, Вы писали:

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


по-барабану, совершенно аналогично прикручивается к любому классу
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[2]: Как в ATL клиенте подписываться на события
От: Odi$$ey Россия http://malgarr.blogspot.com/
Дата: 01.03.06 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>>
Re[3]: Как в ATL клиенте подписываться на события
От: Axil Германия  
Дата: 01.03.06 14: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_....>
Тогда этот каст будет логически обоснован, хоть както .

Но может я и ошибаюсь и гдето чего то просмотрел...
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.