Source code of Windows XP (NT5)
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.

460 lines
12 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_IConnectionPointContainer, (void **)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, (LPVOID *)&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_IDispatch, (LPVOID *)&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, (LPVOID*)&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_IConnectionPointCB, (LPVOID*)&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_IConnectionPointCB, (LPVOID*)&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. #if 1
  260. return CConnectionPointEnum_Create(this, 0, ppEnumOut);
  261. #else
  262. CONNECTDATA *rgConnectData = NULL;
  263. int i, cSinks;
  264. // CopyAndAddRefObject assumes that the IUnknown comes first
  265. // So does CStandardEnum
  266. COMPILETIME_ASSERT(FIELD_OFFSET(CONNECTDATA, pUnk) == 0);
  267. cSinks = 0;
  268. if (_HasSinks())
  269. {
  270. // allocate some memory big enough to hold all of the sinks.
  271. //
  272. // Must use GlobalAlloc because CStandardEnum uses GlobalFree.
  273. //
  274. rgConnectData = (CONNECTDATA *)GlobalAlloc(GMEM_FIXED, m_cSinks * sizeof(CONNECTDATA));
  275. if (!rgConnectData)
  276. return E_OUTOFMEMORY;
  277. // fill in the array
  278. //
  279. for (i = 0; i < m_cSinksAlloc; i++)
  280. {
  281. if (m_rgSinks[i])
  282. {
  283. rgConnectData[cSinks].pUnk = m_rgSinks[i];
  284. rgConnectData[cSinks].dwCookie = i + 1;
  285. cSinks++;
  286. // In case m_rgSinks gets out of sync with m_cSinks,
  287. // just stop when the array gets full.
  288. if (cSinks >= m_cSinks)
  289. {
  290. break;
  291. }
  292. }
  293. }
  294. // Make sure we found all the items we should've found
  295. ASSERT(cSinks == m_cSinks);
  296. }
  297. // create a statndard enumerator object.
  298. //
  299. *ppEnumOut = (IEnumConnections *)(IEnumGeneric *)new CStandardEnum(IID_IEnumConnections,
  300. TRUE, cSinks, sizeof(CONNECTDATA), rgConnectData, CopyAndAddRefObject);
  301. if (!*ppEnumOut)
  302. {
  303. LocalFree(rgConnectData);
  304. return E_OUTOFMEMORY;
  305. }
  306. return S_OK;
  307. #endif
  308. }
  309. //
  310. // CConnectionPoint::DoInvokeIE4
  311. //
  312. // Calls all sinks' IDispatch::Invoke() with Cancel semantics.
  313. HRESULT CConnectionPoint::DoInvokeIE4(LPBOOL pf, LPVOID *ppv, DISPID dispid, DISPPARAMS *pdispparams)
  314. {
  315. return IConnectionPoint_InvokeWithCancel(this->CastToIConnectionPoint(),
  316. dispid, pdispparams, pf, ppv);
  317. }
  318. //
  319. // CConnectionPointEnum
  320. //
  321. HRESULT CConnectionPointEnum_Create(CConnectionPoint *pcp, int iPos, IEnumConnections **ppecOut)
  322. {
  323. *ppecOut = new CConnectionPointEnum(pcp, iPos);
  324. return *ppecOut ? S_OK : E_OUTOFMEMORY;
  325. }
  326. STDMETHODIMP CConnectionPointEnum::QueryInterface(REFIID riid, void **ppvObjOut)
  327. {
  328. if (IsEqualIID(riid, IID_IEnumConnections) ||
  329. IsEqualIID(riid, IID_IUnknown))
  330. {
  331. *ppvObjOut = (IUnknown *)this;
  332. AddRef();
  333. return S_OK;
  334. }
  335. *ppvObjOut = NULL;
  336. return E_NOINTERFACE;
  337. }
  338. STDMETHODIMP_(ULONG) CConnectionPointEnum::AddRef()
  339. {
  340. return ++m_cRef;
  341. }
  342. STDMETHODIMP_(ULONG) CConnectionPointEnum::Release()
  343. {
  344. ULONG cRef = --m_cRef;
  345. if (cRef == 0)
  346. delete this;
  347. return cRef;
  348. }
  349. //
  350. // Next also doubles as Skip. If you pass a NULL output buffer, then
  351. // nothing gets copied (i.e., you're a Skip).
  352. //
  353. STDMETHODIMP CConnectionPointEnum::Next(ULONG ccd, LPCONNECTDATA rgcd, ULONG *pcdFetched)
  354. {
  355. ULONG ccdFetched = 0;
  356. while (ccdFetched < ccd)
  357. {
  358. //
  359. // Look for the next sink or the end of the array
  360. //
  361. while (m_iPos < m_pcp->m_cSinksAlloc && m_pcp->m_rgSinks[m_iPos] == NULL)
  362. {
  363. m_iPos++;
  364. }
  365. if (m_iPos >= m_pcp->m_cSinksAlloc)
  366. break;
  367. if (rgcd)
  368. {
  369. //
  370. // Copy it to the output buffer
  371. //
  372. rgcd->pUnk = m_pcp->m_rgSinks[m_iPos];
  373. rgcd->dwCookie = COOKIEFROMINDEX(m_iPos);
  374. rgcd->pUnk->AddRef();
  375. rgcd++;
  376. }
  377. m_iPos++;
  378. ccdFetched++;
  379. }
  380. if (pcdFetched)
  381. *pcdFetched = ccdFetched;
  382. return (ccdFetched < ccd) ? S_FALSE : S_OK;
  383. }
  384. //
  385. // Our clone enumerates the same CConnectionPoint from the same position.
  386. //
  387. STDMETHODIMP CConnectionPointEnum::Clone(IEnumConnections **ppecOut)
  388. {
  389. return CConnectionPointEnum_Create(m_pcp, m_iPos, ppecOut);
  390. }