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.

413 lines
11 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. EmulateDirectDrawSync.cpp
  5. Abstract:
  6. DirectDraw uses per-thread exclusive mode arbitration on NT. On Win9x this
  7. is done per process. What this means is that if an app releases exclusive
  8. mode from a different thread than that which acquired it, it will be in a
  9. permanently bad state.
  10. This shim ensures that the mutex is obtained and released on the same
  11. thread. During DLL_PROCESS_ATTACH, a new thread is started: this thread
  12. manages the acquisition and release of the mutex.
  13. Note we can't get the mutex by catching CreateMutex because it's called
  14. from the dllmain of ddraw.dll: so it wouldn't work on win2k.
  15. Notes:
  16. This is a general purpose shim.
  17. History:
  18. 09/11/2000 prashkud Created
  19. 10/28/2000 linstev Rewrote to work on win2k
  20. 02/23/2001 linstev Modified to handle cases where DirectDraw was used
  21. inside DllMains
  22. --*/
  23. #include "precomp.h"
  24. IMPLEMENT_SHIM_BEGIN(EmulateDirectDrawSync)
  25. #include "ShimHookMacro.h"
  26. APIHOOK_ENUM_BEGIN
  27. APIHOOK_ENUM_ENTRY(WaitForSingleObject)
  28. APIHOOK_ENUM_ENTRY(ReleaseMutex)
  29. APIHOOK_ENUM_ENTRY(CloseHandle)
  30. APIHOOK_ENUM_END
  31. // Enum used to tell our thread what to do
  32. enum {sNone, sWaitForSingleObject, sReleaseMutex};
  33. // Events we use to signal our thread to do work and wait until its done
  34. HANDLE g_hWaitEvent;
  35. HANDLE g_hDoneEvent;
  36. HANDLE g_hThread = NULL;
  37. //
  38. // Parameters that are passed between the caller thread and our thread
  39. // Access is synchronized with a critical section
  40. //
  41. CRITICAL_SECTION g_csSync;
  42. DWORD g_dwWait;
  43. DWORD g_dwWaitRetValue;
  44. DWORD g_dwTime;
  45. BOOL g_bRetValue;
  46. // Store the DirectDraw mutex handle
  47. HANDLE g_hDDMutex = 0;
  48. // Thread tracking data so we can identify degenerate cases
  49. DWORD g_dwMutexOwnerThreadId = 0;
  50. // Find the DirectDraw mutex
  51. DWORD g_dwFindMutexThread = 0;
  52. /*++
  53. Unfortunately we don't get in early enough on Win2k to get the mutex from the
  54. ddraw call to CreateMutex, so we have to make use of a special hack that knows
  55. about the ddraw internals.
  56. Ddraw has an export called GetOLEThunkData. The name is chosen to prevent
  57. people from calling it. It is designed to be used by the test harness. One of
  58. the things it can do, is release the exclusive mode mutex. This is the hack
  59. we're exploiting so we can determine the mutex handle.
  60. --*/
  61. BOOL
  62. FindMutex()
  63. {
  64. typedef VOID (WINAPI *_pfn_GetOLEThunkData)(ULONG_PTR dwOrdinal);
  65. HMODULE hMod;
  66. _pfn_GetOLEThunkData pfnGetOLEThunkData;
  67. hMod = GetModuleHandleA("ddraw");
  68. if (!hMod) {
  69. DPFN( eDbgLevelError, "[FindMutex] DirectDraw not loaded");
  70. return FALSE;
  71. }
  72. pfnGetOLEThunkData = (_pfn_GetOLEThunkData) GetProcAddress(hMod, "GetOLEThunkData");
  73. if (!pfnGetOLEThunkData) {
  74. DPFN( eDbgLevelError, "[FindMutex] Failed to get GetOLEThunkData API");
  75. return FALSE;
  76. }
  77. //
  78. // Now we plan to go and find the mutex by getting Ddraw to call
  79. // ReleaseMutex.
  80. //
  81. EnterCriticalSection(&g_csSync);
  82. //
  83. // Set the mutex to the current thread so it can be picked up in the
  84. // ReleaseMutex hook
  85. //
  86. g_dwFindMutexThread = GetCurrentThreadId();
  87. //
  88. // Call to the hard-coded (in ddraw) ReleaseMutex hack which releases the
  89. // mutex
  90. //
  91. pfnGetOLEThunkData(6);
  92. g_dwFindMutexThread = 0;
  93. LeaveCriticalSection(&g_csSync);
  94. return (g_hDDMutex != 0);
  95. }
  96. /*++
  97. Hook WaitForSingleObject to determine when DirectDraw is testing or acquiring
  98. the mutex. If we haven't got the mutex yet, we attempt to find it using our
  99. hack.
  100. --*/
  101. DWORD
  102. APIHOOK(WaitForSingleObject)(
  103. HANDLE hHandle,
  104. DWORD dwMilliSeconds
  105. )
  106. {
  107. if (g_hThread) {
  108. //
  109. // Hack to find the DirectDraw mutex
  110. //
  111. if (!g_hDDMutex) {
  112. FindMutex();
  113. }
  114. if (g_hDDMutex && (hHandle == g_hDDMutex)) {
  115. //
  116. // Use our thread to acquire the mutex. We synchronize since we're
  117. // accessing globals to communicate with our thread.
  118. //
  119. DWORD dwRet;
  120. EnterCriticalSection(&g_csSync);
  121. // Set globals to communicate with our thread
  122. g_dwTime = dwMilliSeconds;
  123. g_dwWait = sWaitForSingleObject;
  124. if (!ResetEvent(g_hDoneEvent))
  125. {
  126. DPFN( eDbgLevelError, "ResetEvent failed. Cannot continue");
  127. return WAIT_FAILED;
  128. }
  129. // Signal our thread to obtain the mutex
  130. if (!SetEvent(g_hWaitEvent))
  131. {
  132. DPFN( eDbgLevelError, "SetEvent failed. Cannot continue");
  133. return WAIT_FAILED;
  134. }
  135. // Wait until the state of the mutex has been determined
  136. WaitForSingleObject(g_hDoneEvent, INFINITE);
  137. // Code to detect the degenerate
  138. if (g_dwWaitRetValue == WAIT_OBJECT_0) {
  139. g_dwMutexOwnerThreadId = GetCurrentThreadId();
  140. }
  141. dwRet = g_dwWaitRetValue;
  142. LeaveCriticalSection(&g_csSync);
  143. return dwRet;
  144. }
  145. }
  146. return ORIGINAL_API(WaitForSingleObject)(hHandle, dwMilliSeconds);
  147. }
  148. /*++
  149. Hook ReleaseMutex and release the mutex on our thread.
  150. --*/
  151. BOOL
  152. APIHOOK(ReleaseMutex)(
  153. HANDLE hMutex
  154. )
  155. {
  156. if (g_hThread && (g_dwFindMutexThread == GetCurrentThreadId())) {
  157. //
  158. // We're using our hack to find the DirectDraw mutex
  159. //
  160. DPFN( eDbgLevelInfo, "DDraw exclusive mode mutex found");
  161. g_hDDMutex = hMutex;
  162. // Don't release it, since we never acquired it
  163. return TRUE;
  164. }
  165. //
  166. // First try to release it on the current thread. This will only succeed if
  167. // it was obtained on this thread.
  168. //
  169. BOOL bRet = ORIGINAL_API(ReleaseMutex)(hMutex);
  170. if (!bRet && g_hThread && g_hDDMutex && (hMutex == g_hDDMutex)) {
  171. //
  172. // Use our thread to release the mutex. We synchronize since we're
  173. // accessing globals to communicate with our thread.
  174. //
  175. EnterCriticalSection(&g_csSync);
  176. // Set globals to communicate with our thread
  177. g_dwWait = sReleaseMutex;
  178. if (!ResetEvent(g_hDoneEvent))
  179. {
  180. DPFN( eDbgLevelError, "ResetEvent failed. Cannot continue");
  181. return FALSE;
  182. }
  183. // Wait until our thread returns
  184. if (!SetEvent(g_hWaitEvent))
  185. {
  186. DPFN( eDbgLevelError, "SetEvent failed. Cannot continue");
  187. return FALSE;
  188. }
  189. // Signal our thread to release the mutex
  190. WaitForSingleObject(g_hDoneEvent, INFINITE);
  191. // Detect degenerate case
  192. if (GetCurrentThreadId() != g_dwMutexOwnerThreadId) {
  193. LOGN( eDbgLevelError, "[ReleaseMutex] DirectDraw synchronization error - correcting");
  194. }
  195. if (g_bRetValue) {
  196. g_dwMutexOwnerThreadId = 0;
  197. }
  198. bRet = g_bRetValue;
  199. LeaveCriticalSection(&g_csSync);
  200. }
  201. return bRet;
  202. }
  203. /*++
  204. Clear our handle in case the app frees ddraw and reloads it.
  205. --*/
  206. BOOL
  207. APIHOOK(CloseHandle)(HANDLE hObject)
  208. {
  209. if (g_hThread && (hObject == g_hDDMutex))
  210. {
  211. DPFN( eDbgLevelInfo, "DDraw exclusive mode mutex closed");
  212. g_hDDMutex = 0;
  213. }
  214. return ORIGINAL_API(CloseHandle)(hObject);
  215. }
  216. /*++
  217. Thread used to do all the mutex operations so we can guarantee that the thread
  218. that acquired the mutex is the same one that releases it.
  219. --*/
  220. VOID
  221. WINAPI
  222. ThreadSyncMutex(
  223. LPVOID /*lpParameter*/
  224. )
  225. {
  226. for (;;) {
  227. // Wait until we need to acquire or release the mutex
  228. WaitForSingleObject(g_hWaitEvent, INFINITE);
  229. if (g_dwWait == sWaitForSingleObject) {
  230. // WaitForSingleobject() has been called on the Mutex object
  231. g_dwWaitRetValue = ORIGINAL_API(WaitForSingleObject)(
  232. g_hDDMutex, g_dwTime);
  233. }
  234. else if (g_dwWait == sReleaseMutex) {
  235. // ReleaseMutex has been called
  236. g_bRetValue = ORIGINAL_API(ReleaseMutex)(g_hDDMutex);
  237. }
  238. g_dwWait = sNone;
  239. if (!ResetEvent(g_hWaitEvent))
  240. {
  241. DPFN( eDbgLevelError, "ResetEvent failed. Cannot continue");
  242. return;
  243. }
  244. if (!SetEvent(g_hDoneEvent))
  245. {
  246. DPFN( eDbgLevelError, "SetEvent failed. Cannot continue");
  247. return;
  248. }
  249. }
  250. }
  251. /*++
  252. Register hooked functions
  253. --*/
  254. BOOL
  255. NOTIFY_FUNCTION(
  256. DWORD fdwReason
  257. )
  258. {
  259. if (fdwReason == SHIM_STATIC_DLLS_INITIALIZED) {
  260. //
  261. // We need the critical section all the time
  262. // Security change - InitializeCriticalSection to
  263. // InitializeCriticalSectionAndSpinCount. The high
  264. // bit of 'spincount' is set to 1 for preallocation.
  265. //
  266. if (InitializeCriticalSectionAndSpinCount(&g_csSync, 0x80000000) == FALSE)
  267. {
  268. DPFN( eDbgLevelError, "Failed to initialize critical section");
  269. return FALSE;
  270. }
  271. //
  272. // Create Events that will be used for the thread synchronization, i.e
  273. // to synchronize this thread and the one we will be creating ahead. We
  274. // don't clean these up by design. We have to do this stuff here, rather
  275. // than in the process attach, since OpenGL apps and others do DirectX
  276. // stuff in their dllmains.
  277. //
  278. g_hWaitEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  279. if (!g_hWaitEvent) {
  280. DPFN( eDbgLevelError, "Failed to create Event 1");
  281. return FALSE;
  282. }
  283. g_hDoneEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  284. if (!g_hDoneEvent) {
  285. DPFN( eDbgLevelError, "Failed to create Event 2");
  286. return FALSE;
  287. }
  288. // Create our thread
  289. g_hThread = CreateThread(NULL, 0,
  290. (LPTHREAD_START_ROUTINE) ThreadSyncMutex, NULL, 0,
  291. NULL);
  292. if (!g_hThread) {
  293. DPFN( eDbgLevelError, "Failed to create Thread");
  294. return FALSE;
  295. }
  296. }
  297. return TRUE;
  298. }
  299. HOOK_BEGIN
  300. CALL_NOTIFY_FUNCTION
  301. APIHOOK_ENTRY(KERNEL32.DLL, WaitForSingleObject)
  302. APIHOOK_ENTRY(KERNEL32.DLL, ReleaseMutex)
  303. APIHOOK_ENTRY(KERNEL32.DLL, CloseHandle)
  304. HOOK_END
  305. IMPLEMENT_SHIM_END