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.

485 lines
17 KiB

  1. // Copyright (c) 1999 Microsoft Corporation. All rights reserved.
  2. //
  3. // Simple helper classes that use the "resource acquisition is initialization" technique.
  4. // In English, this means they acquire a reference to a resource and the resource is automatically
  5. // released in the destructor.
  6. //
  7. // This is particularly helpful if you are using exception handling or simulating it (painfully)
  8. // in OLE by putting "if (FAILED(hr)) return;" after every function call. In such circumstances
  9. // it simpler if you know that the resource will be automatically freed no matter how you end
  10. // up exiting the function.
  11. // Since we're not using exception handling, these classes do not throw. If resource acquisition
  12. // could fail, be sure to check for the error before using the resource!
  13. // Sometimes these helper classes are more than just simple wrappers for freeing the resource.
  14. // They may also provide useful methods that make it easier to perform operations on the resource.
  15. // Everything here is enclosed in the SmartRef namespace. Thus you can't refer to CritSec directly.
  16. // Instead you must say SmartRef::CritSec.
  17. #pragma once
  18. #include "debug.h"
  19. #include "mmsystem.h"
  20. #include "dmstrm.h"
  21. #include "dmerror.h"
  22. #include "dmusici.h"
  23. // Place this in the private: section of a class to prevent use of the default C++ copy and assignment.
  24. // Creates an error if someone later tries to use the automatic member-by-member copy that would be incorrect.
  25. // Use this if you don't do the work to implement correct copying or if copying doesn't make sense for this class.
  26. #define NOCOPYANDASSIGN(classname) classname(const classname &o); classname &operator= (const classname &o);
  27. namespace SmartRef
  28. {
  29. // Enters a critical section on contruction. Leaves on destruction.
  30. class CritSec
  31. {
  32. public:
  33. CritSec(CRITICAL_SECTION *pCriticalSection) : m_pCriticalSection(pCriticalSection) { EnterCriticalSection(m_pCriticalSection); }
  34. ~CritSec() { LeaveCriticalSection(m_pCriticalSection); }
  35. private:
  36. NOCOPYANDASSIGN(CritSec)
  37. CRITICAL_SECTION *m_pCriticalSection;
  38. };
  39. // Makes a copy of an ANSI string and frees it on destruction.
  40. // hungarian: astr
  41. class AString
  42. {
  43. public:
  44. AString(const WCHAR *psz) : m_psz(NULL) { this->AssignFromW(psz); }
  45. AString(const char *psz = NULL) : m_psz(NULL) { *this = psz; }
  46. AString(const char *psz, UINT cch); // take first cch characters of psz
  47. AString(const AString &str) : m_psz(NULL) { *this = str.m_psz; }
  48. ~AString() { *this = NULL; }
  49. operator const char *() const { return m_psz; }
  50. AString &operator= (const char *psz);
  51. AString &operator= (const AString &str) { return *this = str.m_psz; }
  52. AString &Assign(const char *psz, UINT cch); // take first cch characters of psz
  53. AString &AssignFromW(const WCHAR *psz);
  54. char ** operator& () { assert(!m_psz); return &m_psz; } // allows direct setting of psz, adopting a string without copying it
  55. private:
  56. char *m_psz;
  57. };
  58. // Same as AString for Unicode strings.
  59. // Also accepts ANSI strings, converting them to Unicode.
  60. // hungarian: wstr
  61. class WString
  62. {
  63. public:
  64. WString(const char *psz) : m_psz(NULL) { this->AssignFromA(psz); }
  65. WString(const WCHAR *psz = NULL) : m_psz(NULL) { *this = psz; }
  66. WString(const WCHAR *psz, UINT cch) : m_psz(NULL) { this->Assign(psz, cch); }
  67. WString(const WString &str) : m_psz(NULL) { *this = str.m_psz; }
  68. ~WString() { *this = static_cast<WCHAR *>(NULL); }
  69. operator const WCHAR *() const { return m_psz; }
  70. WString &operator= (const WCHAR *psz);
  71. WString &operator= (const WString &str) { return *this = str.m_psz; }
  72. WString &Assign(const WCHAR *psz, UINT cch); // take first cch characters of psz
  73. WString &AssignFromA(const char *psz);
  74. WCHAR ** operator& () { assert(!m_psz); return &m_psz; } // allows direct setting of psz, adopting a string without copying it
  75. private:
  76. WCHAR *m_psz;
  77. };
  78. // Allocates a writable buffer of a fixed size and frees it on destruction.
  79. // (For example, you could use a Buffer<char> to write a string into.)
  80. // hungarian: buf prefixed by type
  81. // use abuf for Buffer<char> and wbuf for Buffer<WCHAR>
  82. template<class T>
  83. class Buffer
  84. {
  85. public:
  86. Buffer(UINT uiSize) { m_p = new T[uiSize + 1]; }
  87. ~Buffer() { delete[] m_p; }
  88. operator T *() { return m_p; }
  89. // use to defer allocation (say, if you don't know the size at the declaration)
  90. Buffer() : m_p(NULL) {}
  91. void Alloc(UINT uiSize) { delete[] m_p; m_p = new T[uiSize + 1]; }
  92. T* disown() { T *_p = m_p; m_p = NULL; return _p; }
  93. T** operator& () { assert(!m_p); return &m_p; } // allows direct setting of m_p, adopting a string without copying it
  94. private:
  95. NOCOPYANDASSIGN(Buffer)
  96. T *m_p;
  97. };
  98. // Holds an array that grows automatically.
  99. // Doesn't throw so you must call AccessTo before using a position that might have required
  100. // reallocation to ensure that memory didn't run out.
  101. // Values held in the vector must have value semantics so that they can be copied freely
  102. // into reallocated memory slots.
  103. // hungarian: vec prefixed by type
  104. // of just use svec (for smart vector) without specifying the type
  105. // use avec for Vector<char> and wvec for Vector<WCHAR>
  106. template<class T>
  107. class Vector
  108. {
  109. public:
  110. Vector() : m_pT(NULL), m_size(0), m_capacity(0) {}
  111. ~Vector() { delete[] m_pT; }
  112. UINT size() { return m_size; }
  113. operator bool() { return m_fFail; }
  114. bool AccessTo(UINT uiPos) { return Grow(uiPos + 1); }
  115. T& operator[](UINT uiPos) { assert(uiPos < m_size); return m_pT[uiPos]; }
  116. T* GetArray() { return m_pT; } // Danger: only use when needed and don't write past the end.
  117. void Shrink(UINT uiNewSize) { m_size = uiNewSize; } // Semantically shrinks -- doesn't actually free up any memory
  118. private:
  119. NOCOPYANDASSIGN(Vector)
  120. bool Grow(UINT size)
  121. {
  122. if (size > m_size)
  123. {
  124. if (size > m_capacity)
  125. {
  126. for (UINT capacity = m_capacity ? m_capacity : 1;
  127. capacity < size;
  128. capacity *= 2)
  129. {}
  130. T *pT = new T[capacity];
  131. if (!pT)
  132. return false;
  133. for (UINT i = 0; i < m_size; ++i)
  134. pT[i] = m_pT[i];
  135. delete[] m_pT;
  136. m_pT = pT;
  137. m_capacity = capacity;
  138. }
  139. m_size = size;
  140. }
  141. return true;
  142. }
  143. T *m_pT;
  144. UINT m_size;
  145. UINT m_capacity;
  146. };
  147. // Standard stack abstract data type.
  148. // Values held in the stack must have value semantics so that they can be copied freely
  149. // into reallocated memory slots.
  150. // hungarian: stack prefixed by type
  151. template<class T>
  152. class Stack
  153. {
  154. public:
  155. Stack() : iTop(-1) {}
  156. bool empty() { return iTop < 0; }
  157. HRESULT push(const T& t) { if (!m_vec.AccessTo(iTop + 1)) return E_OUTOFMEMORY; m_vec[++iTop] = t; return S_OK; }
  158. T top() { if (empty()) {assert(false); return T();} return m_vec[iTop]; }
  159. void pop() { if (empty()) {assert(false); return;} --iTop; }
  160. private:
  161. Vector<T> m_vec;
  162. int iTop;
  163. };
  164. // Lookup table that maps keys to values. Grows automatically as needed.
  165. // Type K (keys) must support operator =, operator ==. and a Hash function that returns an int.
  166. // Type V must support operator =.
  167. template <class K, class V>
  168. class Hash
  169. {
  170. public:
  171. Hash(HRESULT *phr, int iInitialSize = 2) : m_p(NULL), m_iCapacity(0), m_iSize(0) { *phr = Grow(iInitialSize); }
  172. ~Hash() { delete[] m_p; }
  173. struct entry
  174. {
  175. V v;
  176. bool fFound() { return iHash != -1; }
  177. private:
  178. // only let the hash make them
  179. friend class Hash<K, V>;
  180. entry() : iHash(-1) {};
  181. entry(const entry &o); // disallowed copy constructor
  182. int iHash;
  183. K k;
  184. };
  185. entry &Find(K k) // if iHash is -1 then it wasn't found and you may immediately add the entry using Add().
  186. {
  187. assert(m_p);
  188. return HashTo(k.Hash(), k, m_p, m_iCapacity);
  189. }
  190. // Warning: no intervening additions may have occurred between the time e was returned by Find and the time Add(e, ...) is called.
  191. // Also k must be the same in both calls. If you want to be crafty, "same" can be replaced with equivalence in terms of Hash and operator==.
  192. HRESULT Add(entry &e, K k, V v)
  193. {
  194. assert(!e.fFound());
  195. assert(&e == &Find(k));
  196. e.v = v;
  197. e.iHash = k.Hash();
  198. e.k = k;
  199. ++m_iSize;
  200. if (m_iSize * 2 > m_iCapacity)
  201. return Grow(m_iCapacity * 2);
  202. return S_OK;
  203. }
  204. V &operator[](K k)
  205. {
  206. entry &e = Find(k);
  207. assert(e.fFound());
  208. return e.v;
  209. }
  210. private:
  211. HRESULT Grow(int iCapacity)
  212. {
  213. #ifdef DBG
  214. // size must be at least 2 and a power of 2
  215. for (int iCheckSize = iCapacity; !(iCheckSize & 1); iCheckSize >>= 1)
  216. {}
  217. assert(iCapacity > 1 && iCheckSize == 1);
  218. #endif
  219. // alloc new table
  220. entry *p = new entry[iCapacity];
  221. if (!p)
  222. {
  223. delete[] m_p;
  224. return E_OUTOFMEMORY;
  225. }
  226. // rehash everything into the larger table
  227. for (int i = 0; i < m_iCapacity; ++i)
  228. {
  229. entry &eSrc = m_p[i];
  230. if (eSrc.iHash != -1)
  231. {
  232. entry &eDst = HashTo(eSrc.iHash, eSrc.k, p, iCapacity);
  233. assert(eDst.iHash == -1);
  234. eDst = eSrc;
  235. }
  236. }
  237. delete[] m_p;
  238. m_p = p;
  239. m_iCapacity = iCapacity;
  240. return S_OK;
  241. }
  242. entry &HashTo(int iHash, K k, entry *p, int iCapacity)
  243. {
  244. // initial hash using modulus, then jump three slots at a time (3 is guaranteed to take us to all slots because capacity is a power of 2)
  245. assert(iHash >= 0);
  246. for (int i = iHash % iCapacity;
  247. p[i].iHash != -1 && (p[i].iHash != iHash || !(p[i].k == k)); // rehash while slot occupied or it doesn't match
  248. i = (i + 3) % iCapacity)
  249. {}
  250. return p[i];
  251. }
  252. entry *m_p;
  253. int m_iCapacity;
  254. int m_iSize;
  255. };
  256. // Holds the supplied pointer and frees it on destruction.
  257. // hungarian: sp (smart pointer)
  258. template <class T>
  259. class Ptr
  260. {
  261. public:
  262. Ptr(T *_p) : p(_p) {}
  263. ~Ptr() { delete p; }
  264. operator T*() { return p; }
  265. T *operator->() { return p; }
  266. T* disown() { T *_p = p; p = NULL; return _p; }
  267. private:
  268. NOCOPYANDASSIGN(Ptr)
  269. T* p;
  270. };
  271. // Holds the supplied pointer to an array and frees it (with delete[]) on destruction.
  272. // hungarian: sprg
  273. template <class T>
  274. class PtrArray
  275. {
  276. public:
  277. PtrArray(T *_p) : p(_p) {}
  278. ~PtrArray() { delete[] p; }
  279. operator T*() { return p; }
  280. T* disown() { T *_p = p; p = NULL; return _p; }
  281. private:
  282. NOCOPYANDASSIGN(PtrArray)
  283. T* p;
  284. };
  285. // Holds the supplied COM interface and releases it on destruction.
  286. // hungarian: scom
  287. template <class T>
  288. class ComPtr
  289. {
  290. public:
  291. ComPtr(T *_p = NULL) : p(_p) {}
  292. ~ComPtr() { *this = NULL; }
  293. operator T*() { return p; }
  294. T* operator-> () { assert(p); return p; }
  295. ComPtr &operator= (T *_p) { if (p) p->Release(); p = _p; return *this; }
  296. T** operator& () { assert(!p); return &p; }
  297. void Release() { *this = NULL; }
  298. T* disown() { T *_p = p; p = NULL; return _p; }
  299. private:
  300. T* p;
  301. };
  302. // Holds the supplied registry key handle and closes it on destruction.
  303. // hungarian: shkey
  304. class HKey
  305. {
  306. public:
  307. HKey(HKEY hkey = NULL) : m_hkey(hkey) {}
  308. ~HKey() { *this = NULL; }
  309. HKey &operator= (HKEY hkey) { if (m_hkey) ::RegCloseKey(m_hkey); m_hkey = hkey; return *this; }
  310. HKEY *operator& () { assert(!m_hkey); return &m_hkey; }
  311. operator HKEY() { return m_hkey; }
  312. private:
  313. NOCOPYANDASSIGN(HKey)
  314. HKEY m_hkey;
  315. };
  316. // Allocates and clears a one of the DMUS_*_PMSG structures. You fill out its fields
  317. // and then call StampAndSend. The message is automatically cleared after a successful
  318. // send or freed on destruction. Be sure the check the hr function for failures.
  319. // hungarian: pmsg
  320. template <class T>
  321. class PMsg
  322. {
  323. public:
  324. T *p; // pointer to the message structure -- use to set the fields before sending
  325. PMsg(IDirectMusicPerformance *pPerf, UINT cbExtra = 0) // use cbExtra to allocate extra space in the structure, such as for DMUS_SYSEX_PMSG or DMUS_LYRIC_PMSG
  326. : m_pPerf(pPerf), m_hr(S_OK), p(NULL)
  327. {
  328. const UINT cb = sizeof(T) + cbExtra;
  329. m_hr = m_pPerf->AllocPMsg(cb, reinterpret_cast<DMUS_PMSG**>(&p));
  330. if (SUCCEEDED(m_hr))
  331. {
  332. assert(p->dwSize == cb);
  333. ZeroMemory(p, cb);
  334. p->dwSize = cb;
  335. }
  336. }
  337. ~PMsg() { if (p) m_pPerf->FreePMsg(reinterpret_cast<DMUS_PMSG*>(p)); }
  338. void StampAndSend(IDirectMusicGraph *pGraph)
  339. {
  340. m_hr = pGraph->StampPMsg(reinterpret_cast<DMUS_PMSG*>(p));
  341. if (FAILED(m_hr))
  342. return;
  343. m_hr = m_pPerf->SendPMsg(reinterpret_cast<DMUS_PMSG*>(p));
  344. if (SUCCEEDED(m_hr))
  345. p = NULL; // PMsg now owned by the performance
  346. }
  347. HRESULT hr() { return m_hr; }
  348. private:
  349. NOCOPYANDASSIGN(PMsg)
  350. IDirectMusicPerformance *m_pPerf; // weak ref
  351. HRESULT m_hr;
  352. };
  353. // Walks through the RIFF file structure held in a stream. Releases it on destruction.
  354. // Although I found this to be quite useful, it a bit complicated. You should look over
  355. // the source or step through some examples before you use it. Although I'm not positive
  356. // this wouldn't work, it is not designed to have multiple RiffIter's walking over the
  357. // same stream at once (see note in Descend).
  358. // hungarian: ri
  359. class RiffIter
  360. {
  361. public:
  362. enum RiffType { Riff, List, Chunk };
  363. RiffIter(IStream *pStream);
  364. ~RiffIter();
  365. RiffIter &operator ++();
  366. RiffIter &Find(RiffType t, FOURCC id);
  367. HRESULT FindRequired(RiffType t, FOURCC id, HRESULT hrOnNotFound) { if (Find(t, id)) return S_OK; HRESULT _hr = hr(); return SUCCEEDED(_hr) ? hrOnNotFound : _hr; } // Attempts to find the expected chunk. Returns S_OK if found, an error code if there was a problem reading, and hrOnNotFound if reading worked OK but the chunk simply wasn't there.
  368. // With Descend, use the returned iterator to process the children before resuming use of the parent. Using both at once won't work.
  369. RiffIter Descend() { validate(); return RiffIter(*this, m_ckChild); }
  370. operator bool() const { return SUCCEEDED(m_hr); }
  371. HRESULT hr() const { return (m_hr == DMUS_E_DESCEND_CHUNK_FAIL) ? S_OK : m_hr; }
  372. RiffType type() const { validate(); return (m_ckChild.ckid == FOURCC_LIST) ? List : ((m_ckChild.ckid == FOURCC_RIFF) ? Riff : Chunk); }
  373. FOURCC id() const { validate(); return (type() == Chunk) ? m_ckChild.ckid : m_ckChild.fccType; }
  374. DWORD size() const { validate(); assert(type() == Chunk); return m_ckChild.cksize; }
  375. HRESULT ReadChunk(void *pv, UINT cb);
  376. HRESULT ReadArrayChunk(DWORD cbSize, void **ppv, int *pcRecords); // Reads an array chunk that is an array of records where the first DWORD gives the size of the records. The records are copied into an array of records of size dwSize (filling with zero if the actual records in the file are smaller and ignoring additional fields if the actual records are larger). ppv is set to return a pointer to this array, which the caller now owns and must delete. pcRecords is set to the number of records returned.
  377. // Find the chunk (or return hrOnNoteFound). Load an object embedded in the stream. Then leaves the iterator on the next chunk.
  378. HRESULT FindAndGetEmbeddedObject(RiffType t, FOURCC id, HRESULT hrOnNotFound, IDirectMusicLoader *pLoader, REFCLSID rclsid, REFIID riid, LPVOID *ppv);
  379. // read specific RIFF structures
  380. HRESULT ReadReference(DMUS_OBJECTDESC *pDESC); // no need to init (zero, set size) the passed descriptor before calling
  381. HRESULT LoadReference(IDirectMusicLoader *pIDMLoader, const IID &iid, void **ppvObject)
  382. {
  383. DMUS_OBJECTDESC desc;
  384. HRESULT hr = ReadReference(&desc);
  385. if(SUCCEEDED(hr))
  386. hr = pIDMLoader->GetObject(&desc, iid, ppvObject);
  387. return hr;
  388. }
  389. struct ObjectInfo
  390. {
  391. ObjectInfo() { Clear(); }
  392. void Clear() { wszName[0] = L'\0'; guid = GUID_NULL; vVersion.dwVersionMS = 0; vVersion.dwVersionLS = 0; }
  393. WCHAR wszName[DMUS_MAX_NAME];
  394. GUID guid;
  395. DMUS_VERSION vVersion;
  396. };
  397. HRESULT LoadObjectInfo(ObjectInfo *pObjInfo, RiffType rtypeStop, FOURCC ridStop); // No need to init/zero. Reads from <guid-ck>, <vers-ck>, and <UNFO-list>/<UNAM-ck>. Stops at rtypeStop/ridStop, or returns E_FAIL if not found.
  398. HRESULT ReadText(WCHAR **ppwsz); // allocates a buffer and reads the current chunk--a NULL-terminated Unicode string--into it
  399. HRESULT ReadTextTrunc(WCHAR *pwsz, UINT cbBufSize); // reads only as much as it can fit in the buffer with a terminator
  400. // This is deliberately placed in the public section but never implemented in order to allow statements such as:
  401. // SmartRef::RiffIter riChild = ri.Descend();
  402. // But it is never defined to prevent someone from trying to actually make two copies of a riffiter and then use them, which is not supported.
  403. // This would yield an unresolved symbol error:
  404. // SmartRef::RiffIter riError = ri;
  405. // We don't allow general copying of RiffIters. Only used to get the return value of Descend, where it is optimized away.
  406. RiffIter(const RiffIter &o);
  407. private:
  408. RiffIter &operator= (const RiffIter &o); // Also never defined -- don't allow assignment
  409. RiffIter(const RiffIter &other, MMCKINFO ckParent);
  410. bool validate() const { if (FAILED(m_hr)) { assert(false); return true; } else return false; }
  411. HRESULT m_hr;
  412. IStream *m_pIStream;
  413. IDMStream *m_pIDMStream;
  414. bool m_fParent;
  415. MMCKINFO m_ckParent;
  416. MMCKINFO m_ckChild;
  417. };
  418. // Templated ReadChunk typed helpers (templated member function wasn't working for me on current version of compiler)
  419. template <class T> HRESULT RiffIterReadChunk(RiffIter &ri, T *pT) { return ri.ReadChunk(pT, sizeof(*pT)); }
  420. template <class T> HRESULT RiffIterReadArrayChunk(RiffIter &ri, T **ppT, int *pcRecords) { return ri.ReadArrayChunk(sizeof(T), reinterpret_cast<void**>(ppT), pcRecords); }
  421. }; // namespace SmartRef