【问题标题】:How to check if a container supports multiple connections?如何检查容器是否支持多个连接?
【发布时间】:2013-04-03 23:38:32
【问题描述】:
  • 实现了 Sink 类 - 从 COM 服务器接收事件通知
  • 事件接口派生自 IDispatch

我有一个问题,即 IConnectionPoint::Advise 调用返回 E_NOTIMPL。这可能是因为连接点只允许一个连接 - MSDN

注意:

  • COM 服务器处于进程外
  • 纯 C++ 实现

编辑:

S8.tlh:相当于 Win32 类型库 S8.tlb 的 C++ 源代码:

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;
}
};

编辑:

Sink类中IUnknown接口的实现

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;
};

问题:

  • 如何检查容器是否支持多个连接?
  • 如果需要更多信息,请作出相应评论。

【问题讨论】:

  • 如果您发送给建议的接收器对象未实现 IUnknown,也会发生这种情况
  • 如何检查这个?如果是这样,如何改变?
  • @Vinay:你觉得你能帮上忙吗?
  • 尝试在advice api中追踪。您也可以在调用建议之前尝试对 IUnknown 对象进行 QI。正如迈克尔在 MSDN 文章中指出的那样,我怀疑 QI 因 IUnknown 而失败。

标签: c++ visual-studio-2008 com connection-points


【解决方案1】:

请再次阅读 MSDN 文章。

仅允许一个接口的连接点 可以返回 E_NOTIMPL FROM IConnectionPoint::EnumConnections 方法 枚举连接:E_NOTIMPL 连接点不支持枚举。

IConnectionPoint::Advise 需要回复

CONNECT_E_ADVISELIMIT

当他的连接点已经达到它的连接限制并且不能再接受时。

--

迈克尔

【讨论】:

  • 嗨迈克尔,感谢您指出这一点。忽略这一点并照常继续是否安全?
  • 如果您想查看我的接收器类的实现或更多信息,请告诉我。我是 COM 编程新手 - 我被困在这个特定点
  • 最简单的事情可能会导致建议返回 E_NOTIMPL, 1 )例如,对 coinitializeex 的调用不匹配。 2)客户端和服务器不是同一模型,即)APARTMENTTHREADED /vs MULTITHREADED 谁编写了服务器连接点或事件类? ——迈克尔
  • @Michael:首先,感谢您的回复!关于:1)我只调用了一次 coinitialize() 2)关于这个建议 - 我会调查 3)服务器 CP 是由 Simul8 和事件类实现的 - 你是指 Sink 类?否则 Simul8
  • @Michael:如果需要,我会附上完整的实现(到目前为止的 COM 客户端和接收器类)。让我知道 - 问候:)
【解决方案2】:

在调用 Advise 调用时,COM 库会为 IUnknown、IMarshall 等众多接口调用 QI。

正如 Michael 在 MSDN 文章中指出的那样,它说它只为 EnumConnections API 而不是 Advise 返回 E_NOTIMPL。我怀疑 QI 返回 E_NOTIMPL。因此,请尝试跟踪 Advise API。你会知道 QI 的失败。

【讨论】:

  • 如何使用 VC++ 工具跟踪 Advise API?
  • 我会尝试以上方法来查找问题
  • 只需在 hresult = pConnection->Advise( pSinkUnk, &dwAdvise); 处放一个断点然后按 F11,当它中断时。
【解决方案3】:

已解决:

  • 使用纯 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;
};

【讨论】:

    【解决方案4】:

    AtlAdvise 应该使连接点在事件接收器上调用 QI。 您无法确定它会调用 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;
    

    所以它只返回正确的界面。

    【讨论】:

    • 从 HRESULT 0xc000005 获得异常,在事件接收器中对 QI 进行更改后:m_dwRefCount++; if (iid == IID_IUnknown || iid == __uuidof(IS8SimulationEvents)) { ppvObject = (void *)this; } 返回 S_OK;
    • 我将附上完整的 Sink 实现和来自 S8.tlh 的 IS8SimulationEvents
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-07-06
    • 2020-01-04
    • 2018-06-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-17
    相关资源
    最近更新 更多