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.

603 lines
14 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. WaveOutList() {}
  146. static WaveOutList * GetLocked();
  147. inline void Lock();
  148. inline void Unlock();
  149. BOOL Init();
  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 BOOL Create();
  159. static void Add(const WaveOutInfo & woi);
  160. static void RemoveWaveOut(const HWAVEOUT & hwo);
  161. static void AddCallbackData(const HWAVEOUT & hwo, const WinMMCallbackData & callbackData);
  162. static void CallCallbackRoutines();
  163. };
  164. /*+
  165. A static pointer to the one-and-only wave out list.
  166. --*/
  167. WaveOutList * WaveOutList::TheWaveOutList = NULL;
  168. /*+
  169. Init the class
  170. --*/
  171. BOOL WaveOutList::Create()
  172. {
  173. TheWaveOutList = new WaveOutList;
  174. return TheWaveOutList->Init();
  175. }
  176. BOOL WaveOutList::Init()
  177. {
  178. // Preallocate the event, prevents EnterCriticalSection
  179. // from throwing an exception in low-memory situations.
  180. return InitializeCriticalSectionAndSpinCount(&TheWaveOutListLock, 0x8000000);
  181. }
  182. /*+
  183. Enter the critical section
  184. --*/
  185. inline void WaveOutList::Lock()
  186. {
  187. EnterCriticalSection(&TheWaveOutListLock);
  188. }
  189. /*+
  190. Unlock the list
  191. --*/
  192. inline void WaveOutList::Unlock()
  193. {
  194. LeaveCriticalSection(&TheWaveOutListLock);
  195. }
  196. /*+
  197. Return a locked pointer to the list
  198. --*/
  199. WaveOutList * WaveOutList::GetLocked()
  200. {
  201. TheWaveOutList->Lock();
  202. return TheWaveOutList;
  203. }
  204. /*+
  205. Search for the member in the list, return index or -1
  206. --*/
  207. int WaveOutList::FindWave(const HWAVEOUT & findMe) const
  208. {
  209. for (int i = 0; i < Size(); ++i)
  210. {
  211. const WaveOutInfo & hwo = Get(i);
  212. if (hwo == findMe)
  213. return i;
  214. }
  215. return -1;
  216. }
  217. /*+
  218. Dump the list, caller is responsible for locking
  219. --*/
  220. void WaveOutList::Dump()
  221. {
  222. #if DBG
  223. for (int i = 0; i < Size(); ++i)
  224. {
  225. DPFN(
  226. eDbgLevelInfo,
  227. "TheWaveOutListLock[i] = HWO(%04d) CALLBACK(0x%08x).",
  228. i,
  229. Get(i).m_DeviceId,
  230. Get(i).m_OrigCallback);
  231. }
  232. #endif
  233. }
  234. /*+
  235. Add this wave out device to the global list.
  236. --*/
  237. void WaveOutList::Add(const WaveOutInfo & woi)
  238. {
  239. WaveOutList * waveOutList = WaveOutList::GetLocked();
  240. if (!waveOutList)
  241. return;
  242. int index = waveOutList->Find(woi);
  243. if (index == -1)
  244. waveOutList->Append(woi);
  245. #if DBG
  246. waveOutList->Dump();
  247. #endif
  248. // unlock the list
  249. waveOutList->Unlock();
  250. }
  251. /*+
  252. Remove the wavout entry with the specified wave out handle from the global list
  253. --*/
  254. void WaveOutList::RemoveWaveOut(const HWAVEOUT & hwo)
  255. {
  256. // Get a pointer to the locked list
  257. WaveOutList * waveOutList = WaveOutList::GetLocked();
  258. if (!waveOutList)
  259. return;
  260. // Look for our device and mark it for a reset.
  261. int woiIndex = waveOutList->FindWave(hwo);
  262. if (woiIndex >= 0)
  263. {
  264. waveOutList->Remove(woiIndex);
  265. #if DBG
  266. waveOutList->Dump();
  267. #endif
  268. }
  269. // unlock the list
  270. waveOutList->Unlock();
  271. }
  272. /*+
  273. Save this callback data for later.
  274. --*/
  275. void WaveOutList::AddCallbackData(const HWAVEOUT & hwo, const WinMMCallbackData & callbackData)
  276. {
  277. // Get a pointer to the locked list
  278. WaveOutList * waveOutList = WaveOutList::GetLocked();
  279. if (!waveOutList)
  280. return;
  281. // Look for our device and if it has a callback
  282. int woiIndex = waveOutList->FindWave(hwo);
  283. if (woiIndex >= 0)
  284. {
  285. WaveOutInfo & woi = waveOutList->Get(woiIndex);
  286. woi.AddCallbackData(callbackData);
  287. }
  288. // unlock the list
  289. waveOutList->Unlock();
  290. }
  291. /*+
  292. Clear the callback data for all our waveout devices.
  293. --*/
  294. void WaveOutList::ClearCallbackData()
  295. {
  296. int nEntries = Size();
  297. for (int i = 0; i < nEntries; ++i)
  298. {
  299. WaveOutInfo & woi = Get(i);
  300. woi.ClearCallbackData();
  301. }
  302. }
  303. /*+
  304. Get the callback value for this wave out device.
  305. --*/
  306. void WaveOutList::CallCallbackRoutines()
  307. {
  308. // Get a pointer to the locked list
  309. WaveOutList * waveOutList = WaveOutList::GetLocked();
  310. if (!waveOutList)
  311. return;
  312. // Quick exit if the list is empty.
  313. if (waveOutList->Size() == 0)
  314. {
  315. waveOutList->Unlock();
  316. return;
  317. }
  318. // We make a duplicate of the list because we cannot call back to the application while
  319. // the list is locked. If it is locked, the first WINMM callback will block attempting
  320. // to add data to the locked list.
  321. VectorT<WaveOutInfo> waveOutCallbackCopy = *waveOutList;
  322. // Remove the callback data from the original list
  323. waveOutList->ClearCallbackData();
  324. // unlock the list
  325. waveOutList->Unlock();
  326. DPFN(
  327. eDbgLevelInfo,
  328. "CallCallbackRoutines Start %d entries.",
  329. waveOutCallbackCopy.Size());
  330. int nEntries = waveOutCallbackCopy.Size();
  331. for (int i = 0; i < nEntries; ++i)
  332. {
  333. WaveOutInfo & woi = waveOutCallbackCopy.Get(i);
  334. woi.CallCallbackRoutines();
  335. }
  336. }
  337. //---------------------------------------------------------------------------------------
  338. /*+
  339. Our version of the WaveCallback routine, all this routine does is to store away
  340. the callback data, for later use..
  341. --*/
  342. void CALLBACK WaveOutCallback(
  343. HWAVEOUT hwo,
  344. UINT uMsg,
  345. DWORD dwInstance,
  346. DWORD dwParam1,
  347. DWORD dwParam2
  348. )
  349. {
  350. WaveOutList::AddCallbackData(hwo, WinMMCallbackData(uMsg, dwInstance, dwParam1, dwParam2));
  351. }
  352. /*+
  353. Call waveOutOpen, saving dwCallback if it is a function.
  354. --*/
  355. MMRESULT
  356. APIHOOK(waveOutOpen)(
  357. LPHWAVEOUT phwo,
  358. UINT uDeviceID,
  359. LPWAVEFORMATEX pwfx,
  360. DWORD dwCallback,
  361. DWORD dwCallbackInstance,
  362. DWORD fdwOpen
  363. )
  364. {
  365. WAVE_OUT_CALLBACK * myCallback = &WaveOutCallback;
  366. MMRESULT returnValue = ORIGINAL_API(waveOutOpen)(
  367. phwo,
  368. uDeviceID,
  369. pwfx,
  370. (DWORD)myCallback,
  371. dwCallbackInstance,
  372. fdwOpen);
  373. if (returnValue == MMSYSERR_NOERROR && (fdwOpen & CALLBACK_FUNCTION))
  374. {
  375. WaveOutInfo woi;
  376. woi.m_DeviceId = *phwo;
  377. woi.m_OrigCallback = (WAVE_OUT_CALLBACK *)dwCallback;
  378. WaveOutList::Add(woi);
  379. LOGN( eDbgLevelError, "waveOutOpen(%d,...) has callback. Added to list.", *phwo);
  380. }
  381. return returnValue;
  382. }
  383. /*+
  384. Call waveOutClose and forget the callback for the device.
  385. --*/
  386. MMRESULT
  387. APIHOOK(waveOutClose)(
  388. HWAVEOUT hwo
  389. )
  390. {
  391. LOGN( eDbgLevelError, "waveOutClose(%d) called. Remove callback from list.", hwo);
  392. WaveOutList::RemoveWaveOut(hwo);
  393. MMRESULT returnValue = ORIGINAL_API(waveOutClose)(hwo);
  394. return returnValue;
  395. }
  396. //--------------------------------------------------------------------------------------------
  397. //--------------------------------------------------------------------------------------------
  398. /*+
  399. Up to this point, this module is generic; that is going to change.
  400. The app necessating this fix uses the WM_TIMER message to pump the
  401. sound system, unfortunately the timer can occur while the game is
  402. inside the WINMM callback routine, causing a deadlock occurs when this timer
  403. callback calls a WINMM routine.
  404. --*/
  405. static TIMERPROC g_OrigTimerCallback = NULL;
  406. VOID CALLBACK TimerCallback(
  407. HWND hwnd, // handle to window
  408. UINT uMsg, // WM_TIMER message
  409. UINT_PTR idEvent, // timer identifier
  410. DWORD dwTime // current system time
  411. )
  412. {
  413. if (g_OrigTimerCallback)
  414. {
  415. // Pass all the delayed WINMM timer callback data
  416. WaveOutList::CallCallbackRoutines();
  417. // Now call the original callback routine.
  418. (*g_OrigTimerCallback)(hwnd, uMsg, idEvent, dwTime);
  419. }
  420. }
  421. /*+
  422. Substitute our timer routine for theirs.
  423. --*/
  424. UINT_PTR
  425. APIHOOK(SetTimer)(
  426. HWND hWnd, // handle to window
  427. UINT_PTR nIDEvent, // timer identifier
  428. UINT uElapse, // time-out value
  429. TIMERPROC lpTimerFunc // timer procedure
  430. )
  431. {
  432. g_OrigTimerCallback = lpTimerFunc;
  433. LOGN( eDbgLevelError, "SetTimer called. Substitute our timer routine for theirs.");
  434. UINT_PTR returnValue = ORIGINAL_API(SetTimer)(hWnd, nIDEvent, uElapse, TimerCallback);
  435. return returnValue;
  436. }
  437. BOOL
  438. NOTIFY_FUNCTION(
  439. DWORD fdwReason
  440. )
  441. {
  442. if (fdwReason == DLL_PROCESS_ATTACH)
  443. {
  444. // Initialize the WaveOutList, fail if we cannot.
  445. return WaveOutList::Create();
  446. }
  447. return TRUE;
  448. }
  449. /*+
  450. Register hooked functions
  451. --*/
  452. HOOK_BEGIN
  453. CALL_NOTIFY_FUNCTION
  454. APIHOOK_ENTRY(WINMM.DLL, waveOutOpen)
  455. APIHOOK_ENTRY(WINMM.DLL, waveOutClose)
  456. APIHOOK_ENTRY(USER32.DLL, SetTimer)
  457. HOOK_END
  458. IMPLEMENT_SHIM_END