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.

309 lines
10 KiB

  1. /*****************************************************************************\
  2. FILE: objcache.cpp
  3. DESCRIPTION:
  4. This is a lightweight API that will cache an object so the class factory
  5. will return the same object each time for every call in this process. If the
  6. caller is on another thread, they will get a marshalled stub to the real
  7. McCoy.
  8. To Add an Object:
  9. 1. classfactory.cpp calls CachedObjClassFactoryCreateInstance(). Add your
  10. object's CLSID to that if statement for that call.
  11. 2. Copy the section in CachedObjClassFactoryCreateInstance() that looks
  12. for a CLSID and calls the correct xxx_CreateInstance() method.
  13. 3. Your object's IUnknown::Release() needs to call CachedObjCheckRelease()
  14. at the top of your Release() method. It may reduce your m_cRef to 1 so
  15. it will go to zero after ::Release() decrements it. The object cache
  16. will hold two references to the object. CachedObjCheckRelease() will check
  17. if the last caller (3rd ref) is releasing, and then it will give up it's 2
  18. refs and clean up it's internal state. The Release() then decrements
  19. the callers ref and it's released because it hit zero.
  20. BryanSt 12/9/1999
  21. Copyright (C) Microsoft Corp 1999-1999. All rights reserved.
  22. \*****************************************************************************/
  23. #include "priv.h"
  24. #include "objcache.h"
  25. //////////////////////////////////////////////
  26. // Object Caching API
  27. //////////////////////////////////////////////
  28. HDPA g_hdpaObjects = NULL;
  29. CRITICAL_SECTION g_hCachedObjectSection;
  30. typedef struct
  31. {
  32. CLSID clsid;
  33. IStream * pStream;
  34. IUnknown * punk;
  35. DWORD dwThreadID;
  36. } CACHEDOBJECTS;
  37. STDAPI _GetObjectCacheArray(void)
  38. {
  39. HRESULT hr = S_OK;
  40. if (!g_hdpaObjects)
  41. {
  42. g_hdpaObjects = DPA_Create(1);
  43. if (!g_hdpaObjects)
  44. {
  45. hr = E_OUTOFMEMORY;
  46. }
  47. }
  48. return hr;
  49. }
  50. // This is the number of references that are used by the cache list
  51. // (One for punk & one for pStream). If we hit this number,
  52. // then there aren't any outstanding refs.
  53. #define REF_RELEASE_POINT 3
  54. int CALLBACK HDPAFindCLSID(LPVOID p1, LPVOID p2, LPARAM lParam)
  55. {
  56. CLSID * pClsidToFind = (CLSID *)p1;
  57. CACHEDOBJECTS * pCachedObjects = (CACHEDOBJECTS *)p2;
  58. int nReturn = 0;
  59. // Are they of different types?
  60. if (pCachedObjects->clsid.Data1 < pClsidToFind->Data1) nReturn = -1;
  61. else if (pCachedObjects->clsid.Data1 > pClsidToFind->Data1) nReturn = 1;
  62. else if (pCachedObjects->clsid.Data2 < pClsidToFind->Data2) nReturn = -1;
  63. else if (pCachedObjects->clsid.Data2 > pClsidToFind->Data2) nReturn = 1;
  64. else if (pCachedObjects->clsid.Data3 < pClsidToFind->Data3) nReturn = -1;
  65. else if (pCachedObjects->clsid.Data3 > pClsidToFind->Data3) nReturn = 1;
  66. else if (*(ULONGLONG *)&pCachedObjects->clsid.Data4 < *(ULONGLONG *)&pClsidToFind->Data4) nReturn = -1;
  67. else if (*(ULONGLONG *)&pCachedObjects->clsid.Data4 > *(ULONGLONG *)&pClsidToFind->Data4) nReturn = 1;
  68. return nReturn;
  69. }
  70. STDAPI ObjectCache_GetObject(CLSID clsid, REFIID riid, void ** ppvObj)
  71. {
  72. HRESULT hr = S_OK;
  73. hr = _GetObjectCacheArray();
  74. if (SUCCEEDED(hr))
  75. {
  76. hr = E_FAIL;
  77. int nIndex = DPA_Search(g_hdpaObjects, &clsid, 0, HDPAFindCLSID, NULL, DPAS_SORTED);
  78. if (0 <= nIndex)
  79. {
  80. CACHEDOBJECTS * pCurrentObject = (CACHEDOBJECTS *) DPA_GetPtr(g_hdpaObjects, nIndex);
  81. if (pCurrentObject)
  82. {
  83. if (GetCurrentThreadId() == pCurrentObject->dwThreadID)
  84. {
  85. // No Marshalling needed.
  86. hr = pCurrentObject->punk->QueryInterface(riid, ppvObj);
  87. }
  88. else
  89. {
  90. // We do need to marshal it. So read it out of the stream.
  91. // But first we want to store our place in the stream so
  92. // we can rewrind for the next schmooo.
  93. LARGE_INTEGER liZero;
  94. ULARGE_INTEGER uli;
  95. liZero.QuadPart = 0;
  96. hr = pCurrentObject->pStream->Seek(liZero, STREAM_SEEK_CUR, &uli);
  97. if (SUCCEEDED(hr))
  98. {
  99. LARGE_INTEGER li;
  100. li.QuadPart = uli.QuadPart;
  101. hr = CoUnmarshalInterface(pCurrentObject->pStream, riid, ppvObj);
  102. if (SUCCEEDED(hr))
  103. {
  104. pCurrentObject->pStream->Seek(li, STREAM_SEEK_SET, NULL);
  105. }
  106. }
  107. }
  108. }
  109. }
  110. }
  111. return hr;
  112. }
  113. // WARNING: DllGetClassObject/CoGetClassObject
  114. // may be much better to use than rolling our own thread safe
  115. // code.
  116. STDAPI ObjectCache_SetObject(CLSID clsid, REFIID riid, IUnknown * punk)
  117. {
  118. HRESULT hr = _GetObjectCacheArray();
  119. if (SUCCEEDED(hr))
  120. {
  121. hr = E_FAIL;
  122. int nIndex = DPA_Search(g_hdpaObjects, &clsid, 0, HDPAFindCLSID, NULL, DPAS_SORTED);
  123. // If it's not in the list.
  124. if (0 > nIndex)
  125. {
  126. CACHEDOBJECTS * pcoCurrentObject = (CACHEDOBJECTS *) LocalAlloc(LPTR, sizeof(CACHEDOBJECTS));
  127. if (pcoCurrentObject)
  128. {
  129. pcoCurrentObject->dwThreadID = GetCurrentThreadId();
  130. pcoCurrentObject->clsid = clsid;
  131. punk->AddRef(); // Ref now equals 2 (The structure will own this ref)
  132. IStream * pStream = SHCreateMemStream(NULL, 0);
  133. if (pStream)
  134. {
  135. hr = CoMarshalInterface(pStream, riid, punk, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL);
  136. if (SUCCEEDED(hr)) // Ref now equals 3
  137. {
  138. LARGE_INTEGER liZero;
  139. // Reset the Stream to the beginning.
  140. liZero.QuadPart = 0;
  141. hr = pStream->Seek(liZero, STREAM_SEEK_SET, NULL);
  142. if (SUCCEEDED(hr))
  143. {
  144. pcoCurrentObject->punk = punk;
  145. pcoCurrentObject->pStream = pStream;
  146. if (-1 == DPA_SortedInsertPtr(g_hdpaObjects, &clsid, 0, HDPAFindCLSID, NULL, (DPAS_SORTED | DPAS_INSERTBEFORE), pcoCurrentObject))
  147. {
  148. // It failed.
  149. hr = E_OUTOFMEMORY;
  150. }
  151. }
  152. }
  153. }
  154. else
  155. {
  156. hr = E_OUTOFMEMORY;
  157. }
  158. if (FAILED(hr))
  159. {
  160. LocalFree(pcoCurrentObject);
  161. punk->Release();
  162. ATOMICRELEASE(pStream);
  163. }
  164. }
  165. else
  166. {
  167. hr = E_OUTOFMEMORY;
  168. }
  169. }
  170. }
  171. return hr;
  172. }
  173. STDAPI CachedObjClassFactoryCreateInstance(CLSID clsid, REFIID riid, void ** ppvObj)
  174. {
  175. HRESULT hr;
  176. EnterCriticalSection(&g_hCachedObjectSection);
  177. hr = ObjectCache_GetObject(clsid, riid, ppvObj);
  178. if (FAILED(hr))
  179. {
  180. /*
  181. if (IsEqualCLSID(clsid, CLSID_MailApp))
  182. {
  183. hr = CMailAppOM_CreateInstance(NULL, riid, ppvObj);
  184. }
  185. */
  186. if (SUCCEEDED(hr))
  187. {
  188. // ObjectCache_SetObject will fail in some
  189. // multi-threaded cases.
  190. hr = ObjectCache_SetObject(clsid, riid, (IUnknown *) *ppvObj);
  191. }
  192. }
  193. LeaveCriticalSection(&g_hCachedObjectSection);
  194. return hr;
  195. }
  196. int ObjectCache_DestroyCB(LPVOID pv, LPVOID pvData)
  197. {
  198. CACHEDOBJECTS * pCachedObjects = (CACHEDOBJECTS *)pv;
  199. AssertMsg((NULL != pCachedObjects), "Why would this be NULL?");
  200. if (pCachedObjects)
  201. {
  202. SAFERELEASE(pCachedObjects->punk);
  203. SAFERELEASE(pCachedObjects->pStream);
  204. LocalFree(pCachedObjects);
  205. }
  206. return TRUE;
  207. }
  208. STDAPI CachedObjCheckRelease(CLSID clsid, int * pnRef)
  209. {
  210. HRESULT hr = E_INVALIDARG;
  211. if (pnRef)
  212. {
  213. hr = S_OK;
  214. if (REF_RELEASE_POINT == *pnRef)
  215. {
  216. EnterCriticalSection(&g_hCachedObjectSection);
  217. if (REF_RELEASE_POINT == *pnRef)
  218. {
  219. hr = _GetObjectCacheArray();
  220. if (SUCCEEDED(hr))
  221. {
  222. hr = E_FAIL;
  223. int nIndex = DPA_Search(g_hdpaObjects, &clsid, 0, HDPAFindCLSID, NULL, DPAS_SORTED);
  224. if (0 <= nIndex)
  225. {
  226. CACHEDOBJECTS * pCurrentObject = (CACHEDOBJECTS *) DPA_GetPtr(g_hdpaObjects, nIndex);
  227. if (pCurrentObject)
  228. {
  229. // We need to delete the pointer from the array before
  230. // we release the object or it will recurse infinitely.
  231. // The problem is that when ObjectCache_DestroyCB() releases
  232. // the object, the Release() function will call CachedObjCheckRelease().
  233. // And since the ref hasn't change -yet-, we need the
  234. // search to fail to stop the recursion.
  235. DPA_DeletePtr(g_hdpaObjects, nIndex);
  236. ObjectCache_DestroyCB(pCurrentObject, NULL);
  237. }
  238. }
  239. }
  240. }
  241. LeaveCriticalSection(&g_hCachedObjectSection);
  242. }
  243. }
  244. return hr;
  245. }
  246. STDAPI PurgeObjectCache(void)
  247. {
  248. HRESULT hr = S_OK;
  249. EnterCriticalSection(&g_hCachedObjectSection);
  250. if (g_hdpaObjects)
  251. {
  252. DPA_DestroyCallback(g_hdpaObjects, ObjectCache_DestroyCB, NULL);
  253. g_hdpaObjects = NULL;
  254. }
  255. LeaveCriticalSection(&g_hCachedObjectSection);
  256. return hr;
  257. }