Russian Qt Forum
Май 02, 2024, 05:07 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: ActiveQt подключение к events  (Прочитано 8212 раз)
Johnik
Крякер
****
Offline Offline

Сообщений: 339


Просмотр профиля
« : Ноябрь 12, 2013, 13:38 »

Здравствуйте.

Есть Qt 5.1.1 и Windows XP 32 bit.

Есть некий COM объект, вот часть idl:
Код:
COM объект реализует несколько интерфейсов, среди прочих IAgentEvents
[
    object,
    uuid(0610FFE9-0FAA-46d1-B3E1-A1377D793E10),
    oleautomation,
    pointer_default(unique)
]
interface IAgentEvents:IUnknown
{
    HRESULT InitOnAdvise([in] BSTR status,
                         [in] IQueueCollection* queues,
                         [in] IAgentPhoneSession* session);
    HRESULT OnPhoneSessionOpened( [in] IAgentPhoneSession* session);
    HRESULT OnStatusChanged( [in] BSTR status);
};

И есть объект реализующий этот интерфейс:
[
uuid(F24B5336-8DDA-4f8f-B561-78AE4D335657),
helpstring("Call Center Agent")
]
coclass CallCenterAgent{
[default] interface IAgent;
[source,default] interface IAgentEvents;
};

получаю объект агента следующим образом:
Код:
QAxObject* agent = callCenter->querySubObject("Agent()");
объект нормально создается, но к объекту не "цепляются" события:
InitOnAdvise(...);
OnPhoneSessionOpened(...);
OnStatusChanged(...);

посмотрел по исходному коду Qt:
события в QAxObject инициализируются (вызывается QAxBase::connectNotify()), только во время первого QObject::connect к сигналу этого объекта.
и еще в qaxbase.cpp есть код:
Код:
TYPEKIND eventKind = eventAttr->typekind;
eventinfo->ReleaseTypeAttr(eventAttr);
if (eventKind != TKIND_DISPATCH) {
    eventinfo->Release();
    break;
}
как раз игнорируется мой случай

В другом COM объетке связанном с вышеописанным есть такое описание:

Код:
[
    uuid(84692E4E-5D0D-4f41-B298-B974792FD8A4),
    helpstring("Connection Class")
]
coclass CSConnection
{
    [default] interface ICSConnection;
    [default, source] dispinterface _ICSConnectionEvents;
};

В котором события описаны как:
dispinterface _ICSConnectionEvents

С отловом событий в этом объекте проблем нет.


Как быть? Как поймать события?
Может кто знает куда копнуть для более низкоуровневой реализации отлова событий?
.net цепляется без проблем к этому объекту и его событиям.
Уже думаю писать обертку .net + qt, но тоже пока не знаю с какой стороны подойти.

Записан
Johnik
Крякер
****
Offline Offline

Сообщений: 339


Просмотр профиля
« Ответ #1 : Ноябрь 14, 2013, 09:53 »

Решение есть, почти написал, приведу в порядок и опубликую.

Но есть вопрос к знатокам COM (ActiveX).
Можно ли объединить QObject и IUnknown в одном объекте (нужно для задействования сигнального механизма Qt)? И тот и другой классы требуют чтобы были указаны первым при наследовании, особенно это касается IUnknown для правильного формирование vtable. Я прав?
Код:
interface IAgentEvent : public IUnknown
{
...
}

class AgentEvent : public QObject, public IAgentEvent
{
    Q_OBJECT
...
}
Записан
Johnik
Крякер
****
Offline Offline

Сообщений: 339


Просмотр профиля
« Ответ #2 : Февраль 09, 2014, 02:00 »

Выкладываю решение:

Класс для подключения к 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 &notifier; }
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);
 
 
« Последнее редактирование: Февраль 10, 2014, 09:00 от Johnik » Записан
gil9red
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 1805



Просмотр профиля WWW
« Ответ #3 : Февраль 09, 2014, 02:51 »

Что то не так с этим кодом Улыбающийся
Код
C++ (Qt)
   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));
   }
 
Записан

Johnik
Крякер
****
Offline Offline

Сообщений: 339


Просмотр профиля
« Ответ #4 : Февраль 09, 2014, 02:57 »

помимо нехватки
Код:
        return S_OK;
еще что-то?
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.184 секунд. Запросов: 22.