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.

1963 lines
64 KiB

  1. //
  2. // Loader.cpp : Implementation of CLoader
  3. //
  4. // Copyright (c) 1997-2001 Microsoft Corporation
  5. //
  6. // @doc EXTERNAL
  7. //
  8. #include "dmusicc.h"
  9. #include "dmusici.h"
  10. #include "validate.h"
  11. #include "loader.h"
  12. #include "debug.h"
  13. #include "riff.h"
  14. #include "dmscriptautguids.h"
  15. #include "miscutil.h"
  16. #ifdef UNDER_CE
  17. #include "dragon.h"
  18. #else
  19. extern BOOL g_fIsUnicode;
  20. #endif
  21. extern long g_cComponent;
  22. /////////////////////////////////////////////////////////////////////////////
  23. // CLoader
  24. static HRESULT GetRegStringW( HKEY hKey, WCHAR* lpSubKey, WCHAR* lpValueName, WCHAR* lpwzString )
  25. {
  26. HKEY hKeyOpen;
  27. DWORD dwType;
  28. DWORD dwCbData;
  29. LONG lResult;
  30. lpwzString[0] = L'\0';
  31. lResult = RegOpenKeyExW( hKey, lpSubKey, 0, KEY_QUERY_VALUE, &hKeyOpen );
  32. if( lResult == ERROR_SUCCESS )
  33. {
  34. dwCbData = MAX_PATH * sizeof(WCHAR);
  35. lResult = RegQueryValueExW( hKeyOpen, lpValueName, NULL, &dwType, (LPBYTE)lpwzString, &dwCbData );
  36. if( lResult != ERROR_SUCCESS )
  37. {
  38. lpwzString[0] = L'\0';
  39. }
  40. else
  41. {
  42. #ifndef UNDER_CE
  43. if( dwType == REG_EXPAND_SZ )
  44. {
  45. WCHAR wzTemp[MAX_PATH];
  46. if( ExpandEnvironmentStringsW( lpwzString, wzTemp, MAX_PATH ) )
  47. {
  48. wcscpy(lpwzString, wzTemp);
  49. }
  50. else
  51. {
  52. lpwzString[0] = L'\0';
  53. }
  54. }
  55. else
  56. #endif
  57. if( dwType != REG_SZ )
  58. {
  59. lpwzString[0] = L'\0';
  60. }
  61. }
  62. RegCloseKey( hKeyOpen );
  63. }
  64. return lResult;
  65. }
  66. #ifndef UNDER_CE
  67. static HRESULT GetRegStringA( HKEY hKey, LPCSTR lpSubKey, LPSTR lpValueName, LPSTR lpszString )
  68. {
  69. HKEY hKeyOpen;
  70. DWORD dwType;
  71. DWORD dwCbData;
  72. LONG lResult;
  73. lpszString[0] = '\0';
  74. lResult = RegOpenKeyEx( hKey, lpSubKey, 0, KEY_QUERY_VALUE, &hKeyOpen );
  75. if( lResult == ERROR_SUCCESS )
  76. {
  77. dwCbData = MAX_PATH;
  78. lResult = RegQueryValueExA( hKeyOpen, lpValueName, NULL, &dwType, (LPBYTE)lpszString, &dwCbData );
  79. if( lResult != ERROR_SUCCESS )
  80. {
  81. lpszString[0] = '\0';
  82. }
  83. else
  84. {
  85. if( dwType == REG_EXPAND_SZ )
  86. {
  87. char szTemp[MAX_PATH];
  88. if( ExpandEnvironmentStringsA( lpszString, szTemp, MAX_PATH ) )
  89. {
  90. strcpy(lpszString, szTemp);
  91. }
  92. else
  93. {
  94. lpszString[0] = '\0';
  95. }
  96. }
  97. else if( dwType != REG_SZ )
  98. {
  99. lpszString[0] = '\0';
  100. }
  101. }
  102. RegCloseKey( hKeyOpen );
  103. }
  104. return lResult;
  105. }
  106. #endif
  107. HRESULT CLoader::Init()
  108. {
  109. #ifndef UNDER_CE
  110. char szGMFile[MAX_PATH];
  111. #endif
  112. WCHAR wzGMFile[MAX_PATH];
  113. // First, get the GM path from the registry, if it exists.
  114. HRESULT hr;
  115. #ifndef UNDER_CE
  116. if( g_fIsUnicode )
  117. #endif
  118. {
  119. hr = GetRegStringW( HKEY_LOCAL_MACHINE,
  120. L"Software\\Microsoft\\DirectMusic",
  121. L"GMFilePath",
  122. wzGMFile );
  123. }
  124. #ifndef UNDER_CE
  125. else
  126. {
  127. hr = GetRegStringA( HKEY_LOCAL_MACHINE,
  128. "Software\\Microsoft\\DirectMusic",
  129. "GMFilePath",
  130. szGMFile );
  131. mbstowcs(wzGMFile,szGMFile,MAX_PATH);
  132. }
  133. #endif
  134. if (hr == S_OK)
  135. {
  136. DMUS_OBJECTDESC DESC; // Descriptor to use to find it.
  137. memset( &DESC, 0, sizeof(DMUS_OBJECTDESC) );
  138. DESC.dwSize = sizeof (DMUS_OBJECTDESC);
  139. DESC.guidClass = CLSID_DirectMusicCollection;
  140. wcscpy(DESC.wszFileName,wzGMFile);
  141. DESC.guidObject = GUID_DefaultGMCollection;
  142. DESC.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME |
  143. DMUS_OBJ_FULLPATH | DMUS_OBJ_OBJECT;
  144. SetObject(&DESC);
  145. }
  146. // prepare root node for garbage collection
  147. assert(!m_pApplicationObject); // this would fail if Init were called twice, which it shouldn't be
  148. m_pApplicationObject = new CObject(NULL);
  149. if (!m_pApplicationObject)
  150. return E_OUTOFMEMORY;
  151. hr = m_pApplicationObject->GC_Collectable();
  152. if (FAILED(hr))
  153. {
  154. delete m_pApplicationObject;
  155. m_pApplicationObject = NULL;
  156. return hr;
  157. }
  158. m_pLoadedObjectContext = m_pApplicationObject;
  159. return S_OK;
  160. }
  161. CLoader::CLoader()
  162. : m_pLoadedObjectContext(NULL),
  163. m_pApplicationObject(NULL)
  164. {
  165. m_fCSInitialized = FALSE;
  166. InterlockedIncrement(&g_cComponent);
  167. InitializeCriticalSection(&m_CriticalSection);
  168. m_fCSInitialized = TRUE;
  169. m_fPathValid = FALSE;
  170. m_fKeepObjects = TRUE;
  171. m_cRef = 1;
  172. m_cPRef = 0;
  173. m_fIMA = FALSE;
  174. }
  175. CLoader::~CLoader()
  176. {
  177. if (m_fCSInitialized)
  178. {
  179. // If critical section never initialized, never got a chance
  180. // to put anything in this list
  181. //
  182. while (!m_ClassList.IsEmpty())
  183. {
  184. CClass *pClass = m_ClassList.RemoveHead();
  185. delete pClass;
  186. }
  187. while (!m_ReleasedObjectList.IsEmpty())
  188. {
  189. CObject *pObject = m_ReleasedObjectList.RemoveHead();
  190. delete pObject;
  191. }
  192. DeleteCriticalSection(&m_CriticalSection);
  193. }
  194. delete m_pApplicationObject;
  195. InterlockedDecrement(&g_cComponent);
  196. }
  197. // CLoader::QueryInterface
  198. //
  199. STDMETHODIMP
  200. CLoader::QueryInterface(const IID &iid,
  201. void **ppv)
  202. {
  203. *ppv = NULL;
  204. if (iid == IID_IUnknown || iid == IID_IDirectMusicLoader || iid == IID_IDirectMusicLoader8) {
  205. *ppv = static_cast<IDirectMusicLoader8*>(this);
  206. }
  207. else if(iid == IID_IDirectMusicLoader8P)
  208. {
  209. *ppv = static_cast<IDirectMusicLoader8P*>(this);
  210. }
  211. else if(iid == IID_IDirectMusicIMA)
  212. {
  213. *ppv = static_cast<IDirectMusicIMA*>(this);
  214. }
  215. if (*ppv == NULL)
  216. return E_NOINTERFACE;
  217. reinterpret_cast<IUnknown*>(this)->AddRef();
  218. return S_OK;
  219. }
  220. // CLoader::AddRef
  221. //
  222. STDMETHODIMP_(ULONG)
  223. CLoader::AddRef()
  224. {
  225. return InterlockedIncrement(&m_cRef);
  226. }
  227. ULONG CLoader::AddRefP()
  228. {
  229. return InterlockedIncrement(&m_cPRef);
  230. }
  231. // CLoader::Release
  232. //
  233. STDMETHODIMP_(ULONG)
  234. CLoader::Release()
  235. {
  236. if (!InterlockedDecrement(&m_cRef))
  237. {
  238. // Ref count of zero indicates that no objects are currently using the loader except for streams.
  239. // Streams support the GetLoader method and hence we can't delete the loader yet. These streams
  240. // hold private references (AddRefP/ReleaseP).
  241. // Since no objects other than those streams are currently being used, we'll clear our cache.
  242. // This will release any of the Loader's refs on the streams and (assuming nobody else is
  243. // holding the streams) bring the private ref count down to zero so we can delete ourself.
  244. InterlockedIncrement(&m_cRef); // Keep streams from deleting loader.
  245. ClearCacheInternal(GUID_DirectMusicAllTypes, true);
  246. CollectGarbage();
  247. if (!InterlockedDecrement(&m_cRef))
  248. {
  249. if (!m_cPRef)
  250. {
  251. delete this;
  252. return 0;
  253. }
  254. }
  255. }
  256. return m_cRef;
  257. }
  258. ULONG CLoader::ReleaseP()
  259. {
  260. if (!InterlockedDecrement(&m_cPRef))
  261. {
  262. if (!m_cRef)
  263. {
  264. delete this;
  265. return 0;
  266. }
  267. }
  268. return m_cPRef;
  269. }
  270. HRESULT CLoader::GetClass(CDescriptor *pDesc, CClass **ppClass, BOOL fCreate)
  271. /* Scan the class list and find the matching class.
  272. If the class can not be found AND fCreate is TRUE,
  273. create a new class.
  274. */
  275. {
  276. *ppClass = NULL;
  277. if ((pDesc->m_dwValidData & DMUS_OBJ_CLASS) == 0) // We must have a valid class id.
  278. {
  279. Trace(1, "The class id field is required and missing in the DMUS_OBJECTDESC.\n");
  280. return DMUS_E_LOADER_NOCLASSID;
  281. }
  282. CClass *pClass = m_ClassList.GetHead();
  283. for (;pClass != NULL;pClass = pClass->GetNext())
  284. {
  285. if (pClass->m_ClassDesc.m_guidClass == pDesc->m_guidClass)
  286. {
  287. *ppClass = pClass;
  288. break;
  289. }
  290. }
  291. if (*ppClass == NULL)
  292. {
  293. if (fCreate)
  294. {
  295. pClass = new CClass(this, pDesc);
  296. if (pClass)
  297. {
  298. m_ClassList.AddHead(pClass);
  299. *ppClass = pClass;
  300. }
  301. else
  302. {
  303. return E_OUTOFMEMORY;
  304. }
  305. }
  306. else
  307. {
  308. #ifdef DBG
  309. WCHAR *polestrClsid = NULL;
  310. if (S_OK != ProgIDFromCLSID(pDesc->m_guidClass, &polestrClsid))
  311. {
  312. StringFromCLSID(pDesc->m_guidClass, &polestrClsid);
  313. }
  314. if (polestrClsid)
  315. Trace(1, "There are no objects of type %S in the loader.\n", polestrClsid);
  316. CoTaskMemFree(polestrClsid);
  317. #endif
  318. return DMUS_E_LOADER_OBJECTNOTFOUND;
  319. }
  320. }
  321. return S_OK;
  322. }
  323. #ifdef DBG
  324. const int GC_Report_DebugLevel = 4;
  325. void GC_Report(CLoader *pThis)
  326. {
  327. struct LocalFunc
  328. {
  329. static void ReportObject(CObject *pObject, bool fReportGC, CObject *pApplicationObject)
  330. {
  331. if (!(pObject->m_dwScanBits & SCAN_GC) == !fReportGC)
  332. {
  333. DMUS_OBJECTDESC desc;
  334. ZeroMemory(&desc, sizeof(desc));
  335. pObject->m_ObjectDesc.Get(&desc);
  336. DebugTrace(GC_Report_DebugLevel, " *%08X %S [%S]\n", pObject, desc.wszName, desc.wszFileName);
  337. if (!(desc.dwValidData & DMUS_OBJ_LOADED))
  338. {
  339. DebugTrace(GC_Report_DebugLevel, " Not loaded.\n");
  340. }
  341. if (fReportGC)
  342. {
  343. // check if object is referenced by the app
  344. for (UINT i = 0; i < pApplicationObject->m_pvecReferences->size(); ++i)
  345. {
  346. if (pObject == (*pApplicationObject->m_pvecReferences)[i])
  347. {
  348. DebugTrace(GC_Report_DebugLevel, " In use by application.\n");
  349. }
  350. }
  351. // output the object's references
  352. assert(pObject->m_pvecReferences);
  353. for (i = 0; i < pObject->m_pvecReferences->size(); ++i)
  354. {
  355. CObject *pObjectRef = (*pObject->m_pvecReferences)[i];
  356. DMUS_OBJECTDESC descRef;
  357. ZeroMemory(&descRef, sizeof(descRef));
  358. pObjectRef->m_ObjectDesc.Get(&descRef);
  359. DebugTrace(GC_Report_DebugLevel, " -%08X %S (%S)\n", pObjectRef, descRef.wszName, descRef.wszFileName);
  360. }
  361. }
  362. }
  363. }
  364. };
  365. SmartRef::CritSec CS(&pThis->m_CriticalSection);
  366. DebugTrace(GC_Report_DebugLevel, "Cached non-GC contents of DirectMusic Loader:\n");
  367. // Do two passes. One to report non-GC items, one to report GC items.
  368. for (int fReportGC = 0; fReportGC < 2; ++fReportGC)
  369. {
  370. for (CClass *pClass = pThis->m_ClassList.GetHead(); pClass != NULL; pClass = pClass->GetNext())
  371. {
  372. for (CObject *pObject = pClass->m_ObjectList.GetHead(); pObject; pObject = pObject->GetNext())
  373. {
  374. LocalFunc::ReportObject(pObject, !!fReportGC, pThis->m_pApplicationObject);
  375. }
  376. }
  377. DebugTrace(GC_Report_DebugLevel, !fReportGC ? "Cached garbage-collected contents:\n" : "Contents released from the cache that aren't yet garbage or haven't been collected:\n");
  378. }
  379. for (CObject *pObject = pThis->m_ReleasedObjectList.GetHead(); pObject; pObject = pObject->GetNext())
  380. {
  381. assert(pObject->m_dwScanBits & SCAN_GC);
  382. LocalFunc::ReportObject(pObject, true, pThis->m_pApplicationObject);
  383. }
  384. DebugTrace(GC_Report_DebugLevel, "End of cache report.\n\n");
  385. }
  386. #endif
  387. /*
  388. @method:(EXTERNAL) HRESULT | IDirectMusicLoader | GetObject | Retrieves
  389. the specified object, potentially by loading it from a file.
  390. @rdesc Returns one of the following
  391. @flag S_OK | Success.
  392. @flag E_OUTOFMEMORY | Insufficient memory to create the object.
  393. @flag E_POINTER | Bad pointer.
  394. @flag E_INVALIDARG | The size of the passed <p pDESC> was too small.
  395. @flag E_NOINTERFACE | The requested object does not support the requested interface.
  396. @flag REGDB_E_CLASSNOTREG | Object class is not registered.
  397. @flag DMUS_E_LOADER_NOCLASSID | No class id in <t DMUS_OBJECTDESC>.
  398. @flag DMUS_E_LOADER_FAILEDOPEN | File open failed - either file doesn't exist or is locked.
  399. @flag DMUS_E_LOADER_FORMATNOTSUPPORTED | Search data type is not supported.
  400. @flag DMUS_E_LOADER_FAILEDCREATE | Unable to find or create object.
  401. For example, DMUS_OBJ_URL will return this error in the initial
  402. release of DirectMusic's loader.
  403. @comm This is the heart of the DirectMusicLoader system. Typically, you
  404. can use <om IDirectMusicLoader::GetObject> as a quick way to load
  405. objects from disk. To do so, create a <t DMUS_OBJECTDESC> structure and
  406. fill all appropriate fields. Usually, the file path will suffice,
  407. though you can also request an object by name or GUID.
  408. <om IDirectMusicLoader::GetObject> compares its internal
  409. database with the object described by <t DMUS_OBJECTDESC>. If it can
  410. find it, it loads the object and returns a pointer to the
  411. requested interface of the requested object (all
  412. DirectMusic compatible objects must implement an <i IDirectMusicObject>
  413. interface as well as an <i IPersistStream> interface for loading
  414. from a stream.)
  415. <om IDirectMusicLoader::GetObject> prioritizes its search as follows:
  416. 1. DMUS_OBJ_OBJECT,
  417. 2. DMUS_OBJ_FILENAME AND DMUS_OBJ_FULLPATH,
  418. 3. DMUS_OBJ_NAME AND DMUS_OBJ_CATEGORY,
  419. 4. DMUS_OBJ_NAME,
  420. 5. DMUS_OBJ_FILENAME
  421. In other words, the highest priority goes to a unique GUID, followed by
  422. the full file path name, followed by internal name plus category,
  423. followed by internal name, followed by local file name.
  424. @ex The following example uses <om IDirectMusicLoader::GetObject> to
  425. load a DirectMusic style from a file on disk: |
  426. void myLoadStyle(
  427. IDirectMusicStyle **ppIStyle) // Style that we wish to load.
  428. {
  429. IDirectMusicLoader *pILoader; // Loader interface.
  430. // Typically, you should create the loader once, and use it
  431. // for the duration of the application. This reduces overhead and
  432. // takes advantage of the loader's ablilty to cache objects.
  433. // However, for purposes of this example, we create it dynamically
  434. // and throw it away once the style is loaded.
  435. CoCreateInstance(
  436. CLSID_DirectMusicLoader,NULL,
  437. CLSCTX_INPROC_SERVER,
  438. IID_IDirectMusicLoader,
  439. (void **) &pILoader);
  440. if (pILoader)
  441. {
  442. DMUS_OBJECTDESC Desc; // Descriptor.
  443. // Start by initializing Desc with the file name and GUID
  444. // for style object.
  445. wcscpy(Desc.wszFileName,L"c:\\mymusic\\funky\\polka.sty");
  446. Desc.guidClass = CLSID_DirectMusicStyle; // Style class.
  447. Desc.dwSize = sizeof (DMUS_OBJECTDESC);
  448. Desc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH;
  449. pILoader->GetObject(&Desc, IID_IDirectMusicStyle, (void**) ppIStyle);
  450. pILoader->Release();
  451. }
  452. }
  453. // At this point, the style is loaded is returned in ppIStyle.
  454. @xref <i IDirectMusicLoader>, <i IDirectMusicObject>, <t DMUS_OBJECTDESC>
  455. */
  456. STDMETHODIMP CLoader::LoadObjectFromFile(REFGUID rguidClassID,
  457. REFIID iidInterfaceID,
  458. WCHAR *pwzFilePath,
  459. void ** ppObject)
  460. {
  461. V_INAME(IDirectMusicLoader8::LoadObjectFromFile);
  462. V_BUFPTR_READ(pwzFilePath,2);
  463. HRESULT hr;
  464. DMUS_OBJECTDESC DESC;
  465. DESC.dwSize = sizeof (DESC);
  466. DESC.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH;
  467. wcscpy(DESC.wszFileName,pwzFilePath);
  468. DESC.guidClass = rguidClassID;
  469. hr = GetObject(&DESC, iidInterfaceID, ppObject);
  470. if (FAILED(hr))
  471. {
  472. DESC.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME;
  473. hr = GetObject(&DESC, iidInterfaceID, ppObject);
  474. }
  475. return hr;
  476. }
  477. STDMETHODIMP CLoader::GetObject(
  478. LPDMUS_OBJECTDESC pDESC, // @parm Description of the requested object in <t DMUS_OBJECTDESC> structure.
  479. REFIID riid, //@parm The interface to return in <p ppv>
  480. LPVOID FAR *ppv) // @parm Receives the interface on success.
  481. {
  482. HRESULT hr = S_OK;
  483. HRESULT hrLoad = S_OK;
  484. CDescriptor Desc;
  485. V_INAME(IDirectMusicLoader::GetObject);
  486. // V_STRUCTPTR_READ(pDESC,DMUS_OLDOBJECTDESC);
  487. V_PTRPTR_WRITE(ppv);
  488. IStream *pStream = pDESC->dwValidData & DMUS_OBJ_STREAM ? pDESC->pStream : NULL;
  489. if (pStream)
  490. {
  491. V_INTERFACE(pStream);
  492. }
  493. // if pDESC has DMUS_OBJ_FULLPATH set, set DMUS_OBJ_FILENAME as well.
  494. if( pDESC->dwValidData & DMUS_OBJ_FULLPATH )
  495. {
  496. pDESC->dwValidData |= DMUS_OBJ_FILENAME;
  497. }
  498. IDirectMusicObject* pDMObj;
  499. EnterCriticalSection(&m_CriticalSection);
  500. CClass *pClass;
  501. CObject *pObject = NULL;
  502. if (pStream)
  503. {
  504. // The loader will save a cloned stream so that it doesn't interfere with
  505. // the application reading from the stream.
  506. // Don't worry -- then we'll restore the original stream pointer.
  507. hr = pStream->Clone(&pDESC->pStream);
  508. if(FAILED(hr))
  509. {
  510. return E_OUTOFMEMORY;
  511. }
  512. }
  513. Desc.Set(pDESC);
  514. if (pStream)
  515. {
  516. // Restore the stream in the descriptor we were passed.
  517. pDESC->pStream->Release(); // release matching call to Clone (ref is now held in descriptor)
  518. pDESC->pStream = pStream;
  519. }
  520. hr = GetClass(&Desc,&pClass, TRUE);
  521. if (SUCCEEDED(hr))
  522. {
  523. hr = pClass->GetObject(&Desc,&pObject);
  524. if (SUCCEEDED(hr))
  525. {
  526. bool fKeep = !!pClass->m_fKeepObjects;
  527. bool fGC = fKeep && m_pLoadedObjectContext && m_pLoadedObjectContext->m_dwScanBits & SCAN_GC;
  528. if (pObject->m_pIDMObject) // Already loaded!
  529. {
  530. pObject->m_pIDMObject->AddRef();
  531. pDMObj = pObject->m_pIDMObject;
  532. hr = S_OK;
  533. }
  534. else
  535. {
  536. CObject *pPrevContext;
  537. if (fGC)
  538. {
  539. // Save a pointer to the current object that will be used to track that it is
  540. // the source object if nested calls to GetObject occur while it is loaded.
  541. pPrevContext = m_pLoadedObjectContext;
  542. m_pLoadedObjectContext = pObject;
  543. // Set this object as garbage-collectable.
  544. if (pObject->m_ObjectDesc.m_guidObject != GUID_DefaultGMCollection) // don't cache GM DLS set
  545. hr = pObject->GC_Collectable();
  546. }
  547. if (SUCCEEDED(hr))
  548. hrLoad = hr = pObject->Load();
  549. if (fGC)
  550. {
  551. // Restore the context that was used to load this object.
  552. m_pLoadedObjectContext = pPrevContext;
  553. }
  554. #ifdef DBG
  555. if (FAILED(hrLoad) || m_pLoadedObjectContext == m_pApplicationObject)
  556. {
  557. DebugTraceLoadFailure(pObject, hrLoad);
  558. }
  559. #endif
  560. if (SUCCEEDED(hr))
  561. {
  562. pDMObj = pObject->m_pIDMObject;
  563. if (fKeep)
  564. {
  565. pObject->m_pIDMObject->AddRef();
  566. }
  567. else
  568. {
  569. pObject->m_ObjectDesc.m_dwValidData &= ~DMUS_OBJ_LOADED;
  570. pObject->m_pIDMObject = NULL;
  571. }
  572. }
  573. }
  574. if (SUCCEEDED(hr) && fGC)
  575. {
  576. assert(m_pLoadedObjectContext);
  577. hr = m_pLoadedObjectContext->GC_AddReference(pObject);
  578. }
  579. if (FAILED(hr))
  580. {
  581. // This happens if either GC_AddReference or Load fails.
  582. pClass->RemoveObject(pObject);
  583. pObject = NULL;
  584. if (Desc.m_dwValidData & DMUS_OBJ_URL)
  585. {
  586. Trace(1, "Error: Attempt to load an object via DMUS_OBJ_URL failed because the DirectMusic Loader doesn't currently support loading objects by URL.");
  587. hr = DMUS_E_LOADER_FORMATNOTSUPPORTED;
  588. }
  589. }
  590. }
  591. else
  592. {
  593. hr = E_OUTOFMEMORY;
  594. }
  595. }
  596. LeaveCriticalSection(&m_CriticalSection);
  597. if( SUCCEEDED(hr) )
  598. {
  599. hr = pDMObj->QueryInterface( riid, ppv );
  600. pDMObj->Release();
  601. }
  602. if( E_FAIL == hr )
  603. {
  604. hr = DMUS_E_LOADER_FAILEDCREATE;
  605. }
  606. #ifdef DBG
  607. // After each top-level call to GetObject, report to debug output the contents of the cache.
  608. if (m_pLoadedObjectContext == m_pApplicationObject)
  609. GC_Report(this);
  610. #endif
  611. if( SUCCEEDED(hr) )
  612. {
  613. return hrLoad;
  614. }
  615. else
  616. {
  617. return hr;
  618. }
  619. }
  620. STDMETHODIMP CLoader::SetObject(
  621. LPDMUS_OBJECTDESC pDESC)
  622. {
  623. HRESULT hr = S_OK;
  624. HRESULT hrLoad = S_OK;
  625. CDescriptor Desc;
  626. V_INAME(IDirectMusicLoader::SetObject);
  627. V_STRUCTPTR_READ(pDESC,DMUS_OLDOBJECTDESC);
  628. IStream *pStream = NULL;
  629. if (pDESC->dwValidData & DMUS_OBJ_STREAM)
  630. {
  631. // Save the stream we were passed and verify it is a valid interface.
  632. pStream = pDESC->pStream;
  633. V_INTERFACE(pStream);
  634. // The loader will save a cloned stream so that the caller can contine
  635. // using the passed stream without intefering with the loader.
  636. // Don't worry -- we'll restore the original stream pointer before returning.
  637. hr = pStream->Clone(&pDESC->pStream);
  638. if (FAILED(hr))
  639. return hr;
  640. }
  641. // if pDESC has DMUS_OBJ_FULLPATH set, set DMUS_OBJ_FILENAME as well.
  642. if( pDESC->dwValidData & DMUS_OBJ_FULLPATH )
  643. {
  644. pDESC->dwValidData |= DMUS_OBJ_FILENAME;
  645. }
  646. EnterCriticalSection(&m_CriticalSection);
  647. CClass *pClass;
  648. Desc.Set(pDESC);
  649. hr = GetClass(&Desc,&pClass, TRUE);
  650. if (SUCCEEDED(hr))
  651. {
  652. CObject *pObject;
  653. hr = pClass->GetObject(&Desc,&pObject);
  654. if (SUCCEEDED(hr))
  655. {
  656. if (Desc.m_dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_MEMORY | DMUS_OBJ_STREAM))
  657. {
  658. pObject->m_ObjectDesc.m_dwValidData &=
  659. ~(DMUS_OBJ_FILENAME | DMUS_OBJ_MEMORY | DMUS_OBJ_STREAM);
  660. }
  661. // Merge in any new things we've added
  662. pObject->m_ObjectDesc.Merge(&Desc);
  663. if (pObject->m_ObjectDesc.m_dwValidData &
  664. (DMUS_OBJ_FILENAME | DMUS_OBJ_MEMORY | DMUS_OBJ_STREAM))
  665. {
  666. // If we can actually load this, have it give us its internal data.
  667. hr = pObject->Parse();
  668. }
  669. // Return the data.
  670. pObject->m_ObjectDesc.Get(pDESC);
  671. }
  672. }
  673. if (pStream)
  674. {
  675. // Restore the stream information in the descriptor we were passed.
  676. // Get will have cleared the stream bit (ordinarily we don't want to be returning these streams out of the loader).
  677. pDESC->dwValidData |= DMUS_OBJ_STREAM;
  678. // The stream pointer was changed to the cloned stream.
  679. pDESC->pStream->Release(); // release matching call to Clone (ref is now held in merged descriptor)
  680. pDESC->pStream = pStream;
  681. }
  682. LeaveCriticalSection(&m_CriticalSection);
  683. return hr;
  684. }
  685. /*
  686. @method:(EXTERNAL) HRESULT | IDirectMusicLoader | SetSearchDirectory |
  687. Sets a search path for finding object files. The search path can be set for
  688. one object file type, or, alternatively, all files.
  689. @rdesc Returns one of the following
  690. @flag S_OK | Success
  691. @flag S_FALSE | The search directory was already set to the requested path.
  692. @flag E_POINTER | Bad pointer passed in <p pszPath>.
  693. @flag DMUS_E_LOADER_BADPATH | Path is invalid.
  694. @flag E_OUTOFMEMORY | Running low on memory, unable to complete task.
  695. @comm Once a search path is set, the loader does not need a full path
  696. every time it is given an object to load by file name.
  697. However, the loader does not automatically becomes
  698. aware of all files of the requested type within the search
  699. directory. After calling <om IDirectMusicLoader::SetSearchDirectory>,
  700. call <om IDirectMusicLoader::ScanDirectory> to scan the
  701. directory for all
  702. files of the requested class and compile a list of them.
  703. Once this is done, you can easily find
  704. files within the directory by object name, or GUID, as well as
  705. file name.
  706. @ex The following example sets the search path for style files, then
  707. loads a style by file name. Although this seems a little redundant
  708. (it's simpler to just use the full path name),
  709. objects that indirectly reference other objects can
  710. find them by file name without knowing the full path: |
  711. // The first function calls SetSearchDirectory to set the path.
  712. // All subsequant calls to load objects in the application
  713. // no longer need to know the full path.
  714. HRESULT mySetLoaderPath (
  715. IDirectMusicLoader *pILoader) // Loader interface, previously created.
  716. {
  717. return pILoader->SetSearchDirectory(CLSID_DirectMusicStyle,
  718. L"c:\\mymusic\\funky",FALSE);
  719. }
  720. // Later, the application wants to load a style by
  721. // local file name.
  722. HRESULT myLoadStyleFromPath (
  723. IDirectMusicStyle **ppIStyle, // Style to load.
  724. IDirectMusicLoader *pILoader) // Loader.
  725. {
  726. HRESULT hr;
  727. DMUS_OBJECTDESC Desc; // Descriptor.
  728. // Start by initializing Desc with the local file name for the object.
  729. wcscpy(Desc.wszName,L"Polka"); // Name is wide char format.
  730. wcscpy(Desc.wszFileName,L"polka.sty"); // Use file name without full path.
  731. Desc.guidClass = CLSID_DirectMusicStyle; // Style class.
  732. Desc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME;
  733. Desc.dwSize = sizeof (DMUS_OBJECTDESC);
  734. hr = pILoader->GetObject(&Desc, IID_IDirectMusicStyle, (void**) ppIStyle);
  735. return hr;
  736. }
  737. @xref <i IDirectMusicLoader>, <i IDirectMusicObject>,
  738. <om IDirectMusicLoader::GetObject>,
  739. <om IDirectMusicLoader::ScanDirectory>,
  740. <om IDirectMusicLoader::EnumObject>
  741. */
  742. STDMETHODIMP CLoader::SetSearchDirectory(
  743. REFCLSID rguidClass, // @parm Class id identifies which clas of objects this pertains to.
  744. // Optionally, GUID_DirectMusicAllTypes specifies all classes.
  745. WCHAR *pwzPath, // @parm File path for directory. Must be a valid directory and
  746. // must be less than MAX_PATH in length.
  747. BOOL fClear) // @parm If TRUE, clears all information about objects
  748. // prior to setting directory.
  749. // This helps avoid accessing objects from the
  750. // previous directory that may have the same name.
  751. // However, this will not remove cached objects.
  752. {
  753. V_INAME(IDirectMusicLoader::SetSearchDirectory);
  754. if (pwzPath)
  755. {
  756. V_BUFPTR_READ(pwzPath,2);
  757. }
  758. HRESULT hr = DMUS_E_LOADER_BADPATH;
  759. WCHAR wzMaxPath[MAX_PATH];
  760. if (pwzPath == NULL)
  761. {
  762. return E_POINTER;
  763. }
  764. wcscpy( wzMaxPath, pwzPath );
  765. if( wzMaxPath[wcslen( wzMaxPath ) - 1] != '\\' )
  766. {
  767. wcscat( wzMaxPath, L"\\" );
  768. }
  769. DWORD dwAttrib;
  770. if (g_fIsUnicode)
  771. {
  772. dwAttrib= GetFileAttributesW(wzMaxPath);
  773. }
  774. else
  775. {
  776. char szPath[MAX_PATH];
  777. wcstombs( szPath, wzMaxPath, MAX_PATH );
  778. dwAttrib= GetFileAttributesA(szPath);
  779. }
  780. if ((dwAttrib != 0xFFFFFFFF) && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY))
  781. {
  782. EnterCriticalSection(&m_CriticalSection);
  783. if (rguidClass == GUID_DirectMusicAllTypes)
  784. {
  785. CClass *pClass = m_ClassList.GetHead();
  786. hr = S_OK;
  787. for (;pClass != NULL;pClass = pClass->GetNext())
  788. {
  789. if( S_FALSE == pClass->SetSearchDirectory(wzMaxPath,fClear))
  790. {
  791. hr = S_FALSE;
  792. }
  793. }
  794. wcsncpy(m_wzPath,wzMaxPath,DMUS_MAX_FILENAME);
  795. m_fPathValid = TRUE;
  796. }
  797. else
  798. {
  799. CClass *pClass;
  800. CDescriptor Desc;
  801. Desc.m_guidClass = rguidClass;
  802. Desc.m_dwValidData = DMUS_OBJ_CLASS;
  803. hr = GetClass(&Desc,&pClass,TRUE);
  804. if (SUCCEEDED(hr))
  805. {
  806. hr = pClass->SetSearchDirectory(wzMaxPath,fClear);
  807. }
  808. }
  809. LeaveCriticalSection(&m_CriticalSection);
  810. }
  811. #ifdef DBG
  812. if (hr == DMUS_E_LOADER_BADPATH)
  813. {
  814. Trace(1, "Error: SetSearchDirectory failed because '%S' is not a valid directory.\n", pwzPath);
  815. }
  816. #endif
  817. return hr;
  818. }
  819. /*
  820. @method:(EXTERNAL) HRESULT | IDirectMusicLoader | ScanDirectory |
  821. Searches a directory on disk for all files of a requested
  822. class type and file extension. Once a directory has been scanned, all
  823. files of the requested type become available for viewing with
  824. <om IDirectMusicLoader::EnumObject>.
  825. Prior to calling <om IDirectMusicLoader::ScanDirectory>,
  826. <om IDirectMusicLoader::SetSearchDirectory> must be called
  827. first to set the location to search.
  828. Once a search path has been scanned, the loader automatically becomes
  829. aware of all files within the search directory, and can easily find
  830. files within that directory by object name, file name, or GUID.
  831. Optionally, the scanned information can be stored in a
  832. cache file, defined by <p pszCacheFileName>. Once it has been
  833. so stored, subsequant calls to <om IDirectMusicLoader::ScanDirectory>
  834. are much quicker, because only files that have changed
  835. are scanned (the cache file stores the file size and date for
  836. each object, so it can identify if a file has changed.)
  837. @comm If the file type has more than one extension,
  838. just call
  839. <om IDirectMusicLoader::ScanDirectory> multiple
  840. times, once for each file extension.
  841. <om IDirectMusicLoader::ScanDirectory> doesn't know
  842. how to parse a particular object class's file and read
  843. the name, guid, and other information that retieves to idenity
  844. files by. Instead, it lets the <i IDirectMusicObject> do the work,
  845. by calling the
  846. <om IDirectMusicObject::ParseDescriptor> method
  847. for the requested object type.
  848. @rdesc Returns one of the following
  849. @flag S_OK | Success.
  850. @flag S_FALSE | Scanned, but no files.
  851. @flag E_POINTER | Bad pointer passed in <p pszFileExtension> or <p pszCacheFileName>.
  852. @flag DMUS_E_NOT_FOUND | Path is invalid.
  853. @flag E_OUTOFMEMORY | Running low on memory, unable to complete task.
  854. @flag REGDB_E_CLASSNOTREG | Object class is not registered, can't read files.
  855. @ex The following example sets the search path for style files, scans the directory,
  856. then uses the EnumObject call to display all available style files: |
  857. // First, scan the directory for all style files.
  858. void myScanDirectory(
  859. IDirectMusicLoader *pILoader) // Loader.
  860. {
  861. HRESULT hr = pILoader->SetSearchDirectory(
  862. CLSID_DirectMusicStyle,L"c:\\mymusic\\wassup",TRUE);
  863. if (SUCCEEDED(hr))
  864. {
  865. hr = pILoader->ScanDirectory(
  866. CLSID_DirectMusicStyle,L"sty",L"stylecache");
  867. if (hr == S_OK) // Only if files were found...
  868. {
  869. DWORD dwIndex;
  870. DMUS_OBJECTDESC Desc;
  871. Desc.dwSize = sizeof(DMUS_OBJECTDESC);
  872. for (dwIndex = 0; ;dwIndex++)
  873. {
  874. if (S_OK ==(pILoader->EnumObject(CLSID_DirectMusicStyle,
  875. dwIndex,&Desc)))
  876. {
  877. TRACE("Name: %S, Category: %S, Path: %S\n",
  878. Desc.wszName,Desc.wszCategory,Desc.wszFileName);
  879. }
  880. else break;
  881. }
  882. }
  883. }
  884. }
  885. @xref <i IDirectMusicLoader>, <i IDirectMusicObject>,
  886. <om IDirectMusicLoader::GetObject>,
  887. <om IDirectMusicLoader::EnumObject>, <om IDirectMusicLoader::SetSearchDirectory>
  888. */
  889. STDMETHODIMP CLoader::ScanDirectory(
  890. REFCLSID rguidClass, // @parm Class id identifies which class of objects this pertains to.
  891. WCHAR *pszFileExtension,// @parm File extension for type of file to look for.
  892. // For example, L"sty" for style files. L"*" will look in all
  893. // files. L"" or NULL will look for files without an
  894. // extension.
  895. WCHAR *pszCacheFileName // @parm Optional storage file to store and retrieve
  896. // cached file information. This file is created by
  897. // the first call to <om IDirectMusicLoader::ScanDirectory>
  898. // and used by subsequant calls. NULL if cache file
  899. // not desired.
  900. )
  901. {
  902. V_INAME(IDirectMusicLoader::ScanDirectory);
  903. if (pszFileExtension)
  904. {
  905. V_BUFPTR_READ(pszFileExtension,2);
  906. }
  907. if (pszCacheFileName)
  908. {
  909. V_BUFPTR_READ(pszCacheFileName,2);
  910. }
  911. HRESULT hr = S_OK;
  912. // First, mark all currently stored objects prior to scanning.
  913. CClass *pClass = m_ClassList.GetHead();
  914. for (;pClass != NULL;pClass = pClass->GetNext())
  915. {
  916. pClass->PreScan();
  917. }
  918. if (pszCacheFileName != NULL)
  919. {
  920. LoadCacheFile(pszCacheFileName);
  921. }
  922. if (rguidClass == GUID_DirectMusicAllTypes)
  923. {
  924. Trace(1, "Error: ScanDirectory requires the clsid of a specific type of object to scan for. GUID_DirectMusicAllTypes is not valid.\n");
  925. return REGDB_E_CLASSNOTREG;
  926. }
  927. else
  928. {
  929. CDescriptor Desc;
  930. Desc.m_guidClass = rguidClass;
  931. Desc.m_dwValidData = DMUS_OBJ_CLASS;
  932. EnterCriticalSection(&m_CriticalSection);
  933. GetClass(&Desc,&pClass,TRUE);
  934. if (pClass)
  935. {
  936. if( pszFileExtension )
  937. {
  938. hr = pClass->SearchDirectory(pszFileExtension);
  939. }
  940. else
  941. {
  942. hr = pClass->SearchDirectory(L"");
  943. }
  944. // if( hr == E_FAIL ) hr = DMUS_E_NOT_FOUND;
  945. }
  946. LeaveCriticalSection(&m_CriticalSection);
  947. }
  948. if (pszCacheFileName != NULL)
  949. {
  950. SaveCacheFile(pszCacheFileName);
  951. }
  952. return hr;
  953. }
  954. HRESULT CLoader::FindObject(CDescriptor *pDesc, CClass **ppClass, CObject ** ppObject)
  955. // Scan through the classes and objects to find the object.
  956. {
  957. assert(pDesc);
  958. HRESULT hr = GetClass(pDesc,ppClass,FALSE);
  959. if (SUCCEEDED(hr))
  960. {
  961. hr = (*ppClass)->FindObject(pDesc,ppObject);
  962. #ifdef DBG
  963. if (hr == DMUS_E_LOADER_OBJECTNOTFOUND)
  964. {
  965. DMUS_OBJECTDESC desc;
  966. ZeroAndSize(&desc);
  967. pDesc->Get(&desc);
  968. Trace(1, "Error: The following object could not be found in the loader: ");
  969. DebugTraceObject(&desc);
  970. }
  971. #endif
  972. }
  973. return hr;
  974. }
  975. // Find the object in the cache. S_FALSE if not loaded. Error if not found.
  976. HRESULT CLoader::FindObject(IDirectMusicObject *pIDMObject, CObject ** ppObject)
  977. {
  978. // Potential optimization:
  979. // The linear search to find the object could be eliminated by using
  980. // an efficient lookup structure such as a hash table.
  981. assert(pIDMObject && ppObject);
  982. HRESULT hr = S_OK;
  983. DMUS_OBJECTDESC DESC;
  984. ZeroMemory( &DESC, sizeof(DMUS_OBJECTDESC) );
  985. DESC.dwValidData = 0;
  986. DESC.dwSize = sizeof (DMUS_OBJECTDESC);
  987. hr = pIDMObject->GetDescriptor(&DESC);
  988. if (FAILED(hr))
  989. return hr;
  990. CDescriptor Desc;
  991. Desc.Set(&DESC);
  992. SmartRef::CritSec CS(&m_CriticalSection);
  993. CClass *pClass;
  994. CObject *pCObject;
  995. hr = FindObject(&Desc,&pClass,&pCObject);
  996. if (FAILED(hr))
  997. return hr;
  998. assert(pCObject);
  999. if (pCObject->m_pIDMObject == pIDMObject)
  1000. {
  1001. *ppObject = pCObject;
  1002. return S_OK;
  1003. }
  1004. else
  1005. {
  1006. return S_FALSE;
  1007. }
  1008. }
  1009. /*
  1010. @method:(EXTERNAL) HRESULT | IDirectMusicLoader | CacheObject |
  1011. Tells the loader to keep a reference to the object. This guarantees
  1012. that the object will not be loaded twice.
  1013. @rdesc Returns one of the following
  1014. @flag S_OK | Success
  1015. @flag S_FALSE | Object already cached.
  1016. @flag E_POINTER | Bad pointer passed in <p pIObject>.
  1017. @flag DMUS_E_LOADER_OBJECTNOTFOUND | Object was not found.
  1018. @comm If you have an object that will be accessed in multiple places
  1019. throughout the life of your program, letting the loader cache the object
  1020. can significantly speed performance.
  1021. Alternatively, tell the loader to automatically cache all objects of
  1022. a particular type with a call to
  1023. <om IDirectMusicLoader::EnableCache>.
  1024. Remove the reference later with a call to
  1025. <om IDirectMusicLoader::ReleaseObject> or
  1026. <om IDirectMusicLoader::ClearCache>.
  1027. @xref <i IDirectMusicLoader>, <om IDirectMusicLoader::EnableCache>,
  1028. <om IDirectMusicLoader::ReleaseObject>,
  1029. <om IDirectMusicLoader::ClearCache>
  1030. */
  1031. STDMETHODIMP CLoader::CacheObject(
  1032. IDirectMusicObject * pObject) // @parm Object to cache.
  1033. {
  1034. HRESULT hr;
  1035. DMUS_OBJECTDESC DESC;
  1036. ZeroMemory( &DESC, sizeof(DMUS_OBJECTDESC) );
  1037. DESC.dwSize = sizeof (DMUS_OBJECTDESC);
  1038. V_INAME(IDirectMusicLoader::CacheObject);
  1039. V_INTERFACE(pObject);
  1040. if (pObject == NULL)
  1041. {
  1042. return E_POINTER;
  1043. }
  1044. DESC.dwValidData = 0;
  1045. hr = pObject->GetDescriptor(&DESC);
  1046. if (SUCCEEDED(hr))
  1047. {
  1048. CDescriptor Desc;
  1049. Desc.Set(&DESC);
  1050. CClass *pClass;
  1051. CObject *pCObject;
  1052. ::EnterCriticalSection(&m_CriticalSection);
  1053. hr = FindObject(&Desc,&pClass,&pCObject);
  1054. if (SUCCEEDED(hr))
  1055. {
  1056. if (pCObject->m_pIDMObject && (pCObject->m_pIDMObject != pObject))
  1057. {
  1058. pCObject->m_pIDMObject->Release();
  1059. pCObject->m_pIDMObject = NULL;
  1060. }
  1061. if (pCObject->m_pIDMObject != pObject)
  1062. {
  1063. pCObject->m_pIDMObject = pObject;
  1064. pCObject->m_ObjectDesc.m_dwValidData |= DMUS_OBJ_LOADED;
  1065. pObject->AddRef();
  1066. }
  1067. else
  1068. {
  1069. hr = S_FALSE;
  1070. }
  1071. }
  1072. ::LeaveCriticalSection(&m_CriticalSection);
  1073. }
  1074. /* if( E_FAIL == hr ) // Should never happen...
  1075. {
  1076. hr = DMUS_E_LOADER_OBJECTNOTFOUND;
  1077. }*/
  1078. return hr;
  1079. }
  1080. /*
  1081. @method:(EXTERNAL) HRESULT | IDirectMusicLoader | ReleaseObject |
  1082. Tells the loader to release its reference to the object.
  1083. @rdesc Returns one of the following
  1084. @flag S_OK | Success
  1085. @flag E_POINTER | Bad pointer passed in <p pIObject>.
  1086. @flag DMUS_E_LOADER_OBJECTNOTFOUND | Object was not found or was already released.
  1087. @comm <om IDirectMusicLoader::ReleaseObject> is the reciprocal
  1088. of <om IDirectMusicLoader::CacheObject>.
  1089. Objects can be cached explicitly via
  1090. <om IDirectMusicLoader::CacheObject>,
  1091. or automatically via <om IDirectMusicLoader::EnableCache>.
  1092. To tell the loader to flush all objects of
  1093. a particular type, call
  1094. <om IDirectMusicLoader::ClearCache>.
  1095. @xref <i IDirectMusicLoader>, <om IDirectMusicLoader::EnableCache>,
  1096. <om IDirectMusicLoader::CacheObject>,
  1097. <om IDirectMusicLoader::ClearCache>
  1098. */
  1099. STDMETHODIMP CLoader::ReleaseObject(
  1100. IDirectMusicObject * pObject) // @parm Object to release.
  1101. {
  1102. V_INAME(IDirectMusicLoader::ReleaseObject);
  1103. V_INTERFACE(pObject);
  1104. HRESULT hr = S_OK;
  1105. CObject *pCObject = NULL;
  1106. SmartRef::CritSec CS(&m_CriticalSection);
  1107. hr = FindObject(pObject, &pCObject);
  1108. // Removed the following because it causes a regression from DX7, even though it is the better return.
  1109. // if (hr == S_FALSE)
  1110. // hr = DMUS_E_LOADER_OBJECTNOTFOUND;
  1111. if (hr == S_OK)
  1112. {
  1113. if (pCObject->m_dwScanBits & SCAN_GC)
  1114. {
  1115. // Other objects may have references to this one so we need to keep this object around
  1116. // and track its references. We'll hold onto the DMObject pointer too because we may
  1117. // later need to Zombie the object in order to break a cyclic reference.
  1118. // We'll place an unloaded object with a duplicate descriptor in the cache to match the
  1119. // non-GC behavior and then move the original object into a list of released objects that
  1120. // will eventually be reclaimed by CollectGarbage.
  1121. // Potential optimization:
  1122. // Here we re-iterate to remove from the list when we just iterated during FindObject.
  1123. // Returning more info from FindObject, expanding it into this function, or using some
  1124. // other technique would make this operation twice as fast.
  1125. hr = pCObject->GC_RemoveAndDuplicateInParentList();
  1126. if (FAILED(hr))
  1127. return hr;
  1128. GC_UpdateForReleasedObject(pCObject);
  1129. }
  1130. else
  1131. {
  1132. pCObject->m_pIDMObject->Release();
  1133. pCObject->m_pIDMObject = NULL;
  1134. pCObject->m_ObjectDesc.m_dwValidData &= ~DMUS_OBJ_LOADED;
  1135. }
  1136. }
  1137. return hr;
  1138. }
  1139. /*
  1140. @method:(EXTERNAL) HRESULT | IDirectMusicLoader | ClearCache |
  1141. Tells the loader to release all references to a particular type
  1142. of object.
  1143. @rdesc Returns just
  1144. @flag S_OK | Always succeeds
  1145. @comm <om IDirectMusicLoader::ClearCache> clears all objects
  1146. that are currently being held. However, if caching is enabled
  1147. via <om IDirectMusicLoader::EnableCache>, this does not
  1148. turn off caching so future file loads will continue to be cached.
  1149. Use <om IDirectMusicLoader::ReleaseObject> to release a specific
  1150. object.
  1151. Call <om IDirectMusicLoader::EnableCache> to turn off automatic
  1152. caching.
  1153. @xref <i IDirectMusicLoader>, <om IDirectMusicLoader::EnableCache>,
  1154. <om IDirectMusicLoader::CacheObject>,
  1155. <om IDirectMusicLoader::ReleaseObject>
  1156. */
  1157. STDMETHODIMP CLoader::ClearCache(
  1158. REFCLSID rguidClass) // @parm Class id identifies which class of objects to clear.
  1159. // Optionally, GUID_DirectMusicAllTypes specifies all types.
  1160. {
  1161. return ClearCacheInternal(rguidClass, false);
  1162. }
  1163. HRESULT CLoader::ClearCacheInternal(
  1164. REFCLSID rguidClass,
  1165. bool fClearStreams)
  1166. {
  1167. SmartRef::CritSec CS(&m_CriticalSection);
  1168. HRESULT hr = S_OK;
  1169. CClass *pClass;
  1170. if (rguidClass == GUID_DirectMusicAllTypes)
  1171. {
  1172. pClass = m_ClassList.GetHead();
  1173. for (;pClass != NULL;pClass = pClass->GetNext())
  1174. {
  1175. hr = pClass->ClearCache(fClearStreams);
  1176. if (FAILED(hr))
  1177. return hr;
  1178. }
  1179. }
  1180. else
  1181. {
  1182. CDescriptor Desc;
  1183. Desc.m_guidClass = rguidClass;
  1184. Desc.m_dwValidData = DMUS_OBJ_CLASS;
  1185. GetClass(&Desc,&pClass,FALSE);
  1186. if (pClass)
  1187. {
  1188. hr = pClass->ClearCache(fClearStreams);
  1189. if (FAILED(hr))
  1190. return hr;
  1191. }
  1192. }
  1193. return S_OK;
  1194. }
  1195. /*
  1196. @method:(EXTERNAL) HRESULT | IDirectMusicLoader | EnableCache |
  1197. Tells the loader to enable or disable automatic caching of
  1198. objects it loads. By default, caching is enabled for all
  1199. object classes.
  1200. Once caching is enabled with a call to
  1201. <om IDirectMusicLoader::EnableCache>, the loader keeps a reference to
  1202. all objects it loads subsequently,
  1203. either directly or indirectly (via a referenced load, for example, a
  1204. Section Segment that references a Style).
  1205. <om IDirectMusicLoader::EnableCache> can also be used to disable
  1206. caching by setting <p fEnable> to FALSE. Before disabling caching, think
  1207. twice. Caching is used extensively in the file loading process to
  1208. resolve links to objects. If an object is not found in the cache, it
  1209. has to be reloaded, even if it already exists. For example, two segments
  1210. could reference the same style. When the first segment loads, it calls the
  1211. loader to get the style, which in turn creates a style, loads it from disk,
  1212. stores a pointer to the style in the cache, and returns it to the segment.
  1213. When the second segment loader, it asks for the style and the loader immediately
  1214. returns it, so both segments point to the same style. If caching is disabled,
  1215. the second segment's request for the style results in a duplicate style
  1216. loaded from the file. This is very inefficient.
  1217. Another example: <i IDirectMusicBand> counts on the loader to keep the
  1218. GM DLS collection cached. Every time it comes across a general MIDI instrument,
  1219. it gets the GM DLS collection from the loader by requesting it with
  1220. GUID_DefaultGMCollection. If caching for CLSID_DirectMusicCollection is
  1221. disabled, every patch change in a general MIDI file will result in a
  1222. seperate copy of the entire GM collection being created! Not good!
  1223. However, with judicious use of <om IDirectMusicLoader::CacheObject>,
  1224. <om IDirectMusicLoader::ReleaseObject>, and <om IDirectMusicLoader::EnableCache>,
  1225. you can have the objects you don't need released, while others stick around
  1226. in the cache.
  1227. To clear the cache without disabling caching, call
  1228. <om IDirectMusicLoader::ClearCache>.
  1229. @ex The following example disables caching for just segment objects, so they
  1230. don't stay in memory after the application releases them. Yet, other objects
  1231. that should be shared, like styles, personalities and DLS collections, continue
  1232. to be cached. |
  1233. void myPrepareLoader(IDirectMusicLoader *pILoader)
  1234. {
  1235. pILoader->EnableCache(GUID_DirectMusicAllTypes, TRUE);
  1236. pILoader->EnableCache(CLSID_DirectMusicSegment, FALSE);
  1237. }
  1238. @rdesc Returns just
  1239. @flag S_OK | Success.
  1240. @flag S_FALSE | The cache was already in the requested state.
  1241. @xref <i IDirectMusicLoader>, <om IDirectMusicLoader::ClearCache>,
  1242. <om IDirectMusicLoader::CacheObject>,
  1243. <om IDirectMusicLoader::ReleaseObject>
  1244. */
  1245. STDMETHODIMP CLoader::EnableCache(
  1246. REFCLSID rguidClass, // @parm Class id identifies which class of objects to cache.
  1247. // Optionally, GUID_DirectMusicAllTypes specifies all types.
  1248. BOOL fEnable) // @parm TRUE to enable caching, FALSE to clear and disable.
  1249. {
  1250. CClass *pClass;
  1251. HRESULT hr = S_OK;
  1252. if (rguidClass == GUID_DirectMusicAllTypes)
  1253. {
  1254. pClass = m_ClassList.GetHead();
  1255. for (;pClass != NULL;pClass = pClass->GetNext())
  1256. {
  1257. if( S_FALSE == pClass->EnableCache(fEnable))
  1258. {
  1259. hr = S_FALSE;
  1260. }
  1261. }
  1262. m_fKeepObjects = fEnable;
  1263. }
  1264. else
  1265. {
  1266. CDescriptor Desc;
  1267. Desc.m_guidClass = rguidClass;
  1268. Desc.m_dwValidData = DMUS_OBJ_CLASS;
  1269. GetClass(&Desc,&pClass,TRUE);
  1270. if (pClass)
  1271. {
  1272. if( S_FALSE == pClass->EnableCache(fEnable))
  1273. {
  1274. hr = S_FALSE;
  1275. }
  1276. }
  1277. }
  1278. return hr;
  1279. }
  1280. /*
  1281. @method:(EXTERNAL) HRESULT | IDirectMusicLoader | EnumObject |
  1282. Enumerate through all available objects of the requested type.
  1283. @rdesc Returns one of the following
  1284. @flag S_OK | Found object at request index.
  1285. @flag S_FALSE | Reached end of list.
  1286. @ex Use <om IDirectMusicLoader::EnumObject> to walk through all styles
  1287. that are already referenced by the loader. These might have been prepared
  1288. with a call to <om IDirectMusicLoader::ScanDirectory> or loaded
  1289. individually. |
  1290. void myDisplayStyles(
  1291. IDirectMusicLoader *pILoader)
  1292. {
  1293. DWORD dwIndex;
  1294. DMUS_OBJECTDESC Desc;
  1295. Desc.dwSize = sizeof(DMUS_OBJECTDESC);
  1296. for (dwIndex = 0; ;dwIndex++)
  1297. {
  1298. if (S_OK ==(pILoader->EnumObject(CLSID_DirectMusicStyle,
  1299. dwIndex,&Desc)))
  1300. {
  1301. TRACE("Name: %S, Category: %S, Path: %S\n",
  1302. Desc.wszName,Desc.wszCategory,Desc.wszFileName);
  1303. }
  1304. else break;
  1305. }
  1306. }
  1307. @xref <i IDirectMusicLoader>, <t DMUS_OBJECTDESC>
  1308. */
  1309. STDMETHODIMP CLoader::EnumObject(
  1310. REFCLSID rguidClass, // @parm Class ID for class of objects to view.
  1311. DWORD dwIndex, // @parm Index into list. Typically, starts with 0 and increments.
  1312. LPDMUS_OBJECTDESC pDESC) // @parm <t DMUS_OBJECTDESC> structure to be filled with data about object.
  1313. {
  1314. HRESULT hr;
  1315. CClass *pClass;
  1316. CDescriptor Desc;
  1317. V_INAME(IDirectMusicLoader::EnumObject);
  1318. V_STRUCTPTR_WRITE(pDESC,DMUS_OLDOBJECTDESC);
  1319. Desc.m_guidClass = rguidClass;
  1320. Desc.m_dwValidData = DMUS_OBJ_CLASS;
  1321. EnterCriticalSection(&m_CriticalSection);
  1322. hr = GetClass(&Desc,&pClass,TRUE);
  1323. if (SUCCEEDED(hr))
  1324. {
  1325. hr = pClass->EnumerateObjects(dwIndex, &Desc);
  1326. Desc.Get(pDESC);
  1327. }
  1328. LeaveCriticalSection(&m_CriticalSection);
  1329. return hr;
  1330. }
  1331. void
  1332. CLoader::GC_Mark(CObject *pObject)
  1333. {
  1334. // mark pObject and everything it references
  1335. GC_TraverseHelper(pObject, NULL, true);
  1336. }
  1337. bool
  1338. CLoader::GC_HasCycle(CObject *pObject)
  1339. {
  1340. // see if pObject has a cyclical reference
  1341. bool fFound = GC_TraverseHelper(pObject, pObject, true);
  1342. // the search left marks while traversing, so clear them
  1343. GC_TraverseHelper(pObject, pObject, false);
  1344. return fFound;
  1345. }
  1346. // Function used to recursively traverse references.
  1347. // pObject: Root to start the search from.
  1348. // pObjectToFind: Stop marking and return true if a reference to this object is encountered.
  1349. // (Can be the same as pObject without being considered a match unless pObject has a reference to itself.)
  1350. // fMark: If true, objects are marked as they are visited. If false, the opposite occurs, clearing marks.
  1351. bool
  1352. CLoader::GC_TraverseHelper(CObject *pObject, CObject *pObjectToFind, bool fMark)
  1353. {
  1354. // Potential optimization:
  1355. // This could be written using an explicit stack instead of recursion and
  1356. // it might be significantly faster. If this were done then this algorithm should
  1357. // also be changed to use a fixed-size stack. If the stack is exhausted, the
  1358. // object would be marked as unexamined and these unexamined objects would be
  1359. // marked in later passes. However, since that's getting unnecessarily complex
  1360. // we'll stick with recursion unless it proves to be a problem.
  1361. if (!pObject || !(pObject->m_dwScanBits & SCAN_GC) || !pObject->m_pvecReferences)
  1362. {
  1363. Trace(1, "Error: Unexpected error encountered during garbage collection.\n");
  1364. return false;
  1365. }
  1366. if (!!(pObject->m_dwScanBits & SCAN_GC_MARK) == fMark)
  1367. return false; // already done
  1368. if (fMark)
  1369. pObject->m_dwScanBits |= SCAN_GC_MARK;
  1370. else
  1371. pObject->m_dwScanBits &= ~SCAN_GC_MARK;
  1372. SmartRef::Vector<CObject*> &vecRefs = *pObject->m_pvecReferences;
  1373. const UINT iEnd = vecRefs.size();
  1374. // While we iterate over the references, we're going to write them back into the
  1375. // vector, compacting away any NULL slots created by GC_RemoveReference.
  1376. UINT iWrite = 0;
  1377. for (UINT i = 0; i < iEnd; ++i)
  1378. {
  1379. CObject *pObjectRef = vecRefs[i];
  1380. if (pObjectRef)
  1381. {
  1382. if (pObjectRef == pObjectToFind)
  1383. return true;
  1384. if (GC_TraverseHelper(pObjectRef, pObjectToFind, fMark))
  1385. return true;
  1386. if (!pObjectToFind)
  1387. {
  1388. // Compact empty slots only when just marking. (Doing so while searching for an object could
  1389. // return before the compacting loop is complete, leaving the vector in an inconsistent state.)
  1390. vecRefs[iWrite++] = pObjectRef;
  1391. }
  1392. }
  1393. }
  1394. if (!pObjectToFind)
  1395. vecRefs.Shrink(iWrite);
  1396. return false;
  1397. }
  1398. STDMETHODIMP_(void)
  1399. CLoader::CollectGarbage()
  1400. {
  1401. SmartRef::CritSec CS(&m_CriticalSection);
  1402. if (m_pApplicationObject)
  1403. {
  1404. #ifdef DBG
  1405. DebugTrace(GC_Report_DebugLevel, "DirectMusic loader CollectGarbage...\n");
  1406. #endif
  1407. GC_Mark(m_pApplicationObject);
  1408. // sweep through everything looking for unmarked GC objects
  1409. m_ReleasedObjectList.GC_Sweep(TRUE);
  1410. for (CClass *pClass = m_ClassList.GetHead(); pClass != NULL; pClass = pClass->GetNext())
  1411. pClass->GC_Sweep();
  1412. m_ReleasedObjectList.GC_Sweep();
  1413. // clear the application's mark for next time (the other marks are all cleared by sweep)
  1414. m_pApplicationObject->m_dwScanBits &= ~SCAN_GC_MARK;
  1415. #ifdef DBG
  1416. DebugTrace(GC_Report_DebugLevel, "End of garbage collection.\n\n");
  1417. #endif
  1418. }
  1419. #ifdef DBG
  1420. GC_Report(this);
  1421. #endif
  1422. }
  1423. STDMETHODIMP
  1424. CLoader::ReleaseObjectByUnknown(IUnknown *pObject)
  1425. {
  1426. V_INAME(CLoader::ReleaseObjectByUnknown);
  1427. V_INTERFACE(pObject);
  1428. IDirectMusicObject *pIDMObject = NULL;
  1429. HRESULT hr = pObject->QueryInterface(IID_IDirectMusicObject, reinterpret_cast<void**>(&pIDMObject));
  1430. if (FAILED(hr))
  1431. return hr;
  1432. hr = ReleaseObject(pIDMObject);
  1433. pIDMObject->Release();
  1434. return hr;
  1435. }
  1436. STDMETHODIMP
  1437. CLoader::GetDynamicallyReferencedObject(
  1438. IDirectMusicObject *pSourceObject,
  1439. LPDMUS_OBJECTDESC pDesc,
  1440. REFIID riid,
  1441. LPVOID FAR *ppv)
  1442. {
  1443. V_INAME(CLoader::GetDynamicallyReferencedObject);
  1444. V_INTERFACE(pSourceObject);
  1445. CObject *pCSourceObject = NULL;
  1446. SmartRef::CritSec CS(&m_CriticalSection);
  1447. HRESULT hr = FindObject(pSourceObject, &pCSourceObject);
  1448. if (FAILED(hr))
  1449. return hr;
  1450. if (!pCSourceObject)
  1451. {
  1452. assert(false);
  1453. return DMUS_E_LOADER_OBJECTNOTFOUND;
  1454. }
  1455. CObject *pPrevContext = m_pLoadedObjectContext;
  1456. m_pLoadedObjectContext = pCSourceObject;
  1457. hr = this->GetObject(pDesc, riid, ppv);
  1458. m_pLoadedObjectContext = pPrevContext;
  1459. #ifdef DBG
  1460. GC_Report(this);
  1461. #endif
  1462. return hr;
  1463. }
  1464. STDMETHODIMP
  1465. CLoader::ReportDynamicallyReferencedObject(
  1466. IDirectMusicObject *pSourceObject,
  1467. IUnknown *pReferencedObject)
  1468. {
  1469. V_INAME(CLoader::GetDynamicallyReferencedObject);
  1470. V_INTERFACE(pSourceObject);
  1471. V_INTERFACE(pReferencedObject);
  1472. CObject *pCSourceObject = NULL;
  1473. SmartRef::CritSec CS(&m_CriticalSection);
  1474. HRESULT hr = FindObject(pSourceObject, &pCSourceObject);
  1475. if (hr == S_FALSE)
  1476. hr = DMUS_E_LOADER_OBJECTNOTFOUND;
  1477. if (FAILED(hr))
  1478. return hr;
  1479. assert(pCSourceObject);
  1480. IDirectMusicObject *pReferencedIDMObject = NULL;
  1481. hr = pReferencedObject->QueryInterface(IID_IDirectMusicObject, reinterpret_cast<void**>(&pReferencedIDMObject));
  1482. if (FAILED(hr))
  1483. {
  1484. if (hr == E_NOINTERFACE)
  1485. hr = S_OK; // If the referenced object isn't a DirectMusic object then that's OK and we don't need to track it.
  1486. return hr;
  1487. }
  1488. CObject *pCDestObject = NULL;
  1489. hr = FindObject(pReferencedIDMObject, &pCDestObject);
  1490. if (hr == S_FALSE)
  1491. hr = DMUS_E_LOADER_OBJECTNOTFOUND;
  1492. if (FAILED(hr))
  1493. return hr;
  1494. assert(pCDestObject);
  1495. hr = pCSourceObject->GC_AddReference(pCDestObject);
  1496. #ifdef DBG
  1497. GC_Report(this);
  1498. #endif
  1499. return hr;
  1500. }
  1501. HRESULT CLoader::GetPath(WCHAR *pwzPath)
  1502. {
  1503. if (m_fPathValid)
  1504. {
  1505. wcsncpy(pwzPath,m_wzPath,DMUS_MAX_FILENAME);
  1506. return S_OK;
  1507. }
  1508. wcsncpy(pwzPath,L"",DMUS_MAX_FILENAME);
  1509. return S_FALSE;
  1510. }
  1511. // Used by ReleaseObject and CClass::ClearCache in removing objects from the cache.
  1512. // The object must have already been removed from its list in the main cache.
  1513. // This method adds it to the released object list and removes it from the list of
  1514. // objects in use by the application.
  1515. void CLoader::GC_UpdateForReleasedObject(CObject *pObject)
  1516. {
  1517. assert(!pObject->GetNext());
  1518. m_ReleasedObjectList.AddHead(pObject);
  1519. assert(m_pApplicationObject);
  1520. m_pApplicationObject->GC_RemoveReference(pObject);
  1521. if (!(pObject->m_ObjectDesc.m_guidClass == CLSID_DirectMusicScript) && !GC_HasCycle(pObject))
  1522. {
  1523. // Although we need to keep the record around (CObject), we know that this object
  1524. // can't be involved in any cycles and therefore we can release it.
  1525. // (If a cycle is possible we'd need to hold a ref on the object so we could break the
  1526. // reference by calling Zombie during CollectGarbage.)
  1527. // bugbug: The hard-coded check for CLSID_IDirectMusicScript will need to be extended
  1528. // if we publicly expose methods like IDirectMusicLoader8P::GetDynamicallyReferencedObject
  1529. // so that objects other than scripts could dynamically load objects.
  1530. // Alternatively, we could assume all objects could be cyclical and always hold onto
  1531. // them. We would have done it this way, except that legacy applications won't ever call
  1532. // CollectGarbage and that would cause them to leak everything they loaded even after calling
  1533. // ReleaseObject. That could be a better way (if we could detect legacy apps that don't
  1534. // call CollectGarbage) because it would avoid calling GC_HasCycle every time
  1535. // through ReleaseObject, which is (worst case) order N where N is the number of objects
  1536. // in the loader. In practice, this worst case only happens if all the objects are
  1537. // arranged in one big cycle.
  1538. pObject->m_pIDMObject->Release();
  1539. pObject->m_pIDMObject = NULL;
  1540. }
  1541. }
  1542. HRESULT CLoader::LoadCacheFile(WCHAR *pwzCacheFileName)
  1543. {
  1544. HRESULT hr = S_OK;
  1545. return hr;
  1546. }
  1547. #define FOURCC_LIST_CLASSLIST mmioFOURCC('c','l','s','l')
  1548. #define FOURCC_CLASSHEADER mmioFOURCC('c','l','s','h')
  1549. #define FOURCC_LIST_OBJLIST mmioFOURCC('o','b','j','l')
  1550. #define FOURCC_OBJHEADER mmioFOURCC('o','b','j','h')
  1551. HRESULT CLoader::SaveCacheFile(WCHAR *pwzCacheFileName)
  1552. {
  1553. HRESULT hr = E_OUTOFMEMORY;
  1554. MMCKINFO ckMain;
  1555. ZeroMemory(&ckMain, sizeof(MMCKINFO));
  1556. CFileStream *pStream = new CFileStream ( this );
  1557. if (pStream)
  1558. {
  1559. hr = pStream->Open(pwzCacheFileName,GENERIC_WRITE);
  1560. if (SUCCEEDED(hr))
  1561. {
  1562. IRIFFStream *pRiff;
  1563. hr = AllocRIFFStream(pStream, &pRiff );
  1564. if (SUCCEEDED(hr))
  1565. {
  1566. ckMain.fccType = FOURCC_RIFF_CACHE;
  1567. if( pRiff->CreateChunk( &ckMain, MMIO_CREATERIFF ) == S_OK)
  1568. {
  1569. MMCKINFO ckList;
  1570. ZeroMemory(&ckList, sizeof(MMCKINFO));
  1571. ckList.fccType = FOURCC_LIST_CLASSLIST;
  1572. if( pRiff->CreateChunk(&ckList, MMIO_CREATELIST) == S_OK )
  1573. {
  1574. CClass *pClass = m_ClassList.GetHead();
  1575. for (;pClass != NULL;pClass = pClass->GetNext())
  1576. {
  1577. hr = pClass->SaveToCache(pRiff);
  1578. if (FAILED(hr))
  1579. {
  1580. Trace(1, "Error: ScanDirectory encountered a seek error attempting to write to cache file %S.\n", pwzCacheFileName);
  1581. pRiff->Release();
  1582. pStream->Release();
  1583. return hr;
  1584. }
  1585. }
  1586. if( pRiff->Ascend( &ckList, 0 ) != S_OK )
  1587. {
  1588. Trace(1, "Error: ScanDirectory encountered a seek error attempting to write to cache file %S.\n", pwzCacheFileName);
  1589. hr = DMUS_E_CANNOTSEEK;
  1590. }
  1591. }
  1592. if( pRiff->Ascend( &ckMain, 0 ) != S_OK )
  1593. {
  1594. Trace(1, "Error: ScanDirectory encountered a seek error attempting to write to cache file %S.\n", pwzCacheFileName);
  1595. hr = DMUS_E_CANNOTSEEK;
  1596. }
  1597. }
  1598. pRiff->Release();
  1599. }
  1600. }
  1601. pStream->Release();
  1602. }
  1603. return hr;
  1604. }
  1605. // IDirectMusicIMA
  1606. STDMETHODIMP CLoader::LegacyCaching( BOOL fEnable)
  1607. {
  1608. m_fIMA = fEnable;
  1609. if (fEnable)
  1610. {
  1611. ScanDirectory(CLSID_DirectMusicStyle,L"sty",L"imafiles");
  1612. ScanDirectory(CLSID_DirectMusicChordMap,L"per",L"imafiles");
  1613. }
  1614. else
  1615. {
  1616. CClass *pClass;
  1617. CDescriptor Desc;
  1618. Desc.m_guidClass = CLSID_DirectMusicStyle;
  1619. Desc.m_dwValidData = DMUS_OBJ_CLASS;
  1620. EnterCriticalSection(&m_CriticalSection);
  1621. GetClass(&Desc,&pClass,FALSE);
  1622. if (pClass)
  1623. {
  1624. pClass->ClearObjects(TRUE,L"sty");
  1625. }
  1626. LeaveCriticalSection(&m_CriticalSection);
  1627. Desc.m_guidClass = CLSID_DirectMusicChordMap;
  1628. Desc.m_dwValidData = DMUS_OBJ_CLASS;
  1629. EnterCriticalSection(&m_CriticalSection);
  1630. GetClass(&Desc,&pClass,FALSE);
  1631. if (pClass)
  1632. {
  1633. pClass->ClearObjects(TRUE,L"per");
  1634. }
  1635. LeaveCriticalSection(&m_CriticalSection);
  1636. }
  1637. return S_OK;
  1638. }
  1639. #ifdef DBG
  1640. void CLoader::DebugTraceObject(DMUS_OBJECTDESC *pDesc)
  1641. {
  1642. WCHAR *polestrType = NULL;
  1643. if (pDesc->dwValidData & DMUS_OBJ_CLASS)
  1644. {
  1645. if (S_OK != ProgIDFromCLSID(pDesc->guidClass, &polestrType))
  1646. {
  1647. StringFromCLSID(pDesc->guidClass, &polestrType);
  1648. }
  1649. }
  1650. WCHAR *polestrGUID = NULL;
  1651. if (pDesc->dwValidData & DMUS_OBJ_OBJECT)
  1652. {
  1653. StringFromCLSID(pDesc->guidObject, &polestrGUID);
  1654. }
  1655. Trace(1,
  1656. " [file %S, name %S, type %S, guid %S]\n",
  1657. (pDesc->dwValidData & DMUS_OBJ_FILENAME) ? pDesc->wszFileName : L"??",
  1658. (pDesc->dwValidData & DMUS_OBJ_NAME) ? pDesc->wszName : L"??",
  1659. polestrType ? polestrType : L"??",
  1660. polestrGUID ? polestrGUID : L"??");
  1661. CoTaskMemFree(polestrType);
  1662. CoTaskMemFree(polestrGUID);
  1663. }
  1664. void CLoader::DebugTraceLoadFailure(CObject *pObject, HRESULT hrLoad)
  1665. {
  1666. if (!pObject)
  1667. {
  1668. assert(false);
  1669. return;
  1670. }
  1671. if (m_pLoadedObjectContext == m_pApplicationObject)
  1672. {
  1673. // This is the object loaded by the application. Print the warning if anything failed to load.
  1674. UINT iSize = m_vecdescDebugTraceLoadFailure.size();
  1675. if (iSize > 0)
  1676. {
  1677. DMUS_OBJECTDESC desc;
  1678. Zero(&desc);
  1679. pObject->m_ObjectDesc.Get(&desc);
  1680. Trace(1, "Load failure. While attempting to load the object\n");
  1681. DebugTraceObject(&desc);
  1682. Trace(1, "the following referenced objects could not be loaded:\n");
  1683. for (UINT i = 0; i < iSize; ++i)
  1684. {
  1685. DebugTraceObject(&m_vecdescDebugTraceLoadFailure[i]);
  1686. }
  1687. m_vecdescDebugTraceLoadFailure.Shrink(0);
  1688. }
  1689. }
  1690. else
  1691. {
  1692. // This is a referenced sub-object. Save the desciptor of the failed object in the next slot.
  1693. UINT uiNewPos = m_vecdescDebugTraceLoadFailure.size();
  1694. if (m_vecdescDebugTraceLoadFailure.AccessTo(uiNewPos))
  1695. {
  1696. DMUS_OBJECTDESC *pdesc = &m_vecdescDebugTraceLoadFailure[uiNewPos];
  1697. Zero(pdesc);
  1698. pObject->m_ObjectDesc.Get(pdesc);
  1699. }
  1700. }
  1701. }
  1702. #endif