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.

593 lines
13 KiB

  1. /*+
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. DelayWinMMCallback.cpp
  5. Abstract:
  6. This Shim does not allow the application to be called from inside the WINMM callback routine.
  7. Very few API's are supported inside the callback. The callback routine's data is stored away,
  8. and passed to the application inside the WM_TIMER callback.
  9. History:
  10. 05/11/2000 robkenny
  11. --*/
  12. #include "precomp.h"
  13. #include "CharVector.h"
  14. IMPLEMENT_SHIM_BEGIN(DelayWinMMCallback)
  15. #include "ShimHookMacro.h"
  16. APIHOOK_ENUM_BEGIN
  17. APIHOOK_ENUM_ENTRY(waveOutOpen)
  18. APIHOOK_ENUM_ENTRY(waveOutClose)
  19. APIHOOK_ENUM_ENTRY(SetTimer)
  20. APIHOOK_ENUM_END
  21. //---------------------------------------------------------------------------------------
  22. // Which device is currently inside the callback routine
  23. // NULL means nobody is inside the routine.
  24. static HWAVEOUT g_InsideCallback = NULL;
  25. typedef void CALLBACK WAVE_OUT_CALLBACK(
  26. HWAVEOUT hwo,
  27. UINT uMsg,
  28. DWORD dwInstance,
  29. DWORD dwParam1,
  30. DWORD dwParam2
  31. );
  32. //---------------------------------------------------------------------------------------
  33. /*+
  34. Just a convenient way of storing the WINMM callback data
  35. --*/
  36. class WinMMCallbackData
  37. {
  38. public:
  39. UINT m_uMsg;
  40. DWORD m_dwInstance;
  41. DWORD m_dwParam1;
  42. DWORD m_dwParam2;
  43. WinMMCallbackData(
  44. UINT uMsg,
  45. DWORD dwInstance,
  46. DWORD dwParam1,
  47. DWORD dwParam2
  48. )
  49. {
  50. m_uMsg = uMsg;
  51. m_dwInstance = dwInstance;
  52. m_dwParam1 = dwParam1;
  53. m_dwParam2 = dwParam2;
  54. }
  55. };
  56. /*
  57. Information particular to a single Wave Out device.
  58. --*/
  59. class WaveOutInfo
  60. {
  61. public:
  62. HWAVEOUT m_DeviceId;
  63. WAVE_OUT_CALLBACK * m_OrigCallback;
  64. VectorT<WinMMCallbackData> m_CallbackData;
  65. inline WaveOutInfo();
  66. inline bool operator == (const HWAVEOUT & deviceId) const;
  67. inline bool operator == (const WaveOutInfo & woi) const;
  68. void AddCallbackData(const WinMMCallbackData & callbackData);
  69. void CallCallbackRoutines();
  70. void ClearCallbackData();
  71. };
  72. inline WaveOutInfo::WaveOutInfo()
  73. {
  74. m_DeviceId = NULL;
  75. m_OrigCallback = NULL;
  76. }
  77. /*+
  78. Does this WaveOutInfo class have the same device id?
  79. --*/
  80. inline bool WaveOutInfo::operator == (const HWAVEOUT & deviceId) const
  81. {
  82. return deviceId == m_DeviceId;
  83. }
  84. /*+
  85. Are these two WaveOutInfo classes the same?
  86. --*/
  87. inline bool WaveOutInfo::operator == (const WaveOutInfo & woi) const
  88. {
  89. return woi.m_DeviceId == m_DeviceId;
  90. }
  91. /*+
  92. Add this callback data
  93. --*/
  94. void WaveOutInfo::AddCallbackData(const WinMMCallbackData & callbackData)
  95. {
  96. DPFN( eDbgLevelInfo, "AddCallbackData(0x%08x) uMsg(0x%08x).", m_DeviceId, callbackData.m_uMsg);
  97. m_CallbackData.Append(callbackData);
  98. }
  99. /*+
  100. Call the app with all the postponed WINMM callback data.
  101. --*/
  102. void WaveOutInfo::CallCallbackRoutines()
  103. {
  104. int nEntries = m_CallbackData.Size();
  105. for (int i = 0; i < nEntries; ++i)
  106. {
  107. WinMMCallbackData & callbackData = m_CallbackData.Get(i);
  108. if (m_OrigCallback)
  109. {
  110. DPFN(
  111. eDbgLevelInfo,
  112. "CallCallbackRoutines m_uMsg(0x08x) m_dwInstance(0x08x) m_dwParam1(0x08x) m_dwParam2(0x08x).",
  113. callbackData.m_uMsg,
  114. callbackData.m_dwInstance,
  115. callbackData.m_dwParam1,
  116. callbackData.m_dwParam2);
  117. (*m_OrigCallback)(
  118. m_DeviceId,
  119. callbackData.m_uMsg,
  120. callbackData.m_dwInstance,
  121. callbackData.m_dwParam1,
  122. callbackData.m_dwParam2);
  123. }
  124. }
  125. ClearCallbackData();
  126. }
  127. void WaveOutInfo::ClearCallbackData()
  128. {
  129. m_CallbackData.Reset();
  130. }
  131. //---------------------------------------------------------------------------------------
  132. /*+
  133. A vector of WaveOutInfo objects.
  134. Access to this list must be inside a critical section.
  135. --*/
  136. class WaveOutList : public VectorT<WaveOutInfo>
  137. {
  138. private:
  139. // Prevent copy
  140. WaveOutList(const WaveOutList & );
  141. WaveOutList & operator = (const WaveOutList & );
  142. private:
  143. static WaveOutList * TheWaveOutList;
  144. CRITICAL_SECTION TheWaveOutListLock;
  145. inline WaveOutList();
  146. inline ~WaveOutList();
  147. static WaveOutList * GetLocked();
  148. inline void Lock();
  149. inline void Unlock();
  150. void Dump();
  151. int FindWave(const HWAVEOUT & hwo) const;
  152. void ClearCallbackData();
  153. public:
  154. // All access to this class is through these static interfaces.
  155. // The app has no direct access to the list, therefore cannot accidentally
  156. // leave the list locked or unlocked.
  157. // All operations are Atomic.
  158. static void Add(const WaveOutInfo & woi);
  159. static void RemoveWaveOut(const HWAVEOUT & hwo);
  160. static void AddCallbackData(const HWAVEOUT & hwo, const WinMMCallbackData & callbackData);
  161. static void CallCallbackRoutines();
  162. };
  163. /*+
  164. A static pointer to the one-and-only wave out list.
  165. --*/
  166. WaveOutList * WaveOutList::TheWaveOutList = NULL;
  167. /*+
  168. Init the class
  169. --*/
  170. inline WaveOutList::WaveOutList()
  171. {
  172. InitializeCriticalSection(&TheWaveOutListLock);
  173. }
  174. /*+
  175. Clean up, releasing all resources.
  176. --*/
  177. inline WaveOutList::~WaveOutList()
  178. {
  179. DeleteCriticalSection(&TheWaveOutListLock);
  180. }
  181. /*+
  182. Enter the critical section
  183. --*/
  184. inline void WaveOutList::Lock()
  185. {
  186. EnterCriticalSection(&TheWaveOutListLock);
  187. }
  188. /*+
  189. Unlock the list
  190. --*/
  191. inline void WaveOutList::Unlock()
  192. {
  193. LeaveCriticalSection(&TheWaveOutListLock);
  194. }
  195. /*+
  196. Return a locked pointer to the list
  197. --*/
  198. WaveOutList * WaveOutList::GetLocked()
  199. {
  200. if (!TheWaveOutList)
  201. {
  202. TheWaveOutList = new WaveOutList;
  203. }
  204. if (TheWaveOutList)
  205. TheWaveOutList->Lock();
  206. return TheWaveOutList;
  207. }
  208. /*+
  209. Search for the member in the list, return index or -1
  210. --*/
  211. int WaveOutList::FindWave(const HWAVEOUT & findMe) const
  212. {
  213. for (int i = 0; i < Size(); ++i)
  214. {
  215. const WaveOutInfo & hwo = Get(i);
  216. if (Get(i) == findMe)
  217. return i;
  218. }
  219. return -1;
  220. }
  221. /*+
  222. Dump the list, caller is responsible for locking
  223. --*/
  224. void WaveOutList::Dump()
  225. {
  226. #if DBG
  227. for (int i = 0; i < Size(); ++i)
  228. {
  229. DPFN(
  230. eDbgLevelInfo,
  231. "TheWaveOutListLock[i] = HWO(%04d) CALLBACK(0x%08x).",
  232. i,
  233. Get(i).m_DeviceId,
  234. Get(i).m_OrigCallback);
  235. }
  236. #endif
  237. }
  238. /*+
  239. Add this wave out device to the global list.
  240. --*/
  241. void WaveOutList::Add(const WaveOutInfo & woi)
  242. {
  243. WaveOutList * waveOutList = WaveOutList::GetLocked();
  244. if (!waveOutList)
  245. return;
  246. int index = waveOutList->Find(woi);
  247. if (index == -1)
  248. waveOutList->Append(woi);
  249. #if DBG
  250. waveOutList->Dump();
  251. #endif
  252. // unlock the list
  253. waveOutList->Unlock();
  254. }
  255. /*+
  256. Remove the wavout entry with the specified wave out handle from the global list
  257. --*/
  258. void WaveOutList::RemoveWaveOut(const HWAVEOUT & hwo)
  259. {
  260. // Get a pointer to the locked list
  261. WaveOutList * waveOutList = WaveOutList::GetLocked();
  262. if (!waveOutList)
  263. return;
  264. // Look for our device and mark it for a reset.
  265. int woiIndex = waveOutList->FindWave(hwo);
  266. if (woiIndex >= 0)
  267. {
  268. waveOutList->Remove(woiIndex);
  269. #if DBG
  270. waveOutList->Dump();
  271. #endif
  272. }
  273. // unlock the list
  274. waveOutList->Unlock();
  275. }
  276. /*+
  277. Save this callback data for later.
  278. --*/
  279. void WaveOutList::AddCallbackData(const HWAVEOUT & hwo, const WinMMCallbackData & callbackData)
  280. {
  281. // Get a pointer to the locked list
  282. WaveOutList * waveOutList = WaveOutList::GetLocked();
  283. if (!waveOutList)
  284. return;
  285. // Look for our device and if it has a callback
  286. int woiIndex = waveOutList->FindWave(hwo);
  287. if (woiIndex >= 0)
  288. {
  289. WaveOutInfo & woi = waveOutList->Get(woiIndex);
  290. woi.AddCallbackData(callbackData);
  291. }
  292. // unlock the list
  293. waveOutList->Unlock();
  294. }
  295. /*+
  296. Clear the callback data for all our waveout devices.
  297. --*/
  298. void WaveOutList::ClearCallbackData()
  299. {
  300. int nEntries = Size();
  301. for (int i = 0; i < nEntries; ++i)
  302. {
  303. WaveOutInfo & woi = Get(i);
  304. woi.ClearCallbackData();
  305. }
  306. }
  307. /*+
  308. Get the callback value for this wave out device.
  309. --*/
  310. void WaveOutList::CallCallbackRoutines()
  311. {
  312. // Get a pointer to the locked list
  313. WaveOutList * waveOutList = WaveOutList::GetLocked();
  314. if (!waveOutList)
  315. return;
  316. // Quick exit if the list is empty.
  317. if (waveOutList->Size() == 0)
  318. {
  319. waveOutList->Unlock();
  320. return;
  321. }
  322. // We make a duplicate of the list because we cannot call back to the application while
  323. // the list is locked. If it is locked, the first WINMM callback will block attempting
  324. // to add data to the locked list.
  325. VectorT<WaveOutInfo> waveOutCallbackCopy = *waveOutList;
  326. // Remove the callback data from the original list
  327. waveOutList->ClearCallbackData();
  328. // unlock the list
  329. waveOutList->Unlock();
  330. DPFN(
  331. eDbgLevelInfo,
  332. "CallCallbackRoutines Start %d entries.",
  333. waveOutCallbackCopy.Size());
  334. int nEntries = waveOutCallbackCopy.Size();
  335. for (int i = 0; i < nEntries; ++i)
  336. {
  337. WaveOutInfo & woi = waveOutCallbackCopy.Get(i);
  338. woi.CallCallbackRoutines();
  339. }
  340. }
  341. //---------------------------------------------------------------------------------------
  342. /*+
  343. Our version of the WaveCallback routine, all this routine does is to store away
  344. the callback data, for later use..
  345. --*/
  346. void CALLBACK WaveOutCallback(
  347. HWAVEOUT hwo,
  348. UINT uMsg,
  349. DWORD dwInstance,
  350. DWORD dwParam1,
  351. DWORD dwParam2
  352. )
  353. {
  354. WaveOutList::AddCallbackData(hwo, WinMMCallbackData(uMsg, dwInstance, dwParam1, dwParam2));
  355. }
  356. /*+
  357. Call waveOutOpen, saving dwCallback if it is a function.
  358. --*/
  359. MMRESULT
  360. APIHOOK(waveOutOpen)(
  361. LPHWAVEOUT phwo,
  362. UINT uDeviceID,
  363. LPWAVEFORMATEX pwfx,
  364. DWORD dwCallback,
  365. DWORD dwCallbackInstance,
  366. DWORD fdwOpen
  367. )
  368. {
  369. WAVE_OUT_CALLBACK * myCallback = &WaveOutCallback;
  370. MMRESULT returnValue = ORIGINAL_API(waveOutOpen)(
  371. phwo,
  372. uDeviceID,
  373. pwfx,
  374. (DWORD)myCallback,
  375. dwCallbackInstance,
  376. fdwOpen);
  377. if (returnValue == MMSYSERR_NOERROR && (fdwOpen & CALLBACK_FUNCTION))
  378. {
  379. WaveOutInfo woi;
  380. woi.m_DeviceId = *phwo;
  381. woi.m_OrigCallback = (WAVE_OUT_CALLBACK *)dwCallback;
  382. WaveOutList::Add(woi);
  383. LOGN( eDbgLevelError, "waveOutOpen(%d,...) has callback. Added to list.", *phwo);
  384. }
  385. return returnValue;
  386. }
  387. /*+
  388. Call waveOutClose and forget the callback for the device.
  389. --*/
  390. MMRESULT
  391. APIHOOK(waveOutClose)(
  392. HWAVEOUT hwo
  393. )
  394. {
  395. LOGN( eDbgLevelError, "waveOutClose(%d) called. Remove callback from list.", hwo);
  396. WaveOutList::RemoveWaveOut(hwo);
  397. MMRESULT returnValue = ORIGINAL_API(waveOutClose)(hwo);
  398. return returnValue;
  399. }
  400. //--------------------------------------------------------------------------------------------
  401. //--------------------------------------------------------------------------------------------
  402. /*+
  403. Up to this point, this module is generic; that is going to change.
  404. The app necessating this fix uses the WM_TIMER message to pump the
  405. sound system, unfortunately the timer can occur while the game is
  406. inside the WINMM callback routine, causing a deadlock occurs when this timer
  407. callback calls a WINMM routine.
  408. --*/
  409. static TIMERPROC g_OrigTimerCallback = NULL;
  410. VOID CALLBACK TimerCallback(
  411. HWND hwnd, // handle to window
  412. UINT uMsg, // WM_TIMER message
  413. UINT_PTR idEvent, // timer identifier
  414. DWORD dwTime // current system time
  415. )
  416. {
  417. if (g_OrigTimerCallback)
  418. {
  419. // Pass all the delayed WINMM timer callback data
  420. WaveOutList::CallCallbackRoutines();
  421. // Now call the original callback routine.
  422. (*g_OrigTimerCallback)(hwnd, uMsg, idEvent, dwTime);
  423. }
  424. }
  425. /*+
  426. Substitute our timer routine for theirs.
  427. --*/
  428. UINT_PTR
  429. APIHOOK(SetTimer)(
  430. HWND hWnd, // handle to window
  431. UINT_PTR nIDEvent, // timer identifier
  432. UINT uElapse, // time-out value
  433. TIMERPROC lpTimerFunc // timer procedure
  434. )
  435. {
  436. g_OrigTimerCallback = lpTimerFunc;
  437. LOGN( eDbgLevelError, "SetTimer called. Substitute our timer routine for theirs.");
  438. UINT_PTR returnValue = ORIGINAL_API(SetTimer)(hWnd, nIDEvent, uElapse, TimerCallback);
  439. return returnValue;
  440. }
  441. /*+
  442. Register hooked functions
  443. --*/
  444. HOOK_BEGIN
  445. APIHOOK_ENTRY(WINMM.DLL, waveOutOpen)
  446. APIHOOK_ENTRY(WINMM.DLL, waveOutClose)
  447. APIHOOK_ENTRY(USER32.DLL, SetTimer)
  448. HOOK_END
  449. IMPLEMENT_SHIM_END