事例:组态软件中,通过web客户端控件实现数据的动画显示,客户端控件绑定了指定的数据点名称,在客户端后台运行一服务进程与服务器通过SOCKET进行通讯,客户端后台进程解码后通过点名来触发指定的客户端控件显示数据。我的想法是在客户端通过STDMETHOD(Advise)(IUnknown* pUnkSink, DWORD* pdwCookie);建立连接时会得到pdwCookie,控件通过一REGISTER(TAGNAME,pdwCookie)向后台服务进程发送订阅,后台建立hash表记录点名与pdwCookie的映射,当解码后通过点名找到pdwCookie,我想能不能通过pdwCookie这个参数触发指定的控件显示新数据或动画,advise过程如下:
STDMETHODIMP IConnectionPointImplMT<T, piid, CDV>::Advise(IUnknown* pUnkSink,
DWORD* pdwCookie)
{
IUnknown* p;
HRESULT hRes = S_OK;
if (pUnkSink == NULL || pdwCookie == NULL)
return E_POINTER;
IID iid;
GetConnectionInterface(&iid);
hRes = pUnkSink->QueryInterface(iid, (void**)&p);
if (SUCCEEDED(hRes))
{
m_CPMTCritSec.Lock();
DWORD dwGITCookie;
hRes = m_pGIT->RegisterInterfaceInGlobal(
p, iid, &dwGITCookie);
if(hRes == S_OK)
{
// Using the CCom(Dynamic)UnkArray to store the cookie instead of an IUnknown *:
*pdwCookie = m_vec.Add(reinterpret_cast<IUnknown *>(dwGITCookie));
hRes = (*pdwCookie != NULL) ? S_OK : CONNECT_E_ADVISELIMIT; if (hRes != S_OK)
m_pGIT->RevokeInterfaceFromGlobal(dwGITCookie);
}
m_CPMTCritSec.Unlock();
// GIT will have AddRef'ed p:
p->Release();
}
else if (hRes == E_NOINTERFACE)
hRes = CONNECT_E_CANNOTCONNECT;
if (FAILED(hRes))
*pdwCookie = 0;
return hRes;
}
        其中这个m_vec好像就是管理连接点的连接枚举器。我做过的一个出接口如下:
HRESULT Fire_Onlink2(VARIANT tagname, VARIANT value, VARIANT tagtime)
{
CComVariant varResult;
T* pT = static_cast<T*>(this);
int nConnectionIndex;
CComVariant* pvars = new CComVariant[3];
int nConnections = m_vec.GetSize();

for (nConnectionIndex = 0; nConnectionIndex < nConnections; nConnectionIndex++)
{
pT->Lock();
CComPtr<IUnknown> sp = m_vec.GetAt(nConnectionIndex);
pT->Unlock();
IDispatch* pDispatch = reinterpret_cast<IDispatch*>(sp.p);
if (pDispatch != NULL)
{
VariantClear(&varResult);
pvars[2] = tagname;
pvars[1] = value;
pvars[0] = tagtime;
DISPPARAMS disp = { pvars, NULL, 3, 0 };
pDispatch->Invoke(0x3, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, &varResult, NULL, NULL);
}
}
delete[] pvars;
return varResult.scode;

}
};
          其中通过 m_vec使所有的连接执行。能否通过m_vec,pdwCookie找到指定的sink然后触发指定的控件呢?而不比当一个数据点值过来时触发所有的控件,然后由控件对比是不是自己绑定的点。请高人指点,跟我一样的大家也出出主意讨论下,不胜感谢。

解决方案 »

  1.   

    inline DWORD CComDynamicUnkArray::Add(IUnknown* pUnk)
    {
    IUnknown** pp = NULL;
    if (m_nSize == 0) // no connections
    {
    m_pUnk = pUnk;
    m_nSize = 1;
    return (DWORD)m_pUnk;
    }
    else if (m_nSize == 1)
    {
    //create array
    pp = (IUnknown**)malloc(sizeof(IUnknown*)*_DEFAULT_VECTORLENGTH);
    if (pp == NULL)
    return 0;
    memset(pp, 0, sizeof(IUnknown*)*_DEFAULT_VECTORLENGTH);
    *pp = m_pUnk;
    m_ppUnk = pp;
    m_nSize = _DEFAULT_VECTORLENGTH;
    }
    for (pp = begin();pp<end();pp++)
    {
    if (*pp == NULL)
    {
    *pp = pUnk;
    return (DWORD)pUnk;
    }
    }
    int nAlloc = m_nSize*2;
    pp = (IUnknown**)realloc(m_ppUnk, sizeof(IUnknown*)*nAlloc);
    if (pp == NULL)
    return 0;
    m_ppUnk = pp;
    memset(&m_ppUnk[m_nSize], 0, sizeof(IUnknown*)*m_nSize);
    m_ppUnk[m_nSize] = pUnk;
    m_nSize = nAlloc;
    return (DWORD)pUnk;
    }     查到了cookie值就是m_vec这个vector添加进去的IUnknown指针的(DWORD),这样就可以在出接口中
    for   (nConnectionIndex   =   0;   nConnectionIndex   <   nConnections;   nConnectionIndex++) 

    pT-> Lock(); 
    CComPtr <IUnknown>   sp   =   m_vec.GetAt(nConnectionIndex); 
    这端进行判断对指定的控件进行触发。晚了睡觉了,希望对遇到同样问题的兄弟门有帮助
      

  2.   

    完全可以,你在fire_***函数中通过m_vec循环查找你想要的cookie值,只有这个值匹配你才进行调用,其它都屏蔽。记得看看ATL源码,连接点指针记录到一个联合体中,如果只有一个连接点,则该联合体的值就是连接点指针(cookie=0),如果有两个以上的连接点,则联合体的值指向一个连接点指针数组,COOKIE值就是指针在数组中的索引值。ATL这样处理只是为了优化性能。
      

  3.   

    还是存在问题,我的想法是在web客户端用一控件负责SOCKET通讯,其他控件与这一控件进行一对多的相互通信,随着页面跳转这一控件需动态的与其他控件建立连接与删除连接,主控件可以提供出接口给其他控件连接,以完成数据的动态显示,但主控件也需要接受其他控件的指令。也要通过其他控件提供连接点对象吗?主控件如何advise到其他控件的出接口。其他控件是随着页面跳转而出现的。能否在其它控件主动连接到主控件时,由那个IUnknown*   pUnkSink获得其他控件的连接点对象然后advise。还是采用其他手段。
      

  4.   

    advise是单向的,必须由客户端调用。看你的情况,你完全可以不必使用连接点,而是通过调用服务器接口方法传递客户端的某个接口指针(这个指针完全类似于连接点,传递过程中必须能被列集),作用就像订阅一样登记客户,服务器可以随时调用这个接口方法来通知客户,而且可以任意双向订阅。
      

  5.   

    就是指不用实现标准的IConnectPoint,而是一种完全自定义的机制(类似于连接点),当然advise就不能使用了。实际运用其实很简单,假设服务器是IServer,提供一个事件接口IServerEvent,客户端实现了IServerEvent,现在就是要让服务器知道客户端的这个接口,可以在IServer中提供一个方法,比如IServer::RegEventClient(IDispatch* pEventReceiver)来登记客户端,当客户端创建服务器后,调用pServer->RegEventClient((IDispatch*)this),以后服务器有事件时就可以调用客户端的IServerEvent接口方法进行通知了。
    服务器的RegEventClient可以类似这样写:// 客户端登记事件
    HRESULT CServer::RegEventClient(IDispatch* pEventReceiver)
    {
      // 假设服务器的类有一个成员变量 IServerEvent* m_pRecv;
      pEventReceiver->QueryInterface(IID_IServerEvent, NULL, &m_pRecv);
      return S_OK;
    }// 事件通知客户端
    void CServer::fire_ServerEvent()
    {
      m_pRecv->SomeNotify(...);
    }