Как проверить, поддерживает ли контейнер несколько подключений?

  • Реализован класс Sink — для получения уведомлений о событиях от COM-сервера.
  • Интерфейс событий унаследован от IDispatch.

У меня возникла проблема, из-за которой вызов IConnectionPoint::Advise возвращает E_NOTIMPL. Это может быть связано с тем, что точка подключения допускает только одно подключение — MSDN.

Примечание:

  • COM-сервер находится вне процесса
  • Чистая реализация C++

РЕДАКТИРОВАТЬ:

S8.tlh: исходный код C++, эквивалентный библиотеке типов Win32 S8.tlb:

struct __declspec(uuid("090910c3-28c3-45fe-861d-edcf11aa9788"))
IS8SimulationEvents : IDispatch
{

    // Methods:
    HRESULT S8SimulationReset ( );
    HRESULT S8SimulationEndRun ( );
    HRESULT S8SimulationCustomEvent (
        BSTR * TextInfo );
    HRESULT S8SimulationOpened ( );
    HRESULT S8SimulationEndTrial ( );
    HRESULT S8SimulationOEMEvent (
        BSTR * TextInfo );
    HRESULT S8SimulationReadyToClose ( );
    HRESULT S8SimulationUserMessage (
        long * Answer,
        BSTR * TextMsg,
        long ValidAnswers );
};

Реализация Class Sink — для обработки уведомлений о событиях:

class Sink : public IS8SimulationEvents
{
public:
Sink(){
    m_dwRefCount = 0;
};
~Sink(){};
/*
* IS8SimulationEvent interface functions
*/
HRESULT S8SimulationEndTrial()
{
    cout << "Simulation complete." << endl;
    return S_OK;;
};

HRESULT S8SimulationOpened()
{
    cout << "Simulation open." << endl;
    return S_OK;
};

HRESULT S8SimulationReadyToClose()
{
    cout << "Simulation ready to close" << endl;
    return S_OK;
};

ULONG STDMETHODCALLTYPE AddRef()
{
    m_dwRefCount++;
    return m_dwRefCount;
};

ULONG STDMETHODCALLTYPE Release()
{
    ULONG l;
    l = m_dwRefCount--;

    if (0 == m_dwRefCount)
    {
        delete this;
    }

    return m_dwRefCount;
};

HRESULT STDMETHODCALLTYPE QueryInterface(
                                        REFIID iid ,
                                        void **ppvObject)
{
    m_dwRefCount++;
    *ppvObject = (void *)this;
    return S_OK;
};

HRESULT STDMETHODCALLTYPE GetTypeInfoCount(UINT *pctinfo)
{
    return E_NOTIMPL;
};

HRESULT STDMETHODCALLTYPE GetIDsOfNames( 
                                        REFIID riid,
                                        LPOLESTR *rgszNames,
                                        UINT cNames,
                                        LCID lcid,
                                        DISPID *rgDispId)
{
    return E_NOTIMPL;
};

HRESULT STDMETHODCALLTYPE GetTypeInfo(
                                    unsigned int iTInfo,
                                    LCID lcid,
                                    ITypeInfo FAR* FAR* ppTInfo)
{
    return E_NOTIMPL;
};

HRESULT STDMETHODCALLTYPE Invoke(
                                DISPID dispIdMember,
                                REFIID riid,
                                LCID lcid,
                                WORD wFlags,
                                DISPPARAMS FAR* pDispParams,
                                VARIANT FAR* pVarResult,
                                EXCEPINFO FAR* pExcepInfo,
                                unsigned int FAR* puArgErr)
{
    HRESULT hresult = S_OK;
    if (pDispParams)
    {
        switch (dispIdMember) {
        case 1:
            return S8SimulationEndTrial();
        case 2:
            return S8SimulationOpened();
        case 3:
            return S8SimulationReadyToClose();
        default:
            return E_NOTIMPL;
        }

    }
    return E_NOTIMPL;
}
private:
    DWORD m_dwRefCount;
public:
void SetupConnectionPoint (IS8Simulation *pis8)
{

    HRESULT hresult;
    IConnectionPointContainer *pContainer = NULL;
    IConnectionPoint *pConnection = NULL;
    IUnknown *pSinkUnk = NULL;
    Sink *pSink = NULL;
    DWORD dwAdvise;

    dwAdvise = 0;

    hresult = pis8->QueryInterface(
                        __uuidof(IConnectionPointContainer),
                        (void **) &pContainer);

    if (SUCCEEDED(hresult))
    {
        cout << "IConnectionPointContainer inteface supported." << endl;
    } else {
        cerr << "Error: No such interface supported." << endl;
        exit (hresult);
    }

                                    __uuidof(IS8SimulationEvents),
                                    &pConnection); 

    switch (HRESULT_CODE(hresult)) {
        case NOERROR:
            cout << "Obtained valid interface pointer." << endl;
            break;
        case E_POINTER:
            cerr << "Invalid pointer: the address is not valid." << endl;
            exit (hresult);
            break;
        case CONNECT_E_NOCONNECTION:
            cerr << "This connectable object not support the "
                    "outgoing interface specified." << endl;
            exit (hresult);
            break;
        case E_UNEXPECTED:
        default:
            cerr << "Catastrophic failure." << endl;
            exit (hresult);
            break;
    }

    pContainer->Release();


    hresult = pSink->QueryInterface(
                            __uuidof(IUnknown),
                            (void **)&pSinkUnk);

    if (FAILED(hresult))
    {
        exit (EXIT_FAILURE);
    }

    hresult = pConnection->Advise(
                            pSinkUnk,
                            &dwAdvise);

    switch (HRESULT_CODE(hresult)) {
        case NOERROR:
            cout << "The connection has been established and "
                    "*dwAdvise has the connection token." << endl;
            break;
        case E_POINTER:
            cerr << "Invalid pointer: "
                    "the value pSinkUnk or dwAdvise is not valid." << endl;
            exit (hresult);
            break;
        case CONNECT_E_ADVISELIMIT:
            cerr << "The connection point has already reached "
                    "its limit of connections and cannot accept "
                    "any more." << endl;
            exit (hresult);
            break;
        case CONNECT_E_CANNOTCONNECT:
            cerr << "The sink does not support the interface "
                    "required by this connection point." << endl;
            exit (hresult);
            break;
        case E_NOTIMPL:
            break;
            case E_UNEXPECTED:
            default:
        cerr << "Catastrophic failure." << endl;
        exit (hresult);
        break;
    }
    return;
}
};

РЕДАКТИРОВАТЬ:

Реализация интерфейса IUnknown в классе Sink

ULONG STDMETHODCALLTYPE AddRef()
{
    m_dwRefCount++;
    return m_dwRefCount;
};

ULONG STDMETHODCALLTYPE Release()
{
    ULONG l;
    l = m_dwRefCount--;

    if (0 == m_dwRefCount)
    {
        delete this;
    }

    return m_dwRefCount;
};

HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **ppvObject)
{
    m_dwRefCount++;
    *ppvObject = (void *)this;
    return S_OK;
};

Вопрос:

  • Как проверить, поддерживает ли контейнер несколько подключений?
  • Если требуется дополнительная информация, прокомментируйте соответствующим образом.

person Aaron    schedule 27.01.2009    source источник
comment
Это также может произойти, если ваш объект приемника, который вы отправляете на консультацию, не реализует IUnknown.   -  person Vinay    schedule 27.01.2009
comment
Как это проверить? И если да, то как изменить?   -  person Aaron    schedule 27.01.2009
comment
@Vinay: Как вы думаете, вы могли бы помочь немного больше, пожалуйста?   -  person Aaron    schedule 27.01.2009
comment
Попробуйте проследить внутри Advisor API. Вы также можете попробовать QI для объекта IUnknown перед вызовом совета. Как Майкл указал на статью MSDN, я подозреваю, что QI не работает для IUnknown.   -  person Vinay    schedule 29.01.2009


Ответы (4)


Пожалуйста, прочитайте статью MSDN еще раз.

 A connection point that allows only one interface
 can return E_NOTIMPL FROM the IConnectionPoint::EnumConnections method
EnumConnections : E_NOTIMPL 
The connection point does not support enumeration.

IConnectionPoint::Advise требуется для ответа

CONNECT_E_ADVISELIMIT

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

--

Майкл

person Michael    schedule 27.01.2009
comment
Привет, Майкл, спасибо, что указали на это. Было бы безопасно игнорировать это и продолжать как обычно? - person Aaron; 27.01.2009
comment
Если вы хотите увидеть мою реализацию класса Sink или дополнительную информацию, пожалуйста, дайте мне знать. Я новичок в программировании COM - я застрял в этом конкретном месте - person Aaron; 27.01.2009
comment
Простейшая вещь может привести к тому, что советуют вернуть E_NOTIMPL, 1 ) например, вызовы coinitializeex не совпадают. 2) клиент и сервер не являются одной и той же моделью, т.е.) APARTMENTTHREADED / vs MULTITTHREADED Кто написал серверную точку подключения или класс событий? -- Майкл - person Michael; 27.01.2009
comment
@Michael: Во-первых, СПАСИБО за ваш ответ! Что касается: 1) я сделал только один вызов coinitialize() 2) что касается этого предложения - я рассмотрю 3) CP сервера был реализован Simul8 и классом событий - вы имеете в виду класс Sink ? В противном случае Simul8 - person Aaron; 28.01.2009
comment
@Michael: Если нужно, я прикреплю полную реализацию (на данный момент COM-клиента и Sink Class). Просто дайте мне знать - привет :) - person Aaron; 28.01.2009
comment
Содержится ли код сортировки (прокси/заглушка) в файле TLB, сгенерированный компилятором MIDL? - person Aaron; 28.01.2009
comment
Атклин. Где-то в файле Simul8 TLB должен быть интерфейс событий. Ваш класс приемника должен реализовать методы этого класса. если только Simul8 не предоставляет события. - person Michael; 29.01.2009
comment
Спасибо за ответ Михаил :). Я могу подтвердить, что интерфейс событий присутствует. Как подтвердить/установить, относятся ли клиент и сервер к одной и той же модели? Я исследую... - person Aaron; 29.01.2009
comment
simul8.com/products/features/external.htm#com Это может случиться так, что реализованный вами источник событий не является правильным интерфейсом для конкретного ком-сервера. Я опубликовал еще один ответ из-за ограничений длины комментариев. - person Michael; 29.01.2009

Библиотека COM вызывает QI для многих интерфейсов, таких как IUnknown, IMarshall и т. д., когда вызывается вызов Advise.

Как Майкл указал на статью MSDN, в ней говорится, что она возвращает E_NOTIMPL только для EnumConnections API, а не для Advise. Я подозреваю, что QI возвращает E_NOTIMPL. Поэтому попробуйте отследить Advise API. И вы узнаете о провале QI.

person Vinay    schedule 30.01.2009
comment
Как я могу отслеживать Advise API с помощью инструментов VC++? - person Aaron; 30.01.2009
comment
Я попытаюсь найти проблему, описанную выше. - person Aaron; 30.01.2009
comment
Просто поставьте точку останова на hresult = pConnection-›Advise( pSinkUnk, &dwAdvise); и нажмите F11, когда он сломается. - person Vinay; 30.01.2009

Решено:

  • Использование чистого C++
  • Нет ATL

Со следующей реализацией QI:

HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **ppvObject)
{
    if (iid == __uuidof(IUnknown) || iid == __uuidof(IS8SimulationEvents))
    {
        *ppvObject = (IS8SimulationEvents*)this;
    } else {
        *ppvObject = NULL;
        return E_NOINTERFACE;
    }
    m_dwRefCount++;
    return S_OK;
};
person Aaron    schedule 01.02.2009

AtlAdvise должен заставить точку подключения вызывать QI в приемнике событий. Вы не можете быть уверены, ЧТО или сколько раз он может вызвать QI.

Предложение: Обратитесь к чему-то подобному в EventSink, чтобы определить правильный интерфейс события.

void GuidToString(PTCHAR s, LPGUID piid )
{
_stprintf(s, _T("{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}"),
    piid->Data1, piid->Data2,
    piid->Data3,
    piid->Data4[0], piid->Data4[1],
    piid->Data4[2], piid->Data4[3],
    piid->Data4[4], piid->Data4[5],
    piid->Data4[6], piid->Data4[7]);
    // Outputdebugstr (s)
}

ТАКЖЕ приемник событий QueryInterface должен делать что-то вроде

if (iid == IID_IUnknown || iid == IID_AppEvents)
     *ppObj = (AppEvents*)this;

Поэтому он возвращает только правильный интерфейс.

person Michael    schedule 29.01.2009
comment
Получено исключение из HRESULT 0xc000005, опубликуйте внесение изменений в QI в приемнике событий: m_dwRefCount++; if (iid == IID_IUnknown || iid == __uuidof(IS8SimulationEvents)) { ppvObject = (void *)this; } вернуть S_OK; - person Aaron; 30.01.2009
comment
Я прикреплю полную реализацию Sink и IS8SimulationEvents из S8.tlh. - person Aaron; 30.01.2009