Так отложил ATL в сторону. (потому как это все визарды)
Взял и проработал книгу "Inside COM" Роджерсона.
Вроде бы толково пишет. И хорошо что на чистом С++ , без визардов.Все ручками.
Но в конце своей чудесной книги он как-то упускается интересный момент для всех КОМОВ.
А конкретно события сервера и подключение клиента к их прослушке.
в общем способ для подключения клиента у меня стандартный , для простоты
я оставил тока основные моменты.
int main(int argc, char* argv[])
{
HRESULT hr;
ULONG dwAdvise;
CoInitialize(0);
//=====================================
IClassFactory *pClassFactory=NULL;
//----------------
hr=CoGetClassObject( CLSID_Car , CLSCTX_LOCAL_SERVER,
NULL, IID_IClassFactory, (void **) &pClassFactory);
ISpeed * pSpeed=NULL; //запрашиваемый интерфейс
hr=pClassFactory->CreateInstance(
NULL ,
IID_ISpeed,
(void**)&pSpeed );
//---------------- стандартное подключение к событиям сервера
LPCONNECTIONPOINTCONTAINER pConnPtCont;
LPCONNECTIONPOINT pConnPt = NULL;
//---------------- получить контейнер точек
pSpeed->QueryInterface(IID_IConnectionPointContainer , (void**)&pConnPtCont);
//---------------- найти нужную точку для подключния
hr=pConnPtCont->FindConnectionPoint(DIID__IRegistrationEvents, &pConnPt);
//================ объект MySink для эскпорта серверу
IRegistrationEvent1 * pSinkUnk=new MySink();
//================ регистрируем My
pConnPt->Advise((IUnknown*) pSinkUnk , &dwAdvise);
//================ проверяем событие которое произойдет
//================ на сервере если вызвать функцию SetSpeed
pSpeed->SetSpeed(10);
pClassFactory->Release();
CoUninitialize();
return 0;
}
//============================================================
//==== Класс MySink с интерфейсом IRegistrationEvent
interface IRegistrationEvent :IUnknown
{
virtual HRESULT OnSetSpeed()=0;
};
//==========
class MySink : public IRegistrationEvent
{
private:
DWORD m_dwRefCount;
public:
MySink::MySink() {m_dwRefCount = 0;}
MySink::~MySink() {}
// методы IUnknown
STDMETHOD(QueryInterface)(REFIID riid, LPVOID* ppv);
STDMETHOD_(ULONG, AddRef)();
STDMETHOD_(ULONG, Release)();
// IRegistrationEvent
virtual HRESULT OnSetSpeed()
{MessageBox(0 , "Client::OnSetSpeed!!" , "Client!!" , 0);return 0;}
}
//===========================================
в общем по идее сервер должен вызвать функцию реализованую в клиенте ,
ведь для COM разницы нет кто вызывает функцию клиент у сервера , или
сервер у клиента.
Опять же для простоты я остановился на клиентском вызове Advise.
"pConnPt->Advise((IUnknown*) pSinkUnk , &dwAdvise);"
И проверил обратный вызов клиентской функции OnSetSpeed
из серверной реализации этой функции вот ее код:
HRESULT __stdcall
CConnectionPoint::Advise(IUnknown* pIUnknownSink, DWORD* pdwCookie )
{
if (pIUnknownSink == NULL || pdwCookie == NULL)
{
*pdwCookie = 0;
return E_POINTER;
}
IUnknown * pIUnk = NULL ;
IRegistrationEvent * pI=NULL;
HRESULT hr = pIUnknownSink->QueryInterface(*m_piid , (void**)&pIUnk) ;
if (SUCCEEDED(hr))
{
MessageBox(0 , "SERVER ADVISE" , "SERVER" , 0);
CONNECTDATA cd ;
cd.dwCookie = ++m_dwNextCookie ;
cd.pUnk = pIUnk ;
pIUnk->QueryInterface(*m_piid , (void**)&pI) ;
// pI->AddRef(); //работает (проверил)
// pI->AddRef(); //тоже работает (проверил)
pI->OnSetSpeed();//опять работает
// pI->OnSetSpeed(); // УЖЕ НЕ РАБОТАЕТ(проверил)
// сохранить в листе
m_SinkList.push_back(cd) ;
// возвратить куки
*pdwCookie = cd.dwCookie ;
return S_OK ;
}
else
{
return hr;//CONNECT_E_CANNOTCONNECT ;
}
}
//===========================================================
в общем методы IUnknown объекта MySink вызываются хорошо.
а вот метод интерфейса IRegistrationEvent почему то только один раз
и только при обязательных условиях
1.если объявлена стрктура CONNECTDATA cd
2.CONNECTDATA должна быть именно в стеке
3.Элемент cd.pUnk должен быть инициализирован не любым значением и не нулем , а действительным полученым через ->QueryInterface числом pIUnk ;;//
хотя нигде в функции ADVISE структура CONNECTDATA не применяется.(МИСТИКА)
//===========================================================
Пытался сохранять параметры например IUnknown* pIUnknownSink в глобальный серверные переменные (pServerSink), что бы напряму без классов контейнера точек обойтись.
Но не работает ни с контейнероми , ни напрямую(через глобальные переменные)
Вот метод SetSpeed
//----------------------------------------------------------
// meth SetSpeed proc
//----------------------------------------------------------
STDMETHODIMP Car::SetSpeed(long nSpeed)
{
//упрощенно вызвать методы клиента
IRegistrationEvent * pMyEvent;
pServerSink->QueryInterface(DIID__IRegistrationEvents , (void**)&pMyEvent);
pMyEvent->OnSetSpeed(); // увы не работает.
//или так
Fire_Event(); //в этой функции стандартная обработка событий через классы
//перечислителей ( но там тоже не срабатывает)
m_nSpeed = nSpeed;
return S_OK;
}
//==============================================================
В итоге если в функции ADVISE , для объекта MySink - методы интерфейса IUnknown работают , а методы моего интерфейса IRegistrationEvent , срабатываеют только один раз.
То в вызове CAR::SetSpeed вообще ничего не работает.
//==============================
МАРШАЛИНГ
file.idl для прокси\стаб библиотеки выглядит так:
import "oaidl.idl";
import "ocidl.idl";
import "unknwn.idl" ;
//////////////////////////////////////////////////
[
object,uuid(D518B0BF-3EE1-4976-9B6A-9F3443A2A186),
helpstring("interface IStatus")
]
interface ISpeed : IUnknown
{
HRESULT GetSpeed([in,out] long * pnSpeed);
HRESULT SetSpeed([in] long nSpeed);
}
/////////////////////////////////////////////////
[
uuid(0331CCE1-669D-4082-8116-01E989993C17),
helpstring("IRegistrationEvent Interface"),
pointer_default(unique)
]
interface IRegistrationEvent : IUnknown
{
HRESULT OnSetSpeed();
};
//====== TypeLib
[
uuid(3F481E63-C189-4d99-A705-9F3F2DFB7145),
version(1.0)
]
library ExeServer {
importlib("stdole32.tlb");
importlib("stdole2.tlb");
[uuid(1F481E63-C189-4d99-A705-9F3F2DFB7145)]
//====== Экспорnирумый класс Sink
coclass mySink
{
[default] interface IRegistrationEvent;
};
//====== ГЛАВНЫЙ КЛАСС СЕРВЕРА
[uuid(2F481E63-C189-4d99-A705-9F3F2DFB7145)]
coclass Car
{
[default] interface ISpeed;
interface IConnectionPointContainer ;
[default,source] interface IRegistrationEvent;
};
};
Может кто сталкивался с таким вопросом?
А то я тут как бы стопорнулся.
Надо копать дальше все равно.