cnctnpt.cpp
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 12k
Category:

Windows Kernel

Development Platform:

Visual C++

  1. //
  2. //  CConnectionPoint
  3. //
  4. //  Common implementation for CConnectionPoint.
  5. //
  6. //
  7. //  Since EnumConnections is called so much, we have a custom
  8. //  enumerator for it which is faster than CStandardEnum and which
  9. //  performs fewer memory allocations.
  10. //
  11. class CConnectionPointEnum : public IEnumConnections
  12. {
  13. public:
  14.     // IUnknown methods
  15.     //
  16.     virtual STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj);
  17.     virtual STDMETHODIMP_(ULONG) AddRef(void);
  18.     virtual STDMETHODIMP_(ULONG) Release(void);
  19.     // IEnumConnections methods
  20.     //
  21.     STDMETHOD(Next)(ULONG ccd, LPCONNECTDATA rgcd, ULONG *pcdFetched);
  22.     STDMETHOD(Skip)(ULONG ccd) { return Next(ccd, NULL, NULL); }
  23.     STDMETHOD(Reset)(void) { m_iPos = 0; return S_OK; }
  24.     STDMETHOD(Clone)(IEnumConnections **ppecOut);
  25.     friend HRESULT CConnectionPointEnum_Create(CConnectionPoint *pcp, int iPos, IEnumConnections **pecOut);
  26. private:
  27.     CConnectionPointEnum(CConnectionPoint *pcp, int iPos)
  28.     : m_cRef(1), m_pcp(pcp), m_iPos(iPos) { m_pcp->AddRef(); }
  29.     ~CConnectionPointEnum() { m_pcp->Release(); }
  30.     int m_cRef;                         // refcount
  31.     CConnectionPoint *m_pcp;            // my dad
  32.     int m_iPos;                         // enumeration state
  33. };
  34. //
  35. //  When we need to grow the sink array, we grow by this many.
  36. //
  37. #define GROWTH      8
  38. //
  39. //  OLE says that zero is never a valid cookie, so our cookies are
  40. //  the array index biased by unity.
  41. //
  42. #define COOKIEFROMINDEX(i)      ((i) + 1)
  43. #define INDEXFROMCOOKIE(dw)     ((dw) - 1)
  44. CConnectionPoint::~CConnectionPoint ()
  45. {
  46.     // clean up some memory stuff
  47.     UnadviseAll();
  48.     if (m_rgSinks)
  49.         CoTaskMemFree(m_rgSinks);
  50. }
  51. HRESULT CConnectionPoint::UnadviseAll(void)
  52. {
  53.     if (m_rgSinks)
  54.     {
  55.         int x;
  56.         for (x = 0; x < m_cSinksAlloc; x++)
  57.         {
  58.             ATOMICRELEASE(m_rgSinks[x]);
  59.         }
  60.     }
  61.     return S_OK;
  62. }
  63. //
  64. //  For backwards-compatibility with IE4, our superclass is
  65. //  CIE4ConnectionPoint.
  66. //
  67. STDMETHODIMP CConnectionPoint::QueryInterface(REFIID riid, void **ppvObjOut)
  68. {
  69.     if (IsEqualIID(riid, IID_IConnectionPoint) ||
  70.         IsEqualIID(riid, IID_IUnknown))
  71.     {
  72.         *ppvObjOut = SAFECAST(this, IConnectionPoint *);
  73.         AddRef();
  74.         return S_OK;
  75.     }
  76.     *ppvObjOut = NULL;
  77.     return E_NOINTERFACE;
  78. }
  79. STDMETHODIMP CConnectionPoint::GetConnectionInterface(IID *piid)
  80. {
  81.     *piid = *m_piid;
  82.     return S_OK;
  83. }
  84. STDMETHODIMP CConnectionPoint::GetConnectionPointContainer(IConnectionPointContainer **ppCPC)
  85. {
  86.     return m_punk->QueryInterface(IID_IConnectionPointContainer, (void **)ppCPC);
  87. }
  88. STDMETHODIMP CConnectionPoint::Advise(IUnknown *pUnk,DWORD *pdwCookie)
  89. {
  90.     HRESULT    hr;
  91.     IUnknown **rgUnkNew;
  92.     IUnknown  *punkTgt;
  93.     int        i = 0;
  94.     if (!pdwCookie)
  95.         return E_POINTER;
  96.     *pdwCookie = 0;
  97.     // first, make sure everybody's got what they thinks they got
  98.     hr = pUnk->QueryInterface(*m_piid, (LPVOID *)&punkTgt);
  99.     if (SUCCEEDED(hr))
  100.     {
  101. #ifdef DEBUG
  102.         //
  103.         //  If we are not an IPropertyNotifySink, then we had better
  104.         //  be derived from IDispatch.  Try to confirm.
  105.         //
  106.         if (m_piid != &IID_IPropertyNotifySink)
  107.         {
  108.             IDispatch *pdisp;
  109.             if (SUCCEEDED(pUnk->QueryInterface(IID_IDispatch, (LPVOID *)&pdisp)))
  110.             {
  111.                 pdisp->Release();
  112.             }
  113.             else
  114.             {
  115.                 AssertMsg(0, TEXT("CConnectionPoint: IID %08x not derived from IDispatch"), m_piid->Data1);
  116.             }
  117.         }
  118. #endif
  119.     }
  120.     else
  121.     {
  122.         if (m_piid != &IID_IPropertyNotifySink)
  123.         {
  124.             // This is against spec, but raymondc is guessing that this is done
  125.             // for compatibility with VB or some other scripting language that
  126.             // talks IDispatch but not necessarily the IDispatch-derived
  127.             // thingie that we officially speak.  Since we really source
  128.             // merely IDispatch::Invoke, we can satisfactorily accept any
  129.             // IDispatch as a sink.
  130.             hr = pUnk->QueryInterface(IID_IDispatch, (LPVOID*)&punkTgt);
  131.         }
  132.     }
  133.     if (SUCCEEDED(hr))
  134.     {
  135.         // we no longer optimize the case where there is only one sink
  136.         // because it's rarely the case any more.
  137.         //
  138.         //  If the table is full, then grow it.
  139.         //
  140.         if (m_cSinks >= m_cSinksAlloc)
  141.         {
  142.             //  CoTaskMemRealloc is so smart.  If you realloc from NULL, it
  143.             //  means Alloc.  What this means for us?  No special cases!
  144.             rgUnkNew = (IUnknown **)CoTaskMemRealloc(m_rgSinks, (m_cSinksAlloc + GROWTH) * sizeof(IUnknown *));
  145.             if (!rgUnkNew)
  146.             {
  147.                 punkTgt->Release();
  148.                 // GetLastError();
  149.                 return E_OUTOFMEMORY;
  150.             }
  151.             m_rgSinks = rgUnkNew;
  152.             //
  153.             //  OLE does not guarantee that the new memory is zero-initialized.
  154.             //
  155.             ZeroMemory(&m_rgSinks[m_cSinksAlloc], GROWTH * sizeof(IUnknown *));
  156.             m_cSinksAlloc += GROWTH;
  157.         }
  158.         //
  159.         //  Look for an empty slot.  There has to be one since we grew the
  160.         //  table if we were full.
  161.         //
  162.         for (i = 0; m_rgSinks[i]; i++) {
  163.             ASSERT(i < m_cSinksAlloc);
  164.         }
  165.         ASSERT(m_rgSinks[i] == NULL);   // Should've found a free slot
  166.         m_rgSinks[i] = punkTgt;
  167.         *pdwCookie = COOKIEFROMINDEX(i);
  168.         m_cSinks++;
  169.         // notify our owner that someone is connecting to us --
  170.         // they may want to hook something up at the last minute
  171.         //
  172.         IConnectionPointCB* pcb;
  173.         if (SUCCEEDED(m_punk->QueryInterface(IID_IConnectionPointCB, (LPVOID*)&pcb)))
  174.         {
  175.             pcb->OnAdvise(*m_piid, m_cSinks, *pdwCookie);
  176.             pcb->Release();
  177.         }
  178.     }
  179.     else
  180.     {
  181.         hr = CONNECT_E_CANNOTCONNECT;
  182.     }
  183.     return hr;
  184. }
  185. STDMETHODIMP CConnectionPoint::Unadvise(DWORD dwCookie)
  186. {
  187.     if (!dwCookie)
  188.         return S_OK;
  189.     int x = INDEXFROMCOOKIE(dwCookie);
  190.     // Validate the cookie.
  191.     if (x >= m_cSinksAlloc || m_rgSinks[x] == NULL)
  192.         return CONNECT_E_NOCONNECTION;
  193.     // notify our owner that someone is disconnecting from us --
  194.     // they may want to clean up from the OnAdvise call
  195.     // Perform the callback while the sink is still alive, in case
  196.     // the callback wants to do some last-minute communication.
  197.     //
  198.     IConnectionPointCB* pcb;
  199.     if (SUCCEEDED(m_punk->QueryInterface(IID_IConnectionPointCB, (LPVOID*)&pcb)))
  200.     {
  201.         pcb->OnUnadvise(*m_piid, m_cSinks - 1, dwCookie);
  202.         pcb->Release();
  203.     }
  204.     // Free up the slot.  We cannot relocate any elements because that
  205.     // would screw up the outstanding cookies.
  206.     ATOMICRELEASE(m_rgSinks[x]);
  207.     m_cSinks--;
  208.     // Don't free the memory on the loss of the last sink; a new one
  209.     // will probably show up soon.
  210.     return S_OK;
  211. }
  212. //=--------------------------------------------------------------------------=
  213. // CConnectionPoint::EnumConnections
  214. //=--------------------------------------------------------------------------=
  215. // enumerates all current connections
  216. //
  217. // Paramters:
  218. //    IEnumConnections ** - [out] new enumerator object
  219. //
  220. // Output:
  221. //    HRESULT
  222. //
  223. // NOtes:
  224. STDMETHODIMP CConnectionPoint::EnumConnections(IEnumConnections **ppEnumOut)
  225. {
  226. #if 1
  227.     return CConnectionPointEnum_Create(this, 0, ppEnumOut);
  228. #else
  229.     CONNECTDATA *rgConnectData = NULL;
  230.     int i, cSinks;
  231.     // CopyAndAddRefObject assumes that the IUnknown comes first
  232.     // So does CStandardEnum
  233.     COMPILETIME_ASSERT(FIELD_OFFSET(CONNECTDATA, pUnk) == 0);
  234.     cSinks = 0;
  235.     if (_HasSinks())
  236.     {
  237.         // allocate some memory big enough to hold all of the sinks.
  238.         //
  239.         // Must use GlobalAlloc because CStandardEnum uses GlobalFree.
  240.         //
  241.         rgConnectData = (CONNECTDATA *)GlobalAlloc(GMEM_FIXED, m_cSinks * sizeof(CONNECTDATA));
  242.         if (!rgConnectData)
  243.             return E_OUTOFMEMORY;
  244.         // fill in the array
  245.         //
  246.         for (i = 0; i < m_cSinksAlloc; i++)
  247.         {
  248.             if (m_rgSinks[i])
  249.             {
  250.                 rgConnectData[cSinks].pUnk = m_rgSinks[i];
  251.                 rgConnectData[cSinks].dwCookie = i + 1;
  252.                 cSinks++;
  253.                 // In case m_rgSinks gets out of sync with m_cSinks,
  254.                 // just stop when the array gets full.
  255.                 if (cSinks >= m_cSinks)
  256.                 {
  257.                     break;
  258.                 }
  259.             }
  260.         }
  261.         // Make sure we found all the items we should've found
  262.         ASSERT(cSinks == m_cSinks);
  263.     }
  264.     // create a statndard  enumerator object.
  265.     //
  266.     *ppEnumOut = (IEnumConnections *)(IEnumGeneric *)new CStandardEnum(IID_IEnumConnections,
  267.                        TRUE, cSinks, sizeof(CONNECTDATA), rgConnectData, CopyAndAddRefObject);
  268.     if (!*ppEnumOut)
  269.     {
  270.         CoTaskMemFree(rgConnectData);
  271.         return E_OUTOFMEMORY;
  272.     }
  273.     return S_OK;
  274. #endif
  275. }
  276. //
  277. // CConnectionPoint::DoInvokeIE4
  278. //
  279. // Calls all sinks' IDispatch::Invoke() with Cancel semantics.
  280. HRESULT CConnectionPoint::DoInvokeIE4(LPBOOL pf, LPVOID *ppv, DISPID dispid, DISPPARAMS *pdispparams)
  281. {
  282.     return IConnectionPoint_InvokeWithCancel(this->CastToIConnectionPoint(),
  283.                                     dispid, pdispparams, pf, ppv);
  284. }
  285. //
  286. //  CConnectionPointEnum
  287. //
  288. HRESULT CConnectionPointEnum_Create(CConnectionPoint *pcp, int iPos, IEnumConnections **ppecOut)
  289. {
  290.     *ppecOut = new CConnectionPointEnum(pcp, iPos);
  291.     return *ppecOut ? S_OK : E_OUTOFMEMORY;
  292. }
  293. STDMETHODIMP CConnectionPointEnum::QueryInterface(REFIID riid, void **ppvObjOut)
  294. {
  295.     if (IsEqualIID(riid, IID_IEnumConnections) ||
  296.         IsEqualIID(riid, IID_IUnknown))
  297.     {
  298.         *ppvObjOut = (IUnknown *)this;
  299.         AddRef();
  300.         return S_OK;
  301.     }
  302.     *ppvObjOut = NULL;
  303.     return E_NOINTERFACE;
  304. }
  305. STDMETHODIMP_(ULONG) CConnectionPointEnum::AddRef()
  306. {
  307.     return ++m_cRef;
  308. }
  309. STDMETHODIMP_(ULONG) CConnectionPointEnum::Release()
  310. {
  311.     ULONG cRef = --m_cRef;
  312.     if (cRef == 0)
  313.         delete this;
  314.     return cRef;
  315. }
  316. //
  317. //  Next also doubles as Skip.  If you pass a NULL output buffer, then
  318. //  nothing gets copied (i.e., you're a Skip).
  319. //
  320. STDMETHODIMP CConnectionPointEnum::Next(ULONG ccd, LPCONNECTDATA rgcd, ULONG *pcdFetched)
  321. {
  322.     ULONG ccdFetched = 0;
  323.     while (ccdFetched < ccd)
  324.     {
  325.         //
  326.         //  Look for the next sink or the end of the array
  327.         //
  328.         while (m_iPos < m_pcp->m_cSinksAlloc && m_pcp->m_rgSinks[m_iPos] == NULL)
  329.         {
  330.             m_iPos++;
  331.         }
  332.         if (m_iPos >= m_pcp->m_cSinksAlloc)
  333.             break;
  334.         if (rgcd)
  335.         {
  336.             //
  337.             //  Copy it to the output buffer
  338.             //
  339.             rgcd->pUnk = m_pcp->m_rgSinks[m_iPos];
  340.             rgcd->dwCookie = COOKIEFROMINDEX(m_iPos);
  341.             rgcd->pUnk->AddRef();
  342.             rgcd++;
  343.         }
  344.         m_iPos++;
  345.         ccdFetched++;
  346.     }
  347.     if (pcdFetched)
  348.         *pcdFetched = ccdFetched;
  349.     return (ccdFetched < ccd) ? S_FALSE : S_OK;
  350. }
  351. //
  352. //  Our clone enumerates the same CConnectionPoint from the same position.
  353. //
  354. STDMETHODIMP CConnectionPointEnum::Clone(IEnumConnections **ppecOut)
  355. {
  356.     return CConnectionPointEnum_Create(m_pcp, m_iPos, ppecOut);
  357. }