Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

406 lines
11 KiB

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