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.

471 lines
11 KiB

  1. // ITUnk.cpp -- Implementation for our base level IUnknown interface
  2. #include "stdafx.h"
  3. CITCriticalSection CITUnknown::s_csUnk;
  4. CITUnknown *CITUnknown::s_pitunkActive; // = NULL;
  5. void CITUnknown::CloseActiveObjects()
  6. {
  7. while (s_pitunkActive)
  8. {
  9. CITUnknown *pitNext = s_pitunkActive;
  10. for (; pitNext; pitNext = pitNext->m_pitunkNext)
  11. if (!(pitNext->IsSecondary()))
  12. break;
  13. RonM_ASSERT(pitNext);
  14. if (!pitNext) break;
  15. while (pitNext->Release() > 0);
  16. }
  17. }
  18. #ifdef _DEBUG
  19. BOOL CITUnknown::s_fCSInitialed; // = FALSE;
  20. void CITUnknown::OpenReferee(void)
  21. {
  22. // RonM_ASSERT(!s_fCSInitialed);
  23. if (s_fCSInitialed) // We can be called repeatedly because LoadLibrary
  24. return; // causes a Process_Attach!
  25. s_fCSInitialed= TRUE;
  26. }
  27. void CITUnknown::CloseReferee(void)
  28. {
  29. RonM_ASSERT(s_fCSInitialed); // BugBug: Can UnloadLibrary cause a Process_Detach?
  30. s_fCSInitialed = FALSE;
  31. }
  32. #endif // _DEBUG
  33. CITUnknown::CITUnknown(const IID *paIID, UINT cIID, IUnknown **papIFace)
  34. {
  35. m_paIID = paIID;
  36. m_cIID = cIID;
  37. m_papIFace = papIFace;
  38. m_pIFace = NULL;
  39. CommonInitialing();
  40. }
  41. CITUnknown::CITUnknown(const IID *paIID, UINT cIID, IUnknown *pIFace)
  42. {
  43. m_paIID = paIID;
  44. m_cIID = cIID;
  45. m_pIFace = pIFace;
  46. m_papIFace = NULL;
  47. CommonInitialing();
  48. }
  49. #if 0
  50. CITUnknown::CITUnknown()
  51. {
  52. m_paIID = NULL;
  53. m_cIID = 0;
  54. m_pIFace = NULL;
  55. m_papIFace = NULL;
  56. CommonInitialing();
  57. }
  58. #endif // 0
  59. void CITUnknown::Uncouple()
  60. {
  61. CSyncWith sw(s_csUnk);
  62. #ifdef _DEBUG
  63. BOOL fFoundThis = FALSE;
  64. CITUnknown *pITUnkNext = s_pitunkActive;
  65. for (;pITUnkNext; pITUnkNext = pITUnkNext->m_pitunkNext)
  66. {
  67. if (pITUnkNext == this)
  68. {
  69. RonM_ASSERT(!fFoundThis);
  70. fFoundThis = TRUE;
  71. }
  72. }
  73. RonM_ASSERT(fFoundThis);
  74. #endif // _DEBUG
  75. if (m_pitunkPrev)
  76. m_pitunkPrev->m_pitunkNext = m_pitunkNext;
  77. else s_pitunkActive = m_pitunkNext;
  78. if (m_pitunkNext)
  79. m_pitunkNext->m_pitunkPrev = m_pitunkPrev;
  80. }
  81. void CITUnknown::MoveInFrontOf(CITUnknown *pITUnk)
  82. {
  83. if (!pITUnk)
  84. pITUnk = s_pitunkActive;
  85. CSyncWith sw(s_csUnk);
  86. RonM_ASSERT(pITUnk);
  87. RonM_ASSERT(pITUnk != this);
  88. #ifdef _DEBUG
  89. BOOL fFoundThis = FALSE;
  90. BOOL fFoundThat = FALSE;
  91. CITUnknown *pITUnkNext = s_pitunkActive;
  92. for (;pITUnkNext; pITUnkNext = pITUnkNext->m_pitunkNext)
  93. {
  94. if (pITUnkNext == this)
  95. {
  96. RonM_ASSERT(!fFoundThis);
  97. fFoundThis = TRUE;
  98. }
  99. if (pITUnkNext == pITUnk)
  100. {
  101. RonM_ASSERT(!fFoundThat);
  102. fFoundThat = TRUE;
  103. }
  104. }
  105. RonM_ASSERT(fFoundThis && fFoundThat);
  106. #endif // _DEBUG
  107. Uncouple();
  108. m_pitunkNext = pITUnk;
  109. m_pitunkPrev = pITUnk->m_pitunkPrev;
  110. pITUnk->m_pitunkPrev = this;
  111. if (m_pitunkPrev)
  112. m_pitunkPrev->m_pitunkNext = this;
  113. else s_pitunkActive = this;
  114. }
  115. void CITUnknown::CommonInitialing()
  116. {
  117. m_cRefs = 0;
  118. m_fSecondary = FALSE;
  119. m_fAlreadyDead = FALSE;
  120. pDLLServerState->ObjectAdded();
  121. // #ifdef _DEBUG
  122. {
  123. CSyncWith sw(s_csUnk);
  124. m_pitunkPrev = NULL;
  125. m_pitunkNext = s_pitunkActive;
  126. if (m_pitunkNext)
  127. m_pitunkNext->m_pitunkPrev = this;
  128. s_pitunkActive = this;
  129. }
  130. // #endif // _DEBUG
  131. #if 0 // #ifdef _DEBUG
  132. // The loop below inserts this object at the head of the s_pitunkActive list.
  133. // This works correctly when multiple threads are simultaneously altering
  134. // the list.
  135. for (;;)
  136. {
  137. // Note! InterlockedCompareExchange is an NT-only API.
  138. if (PVOID(m_pitunkNext) == InterlockedCompareExchange
  139. ((PVOID *)&s_pitunkActive, PVOID(this),
  140. PVOID(m_pitunkNext)
  141. )
  142. )
  143. break;
  144. }
  145. #endif // _DEBUG
  146. }
  147. STDMETHODIMP_(ULONG) CITUnknown::AddRef (void)
  148. {
  149. return InterlockedIncrement(&m_cRefs);
  150. }
  151. STDMETHODIMP_(ULONG) CITUnknown::Release(void)
  152. {
  153. /*
  154. Each COM object is reference counted and their destructor is
  155. invoked when the reference count goes from 1 to zero.
  156. There are several often forgotten assumptions which go along
  157. with reference counting. For example each increment of the count
  158. is supposed to correspond to a pointer variable somewhere
  159. which points to the object. So whenever you call Release,
  160. you're also promising that the corresponding pointer variable
  161. no longer points to the released object.
  162. If the calling code isn't disciplined about nulling out pointers
  163. when they're released, we can get subsequent calls to release an
  164. object that's already dead. One way to avoid that problem is to
  165. use the DetachRef define (See ITUnk.h) to disconnect pointers
  166. for objects derived from PCImpITUnknown.
  167. However that still leaves the possibility of concurrent threads
  168. simultaneously racing to detach the same pointer variable. In
  169. practice the way to deal with that problem is to put the call
  170. to Release in one place in the calling code and to protect it
  171. with a critical section.
  172. The other design issue with reference counts is circular references.
  173. For example if you create object A with one external reference and
  174. its constructor creates object B in turn which refers to A, we have
  175. two references -- one reference to A from the outside environment
  176. and one internal circular reference from A to B to A.
  177. If you let a circular reference come into being and take no further
  178. action, your object can never be destroyed. The way that we solve
  179. this problem in the ITSS code is to have the containing object ("A"
  180. in the example above) follow this discipline:
  181. 1. In its contructor after the circular references have been
  182. created, it must first call AddRef to account for the external
  183. reference and then call Release once for each circular reference.
  184. 2. In its destructor it must first call AddRef for each circular
  185. reference.
  186. The code below makes that discipline work and also detects undisciplined
  187. situations. In particular it enforces the rule that an object is destroyed
  188. only once no matter how many times its reference count goes from one to
  189. zero.
  190. */
  191. RonM_ASSERT(m_cRefs > 0);
  192. LONG cRefs= InterlockedDecrement(&m_cRefs);
  193. if (!cRefs)
  194. {
  195. RonM_ASSERT(!m_fAlreadyDead);
  196. if (!m_fAlreadyDead) // No double jeopardy for zombies!
  197. {
  198. m_fAlreadyDead = TRUE; // To keep objects from dying more than once.
  199. delete this;
  200. }
  201. }
  202. return cRefs;
  203. }
  204. STDMETHODIMP CITUnknown::QueryInterface(REFIID riid, PPVOID ppv)
  205. {
  206. IUnknown *pUnkResult = NULL;
  207. if (riid == IID_IUnknown) pUnkResult = this;
  208. else
  209. {
  210. RonM_ASSERT(!m_pIFace || !m_papIFace); // Can't use both...
  211. RonM_ASSERT( m_pIFace || m_papIFace); // Must have at least one.
  212. IUnknown **ppUnk = m_papIFace;
  213. const IID *pIID = m_paIID;
  214. for (UINT c = m_cIID; c--; pIID++)
  215. {
  216. IUnknown *pUnk = ppUnk? *ppUnk++ : m_pIFace;
  217. if (riid == *pIID)
  218. {
  219. pUnkResult = pUnk;
  220. break;
  221. }
  222. }
  223. if (!pUnkResult)
  224. return E_NOINTERFACE;
  225. }
  226. pUnkResult->AddRef();
  227. *ppv = pUnkResult;
  228. return NOERROR;
  229. }
  230. CITUnknown::~CITUnknown()
  231. {
  232. pDLLServerState->ObjectReleased();
  233. // #ifdef _DEBUG
  234. // RonM_ASSERT(s_fCSInitialed);
  235. Uncouple();
  236. // #endif // _DEBUG
  237. #if 0 // #ifdef _DEBUG
  238. // The loop below removes this object from the s_pitunkActive list.
  239. // This works correctly when multiple threads are simultaneously altering
  240. // the list.
  241. for (;;)
  242. {
  243. // We always make at least two passes through the list.
  244. // First we scan to locate our object and remove it from the list.
  245. // Then we scan the list again to verify that our object has been
  246. // removed. The verification scan is necessary given that other
  247. // threads may be modifying the list at the same time.
  248. BOOL fFound = FALSE;
  249. for (CITUnknown **ppitunk= &s_pitunkActive; *ppitunk;
  250. ppitunk= &((*ppitunk)->m_pitunkNext)
  251. )
  252. {
  253. if (this != *ppitunk) continue;
  254. fFound= TRUE;
  255. // Note! InterlockedCompareExchange is an NT-only API.
  256. InterlockedCompareExchange((PVOID *) ppitunk, PVOID(m_pitunkNext), PVOID(this));
  257. break;
  258. }
  259. if (!fFound) break;
  260. }
  261. #endif // _DEBUG
  262. }
  263. HRESULT CITUnknown::FinishSetup(HRESULT hr, CITUnknown *pUnk, REFIID riid, PPVOID ppv)
  264. {
  265. if (SUCCEEDED(hr))
  266. {
  267. pUnk->AddRef();
  268. CImpITUnknown *pImp;
  269. hr = pUnk->QueryInterface(riid, (PPVOID) &pImp);
  270. if (SUCCEEDED(hr))
  271. {
  272. if (riid != IID_IUnknown && pImp->HasControllingUnknown())
  273. {
  274. hr = CLASS_E_NOAGGREGATION;
  275. pImp->Release(); pImp = NULL;
  276. }
  277. }
  278. *ppv = (PVOID) pImp;
  279. pUnk->Release();
  280. }
  281. else
  282. if (pUnk) delete pUnk;
  283. return hr;
  284. }
  285. CImpITUnknown::CImpITUnknown(CITUnknown *pBackObj, IUnknown *punkOuter)
  286. {
  287. m_fControlled = punkOuter != NULL;
  288. m_pUnkOuter = m_fControlled? punkOuter : pBackObj;
  289. m_pBackObj = pBackObj;
  290. m_fActive = FALSE;
  291. m_pImpITUnknownNext = NULL;
  292. m_ppImpITUnknownList = NULL;
  293. }
  294. CImpITUnknown::~CImpITUnknown()
  295. {
  296. if (m_fActive)
  297. MarkInactive();
  298. }
  299. STDMETHODIMP_(ULONG) CImpITUnknown::AddRef (void)
  300. {
  301. return m_pUnkOuter->AddRef();
  302. }
  303. void CImpITUnknown::DetachReference(PCImpITUnknown &pITUnk)
  304. {
  305. PCImpITUnknown pITUnkTmp = pITUnk;
  306. pITUnk = NULL;
  307. pITUnkTmp->Release();
  308. }
  309. STDMETHODIMP_(ULONG) CImpITUnknown::Release(void)
  310. {
  311. return m_pUnkOuter->Release();
  312. }
  313. STDMETHODIMP CImpITUnknown::QueryInterface(REFIID riid, PPVOID ppv)
  314. {
  315. return m_pUnkOuter->QueryInterface(riid, ppv);
  316. }
  317. void CImpITUnknown::MarkActive(PCImpITUnknown &pListStart)
  318. {
  319. RonM_ASSERT(!m_fActive);
  320. RonM_ASSERT(!m_ppImpITUnknownList);
  321. m_ppImpITUnknownList = &pListStart;
  322. m_pImpITUnknownNext = pListStart;
  323. pListStart = this;
  324. m_fActive = TRUE;
  325. }
  326. void CImpITUnknown::MarkInactive()
  327. {
  328. RonM_ASSERT(m_fActive);
  329. RonM_ASSERT(m_ppImpITUnknownList);
  330. PCImpITUnknown *ppImpITUnk = m_ppImpITUnknownList;
  331. RonM_ASSERT(*ppImpITUnk);
  332. for (;;)
  333. {
  334. CImpITUnknown *pImpITUnk = *ppImpITUnk;
  335. if (pImpITUnk == this)
  336. {
  337. *ppImpITUnk = m_pImpITUnknownNext;
  338. m_ppImpITUnknownList = NULL;
  339. m_fActive = FALSE;
  340. return;
  341. }
  342. RonM_ASSERT(pImpITUnk->m_pImpITUnknownNext);
  343. ppImpITUnk = &(pImpITUnk->m_pImpITUnknownNext);
  344. }
  345. RonM_ASSERT(FALSE); // Should have found this object in the chain.
  346. }