Выкладываю решение:
Класс для подключения к event'ам:
C++ (Qt)
#ifndef EVENTSINK_H
#define EVENTSINK_H
#include <OCIdl.h>
class EventSinkBase
{
public:
EventSinkBase(const IID* iidEvent, IUnknown* that)
: _iidEvent(iidEvent)
, _that(that)
, _cpoint(NULL)
, _cookie(NULL)
{ }
virtual ~EventSinkBase()
{
unadvise();
}
const IID* iidEvent() const { return _iidEvent; }
bool advise(IConnectionPoint* cpoint)
{
_cpoint = cpoint;
_cpoint->AddRef();
_cpoint->Advise(_that, &_cookie);
return _cookie;
}
void unadvise()
{
if (_cpoint) {
_cpoint->Unadvise(_cookie);
_cpoint->Release();
_cpoint = NULL;
_cookie = NULL;
}
}
void addRef() { _that->AddRef(); }
void release() { _that->Release(); }
private:
const IID* _iidEvent;
IUnknown* _that;
IConnectionPoint* _cpoint;
ULONG _cookie;
};
template<class TEvent>
class EventSink : public TEvent, public EventSinkBase
{
public:
EventSink(const IID* iidEvent) : EventSinkBase(iidEvent, this) { }
virtual STDMETHODIMP QueryInterface(const IID& iid, void** ppv)
{
if (iid == IID_IUnknown) {
*ppv = static_cast<IUnknown*>(this);
} else
if (iid == *iidEvent()) {
*ppv = static_cast<TEvent*>(this);
} else {
*ppv = NULL;
return E_NOINTERFACE;
}
reinterpret_cast<IUnknown*>(*ppv)->AddRef();
return S_OK;
}
virtual STDMETHODIMP_(ULONG) AddRef()
{
return InterlockedIncrement(&_ref);
}
virtual STDMETHODIMP_(ULONG) Release()
{
ULONG refCount = InterlockedDecrement(&_ref);
if (!refCount) {
delete this;
}
return refCount;
}
private:
ULONG _ref;
};
#endif // EVENTSINK_H
от него наследуем наш обработчик:
C++ (Qt)
class AgentEvents
: public EventSink<IAgentEvents>
{
public:
AgentEvents();
~AgentEvents();
HRESULT STDMETHODCALLTYPE InitOnAdvise(
BSTR status,
IQueueCollection* queues,
IAgentPhoneSession* session)
{
QString s = QString::fromWCharArray(status, ::SysStringLen(status));
emit notifier()->initOnAdvise(s,
new QAxObject(queues), new QAxObject(session));
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnPhoneSessionOpened(
IAgentPhoneSession* session)
{
emit notifier()->openedPhoneSession(new QAxObject(session));
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnStatusChanged(
BSTR status)
{
QString s = QString::fromWCharArray(status, ::SysStringLen(status));
emit notifier()->changedStatus(s);
return S_OK;
}
inline AgentEventsNotifier* notifier() { return ¬ifier; }
private:
AgentEventsNotifier notifier;
};
Вспомогательный класс который будет испускать сигналы (все в одном реализовать не получилось):
C++ (Qt)
class QAxObject;
class AgentEventsNotifier : public QObject
{
Q_OBJECT
public:
AgentEventsNotifier() : QObject() { }
~AgentEventsNotifier() { }
signals:
void initOnAdvise(const QString& status,
QAxObject* queues, QAxObject* session);
void openedPhoneSession(QAxObject* session);
void changedStatus(const QString& status);
};
ну и класс "подключатель", который связывает сам COM объект и event'ы:
C++ (Qt)
#ifndef AXEVENTCONNECTOR_H
#define AXEVENTCONNECTOR_H
#include <QUuid>
#include <QVector>
#include "eventsink.h"
class QObject;
class QAxObject;
class AxEventConnector
{
public:
AxEventConnector(IUnknown* iface);
AxEventConnector(QAxObject* container);
~AxEventConnector();
bool isValid() const;
bool connectEvent(
EventSinkBase* eventSink, bool deleteEventSinkAtError = true);
private:
IUnknown* _iface;
IConnectionPointContainer* _cpoints;
QVector<EventSinkBase*> _eventSinks;
};
#endif // AXEVENTCONNECTOR_H
его код:
C++ (Qt)
#include "axeventconnector.h"
#include <QAxObject>
#include <QUuid>
AxEventConnector::AxEventConnector(IUnknown* iface)
: _iface(NULL)
, _cpoints(NULL)
{
if (iface) {
iface->QueryInterface(IID_IUnknown, (void**)&_iface);
}
}
AxEventConnector::AxEventConnector(QAxObject* container)
: _iface(NULL)
, _cpoints(NULL)
{
if (container) {
container->queryInterface(IID_IUnknown, (void**)&_iface);
}
}
AxEventConnector::~AxEventConnector()
{
// clear sinks table
QVector<EventSinkBase*>::iterator it = _eventSinks.begin();
while (it != _eventSinks.end()) {
EventSinkBase* eventSink = *it;
eventSink->unadvise();
eventSink->release();
++it;
}
if (_cpoints) {
_cpoints->Release();
}
if (_iface) {
_iface->Release();
}
}
bool AxEventConnector::isValid() const
{
return !_iface;
}
bool AxEventConnector::connectEvent(
EventSinkBase* eventSink, bool deleteEventSinkAtError /* = true*/)
{
bool result = false;
if (!_iface) { return result; }
if (!_cpoints) {
_iface->QueryInterface(IID_IConnectionPointContainer, (void**)&_cpoints);
}
if (_cpoints) {
IConnectionPoint* cpoint = 0;
_cpoints->FindConnectionPoint(*(eventSink->iidEvent()), &cpoint);
if (cpoint) {
if (result = eventSink->advise(cpoint)) {
_eventSinks.append(eventSink);
} else
if (deleteEventSinkAtError){
delete eventSink;
}
cpoint->Release();
}
}
return result;
}
И наконец использование:
C++ (Qt)
QAxObject agent = ...;
AgentEvents* events = new AgentEvents();
AgentEventsNotifier* obj = events->notifier();
connect(obj, SIGNAL(initOnAdvise(QString,QAxObject*,QAxObject*)),
SLOT(agent_agentInited(QString,QAxObject*,QAxObject*)));
connect(obj, SIGNAL(changedStatus(QString)),
SLOT(agent_statusChanged(QString)));
connect(obj, SIGNAL(openedPhoneSession(QAxObject*)),
SLOT(agent_sessionOpened(QAxObject*)));
AxEventConnector connector = new AxEventConnector(agent);
connector->connectEvent(events);