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.

706 lines
18 KiB

  1. /*****************************************************************************
  2. *
  3. * DIExcl.c
  4. *
  5. * Copyright (c) 1997 Microsoft Corporation. All Rights Reserved.
  6. *
  7. * Abstract:
  8. *
  9. * Management and negotiation of exclusive access.
  10. *
  11. * Contents:
  12. *
  13. * Excl_Acquire
  14. * Excl_Unacquire
  15. *
  16. *****************************************************************************/
  17. #include "dinputpr.h"
  18. /*****************************************************************************
  19. *
  20. * The sqiffle for this file.
  21. *
  22. *****************************************************************************/
  23. #define sqfl sqflExcl
  24. #pragma BEGIN_CONST_DATA
  25. #ifndef WINNT
  26. TCHAR c_tszVxd[] = TEXT("\\\\.\\DINPUT.VXD");
  27. #endif
  28. /*****************************************************************************
  29. *
  30. * @doc INTERNAL
  31. *
  32. * @struct SHAREDOBJECT |
  33. *
  34. * Each object that can be taken exclusively receives one of
  35. * these structures. This structure is shared across processes,
  36. * so you must protect access with the global DLL mutex.
  37. *
  38. * You would think that we could just use a named semaphore.
  39. * Well, that won't work because if the app crashes, nobody
  40. * will release the semaphore token and the device will be
  41. * unavailable forever.
  42. *
  43. * And we can't use a named mutex because mutexes are tracked
  44. * on a per-thread basis, but device acquisition is maintained
  45. * on a per-process basis.
  46. *
  47. * So instead we have to roll our own "process-level mutex".
  48. *
  49. * To conserve memory, we dump all our tiny <t SHAREDOBJECT>
  50. * structures into a single page. This means that we cannot
  51. * support more than about 4000 / sizeof(SHAREDOBJECT) =
  52. * 140 devices simultaneously acquired exclusively.
  53. *
  54. * Since USB maxes out at 64 devices, we've got plenty of room.
  55. *
  56. * WARNING! This structure may not change between DEBUG and
  57. * RETAIL. Otherwise, you have problems if one DirectInput
  58. * app is using DEBUG and another is using RETAIL.
  59. *
  60. * @field GUID | guid |
  61. *
  62. * The identifier for the device that is acquired exclusively.
  63. *
  64. * @field HWND | hwndOwner |
  65. *
  66. * The window handle associated with the device that has
  67. * obtained exclusive access.
  68. *
  69. * @field DWORD | pidOwner |
  70. *
  71. * The process ID of the owner window. This is used as a
  72. * cross-check against <f hwndOwner> in case the application
  73. * which is the owner suddenly crashes.
  74. *
  75. * @field DWORD | discl |
  76. *
  77. * Cooperative level with which the device was acquired.
  78. * We care about foreground-ness so that
  79. * we can steal acquisition from a window that
  80. * has stopped responding.
  81. *
  82. *****************************************************************************/
  83. typedef struct SHAREDOBJECT
  84. {
  85. GUID guid;
  86. HWND hwndOwner;
  87. DWORD pidOwner;
  88. DWORD discl;
  89. } SHAREDOBJECT, *PSHAREDOBJECT;
  90. typedef const SHAREDOBJECT *PCSHAREDOBJECT;
  91. /*****************************************************************************
  92. *
  93. * @doc INTERNAL
  94. *
  95. * @define csoMax | (cbShared - cbX(SHAREDOBJECTHEADER)) / cbX(SHAREDOBJECT) |
  96. *
  97. * The maximum number of simultaneously acquired devices.
  98. *
  99. *****************************************************************************/
  100. #define cbShared 4096
  101. #define csoMax ((cbShared - cbX(SHAREDOBJECTHEADER)) / cbX(SHAREDOBJECT))
  102. /*****************************************************************************
  103. *
  104. * @doc INTERNAL
  105. *
  106. * @struct SHAREDOBJECTPAGE |
  107. *
  108. * A header followed by an array of shared objects.
  109. *
  110. * The header must be first. <c g_soh> relies on it.
  111. *
  112. * @field SHAREDOBJECTHEADER | soh |
  113. *
  114. * The header.
  115. *
  116. * @field SHAREDOBJECT | rgso[csoMax] |
  117. *
  118. * Array of shared object structures.
  119. *
  120. *****************************************************************************/
  121. typedef struct SHAREDOBJECTPAGE
  122. {
  123. SHAREDOBJECTHEADER soh;
  124. SHAREDOBJECT rgso[csoMax];
  125. } SHAREDOBJECTPAGE, *PSHAREDOBJECTPAGE;
  126. void INLINE
  127. CheckSharedObjectPageSize(void)
  128. {
  129. CAssertF(cbX(SHAREDOBJECTPAGE) <= cbShared);
  130. CAssertF(cbX(SHAREDOBJECTPAGE) + cbX(SHAREDOBJECT) > cbShared);
  131. }
  132. /*****************************************************************************
  133. *
  134. * @doc INTERNAL
  135. *
  136. * @func PSHAREDOBJECT | Excl_FindGuid |
  137. *
  138. * Locate a GUID in the shared object array.
  139. *
  140. * The shared global mutex must already be taken.
  141. *
  142. * @parm PCGUID | pguid |
  143. *
  144. * The GUID to locate.
  145. *
  146. * @returns
  147. *
  148. * A pointer to the entry, or 0 if not found.
  149. *
  150. *
  151. *****************************************************************************/
  152. PSHAREDOBJECT INTERNAL
  153. Excl_FindGuid(PCGUID pguid)
  154. {
  155. PSHAREDOBJECTPAGE psop;
  156. PSHAREDOBJECT pso, psoMax;
  157. DWORD Data1;
  158. EnterProcI(Excl_FindGuid, (_ "G", pguid));
  159. psop = g_psop;
  160. Data1 = pguid->Data1;
  161. AssertF(g_psop);
  162. for(pso = &psop->rgso[0], psoMax = &psop->rgso[psop->soh.cso];
  163. pso < psoMax; pso++)
  164. {
  165. if(pso->guid.Data1 == Data1 && IsEqualGUID(pguid, &pso->guid))
  166. {
  167. goto done;
  168. }
  169. }
  170. pso = 0;
  171. done:;
  172. ExitProcX((UINT_PTR)pso);
  173. return pso;
  174. }
  175. /*****************************************************************************
  176. *
  177. * @doc INTERNAL
  178. *
  179. * @func HRESULT | Excl_CanStealPso |
  180. *
  181. * Determine whether the <t SHAREDOBJECT> is self-consistent
  182. * and represents an instance which validly holds the
  183. * exclusive acquisition. If so, then it cannot be stolen.
  184. * Else, it is dead and can be stolen.
  185. *
  186. * @parm PCSHAREDOBJECT | pso |
  187. *
  188. * The <t SHAREDOBJECT> structure to validate.
  189. *
  190. * @returns
  191. *
  192. * <c S_OK> if acquisition can be stolen, or
  193. * <c DIERR_OTHERAPPHASPRIO> if acquisition is validly
  194. * held by another instance.
  195. *
  196. *****************************************************************************/
  197. STDMETHODIMP
  198. Excl_CanStealPso(PCSHAREDOBJECT pso)
  199. {
  200. HRESULT hres = S_OK;
  201. /*
  202. * The window handle should be valid and still refer to the pid.
  203. */
  204. if(GetWindowPid(pso->hwndOwner) == pso->pidOwner)
  205. {
  206. if( pso->discl & DISCL_FOREGROUND )
  207. {
  208. if( GetForegroundWindow() != pso->hwndOwner)
  209. {
  210. // 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
  211. RPF("Acquire: can't steal Pso because it belongs to another app. (current hwnd=0x%p)",
  212. pso->hwndOwner);
  213. hres = DIERR_OTHERAPPHASPRIO;
  214. } else
  215. {
  216. // 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
  217. RPF("Acquire: Current owner hwnd=0x%p has priority; "
  218. "stealing", pso->hwndOwner);
  219. hres = S_OK;
  220. }
  221. }
  222. } else
  223. {
  224. /*
  225. * App died. Can steal.
  226. */
  227. // 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
  228. RPF("Acquire: Previous owner pid=0x%p mysteriously died; "
  229. "stealing", pso->pidOwner);
  230. hres = S_OK;
  231. }
  232. return hres;
  233. }
  234. /*****************************************************************************
  235. *
  236. * @doc INTERNAL
  237. *
  238. * @func HRESULT | Excl_Acquire |
  239. *
  240. * Attempt to acquire the device exclusively.
  241. *
  242. * @parm PCGUID | pguid |
  243. *
  244. * The GUID to acquire.
  245. *
  246. * @parm HWND | hwnd |
  247. *
  248. * Window handle with which to associate the device.
  249. *
  250. * @parm DWORD | discl |
  251. *
  252. * Flags describing cooperative level.
  253. * We are interested only in devices acquired exclusively.
  254. *
  255. * @returns
  256. *
  257. * S_OK on success, or
  258. *
  259. * DIERR_OTHERAPPHASPRIO
  260. * hresLe(ERROR_INVALID_WINDOW_HANDLE)
  261. *
  262. *
  263. *****************************************************************************/
  264. STDMETHODIMP
  265. Excl_Acquire(PCGUID pguid, HWND hwnd, DWORD discl)
  266. {
  267. HRESULT hres;
  268. AssertF(g_psop);
  269. if(discl & DISCL_EXCLUSIVE)
  270. {
  271. /*
  272. * Window must be owned by this process.
  273. */
  274. if(GetWindowPid(hwnd) == GetCurrentProcessId())
  275. {
  276. PSHAREDOBJECT pso;
  277. WaitForSingleObject(g_hmtxGlobal, INFINITE);
  278. pso = Excl_FindGuid(pguid);
  279. /*
  280. * If we found a match, then it might be a sharing violation.
  281. */
  282. if(pso)
  283. {
  284. hres = Excl_CanStealPso(pso);
  285. } else
  286. {
  287. /*
  288. * Allocate a slot for it.
  289. */
  290. if(g_psop->soh.cso < csoMax)
  291. {
  292. pso = &g_psop->rgso[g_psop->soh.cso++];
  293. pso->guid = *pguid;
  294. hres = S_OK;
  295. } else
  296. {
  297. //ISSUE-2001/03/29-timgill hard limit on number of exclusive devices
  298. //Can be annoying
  299. RPF("Too many devices acquired exclusively");
  300. hres = E_FAIL;
  301. }
  302. }
  303. if(SUCCEEDED(hres))
  304. {
  305. pso->hwndOwner = hwnd;
  306. pso->pidOwner = GetCurrentProcessId();
  307. pso->discl = discl;
  308. hres = S_OK;
  309. }
  310. ReleaseMutex(g_hmtxGlobal);
  311. } else
  312. {
  313. hres = hresLe(ERROR_INVALID_WINDOW_HANDLE);
  314. }
  315. } else
  316. {
  317. hres = S_OK;
  318. }
  319. return hres;
  320. }
  321. /*****************************************************************************
  322. *
  323. * @doc INTERNAL
  324. *
  325. * @func void | Excl_Unacquire |
  326. *
  327. * Undo the effects of an acquire.
  328. *
  329. * @parm PCGUID | pguid |
  330. *
  331. * The GUID to acquire.
  332. *
  333. * @parm HWND | hwnd |
  334. *
  335. * Window handle with which to associate the device.
  336. *
  337. * @parm DWORD | discl |
  338. *
  339. * Flags describing cooperative level.
  340. * We are interested only in devices acquired exclusively.
  341. *
  342. *****************************************************************************/
  343. void EXTERNAL
  344. Excl_Unacquire(PCGUID pguid, HWND hwnd, DWORD discl)
  345. {
  346. AssertF(g_psop);
  347. if(discl & DISCL_EXCLUSIVE)
  348. {
  349. PSHAREDOBJECT pso;
  350. WaitForSingleObject(g_hmtxGlobal, INFINITE);
  351. pso = Excl_FindGuid(pguid);
  352. /*
  353. * Make sure it's really ours.
  354. */
  355. if(pso && pso->hwndOwner == hwnd &&
  356. pso->pidOwner == GetCurrentProcessId())
  357. {
  358. /*
  359. * Delete the entry and close up the gap.
  360. */
  361. *pso = g_psop->rgso[--g_psop->soh.cso];
  362. }
  363. ReleaseMutex(g_hmtxGlobal);
  364. }
  365. }
  366. /*****************************************************************************
  367. *
  368. * @doc INTERNAL
  369. *
  370. * @func HRESULT | Excl_Init |
  371. *
  372. * Initialize the exclusive device manager.
  373. *
  374. * @returns
  375. *
  376. * <c S_OK> if all is well.
  377. *
  378. * <c E_FAIL> if something is horribly wrong.
  379. *
  380. *****************************************************************************/
  381. STDMETHODIMP
  382. Excl_Init(void)
  383. {
  384. HRESULT hres;
  385. TCHAR tszName[ctchNameGuid];
  386. DllEnterCrit();
  387. /*
  388. * Create the global mutex used to gate access to shared memory.
  389. */
  390. if(g_hmtxGlobal == 0)
  391. {
  392. NameFromGUID(tszName, &IID_IDirectInputW);
  393. g_hmtxGlobal = CreateMutex(0, TRUE, tszName);
  394. if(g_hmtxGlobal)
  395. {
  396. /*
  397. * If we need to do smth only once, we can do:
  398. * if ( GetLastError() != ERROR_ALREADY_EXISTS )
  399. * {
  400. * do our stuff
  401. * }
  402. */
  403. g_flEmulation = RegQueryDIDword(NULL, REGSTR_VAL_EMULATION, 0);
  404. #ifndef WINNT
  405. /*
  406. * We have to open the VxD while we own the global mutex in order
  407. * to avoid a race condition that occurs when two processes try
  408. * to open a VxD at the same time. See DInput VxD for details.
  409. */
  410. if (_OpenVxDHandle)
  411. {
  412. /*
  413. * CreateFile on a \\.\ name does not check the dwCreationDisposition
  414. * parameter but BoundsChecker does so use a valid value.
  415. */
  416. g_hVxD = CreateFile(c_tszVxd, 0, 0, 0, OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, 0);
  417. if( g_hVxD != INVALID_HANDLE_VALUE )
  418. {
  419. LONG lGranularity;
  420. /*
  421. * If we can't get the sequence number (weird), then set it
  422. * back to NULL so it will point at the shared memory
  423. * block just like on NT.
  424. */
  425. if (FAILED(IoctlHw(IOCTL_GETSEQUENCEPTR, 0, 0,
  426. &g_pdwSequence, cbX(g_pdwSequence))))
  427. {
  428. g_pdwSequence = 0;
  429. }
  430. if (SUCCEEDED(IoctlHw(IOCTL_MOUSE_GETWHEEL, 0, 0,
  431. &lGranularity, cbX(lGranularity))))
  432. {
  433. g_lWheelGranularity = lGranularity;
  434. }
  435. }
  436. else
  437. {
  438. RPF( "ERROR: Cannot load %s", &c_tszVxd[4] );
  439. }
  440. }
  441. #endif
  442. /*
  443. * We defer ExtDll work until now, because it is not safe to
  444. * call LoadLibrary during PROCESS_ATTACH.
  445. *
  446. * We also steal g_hmtxGlobal to protect us from doing it twice.
  447. */
  448. ExtDll_Init();
  449. ReleaseMutex(g_hmtxGlobal);
  450. }
  451. else
  452. {
  453. RPF("Cannot create shared semaphore %s", tszName);
  454. hres = E_FAIL;
  455. goto fail;
  456. }
  457. }
  458. /*
  459. * Create the shared memory.
  460. *
  461. * Warning! The file mapping handle must be kept alive
  462. * so its name stays alive. NT destroys the file mapping
  463. * object when you close the handle; as a result, the
  464. * name goes away with it and another instance of
  465. * DirectInput fails to find it.
  466. */
  467. if(g_psop == 0)
  468. {
  469. NameFromGUID(tszName, &IID_IDirectInputDeviceW);
  470. g_hfm = CreateFileMapping(INVALID_HANDLE_VALUE, 0,
  471. PAGE_READWRITE, 0,
  472. cbShared, tszName);
  473. if(g_hfm)
  474. {
  475. g_psop = MapViewOfFile(g_hfm, FILE_MAP_WRITE | FILE_MAP_READ,
  476. 0, 0, 0);
  477. if(g_psop)
  478. {
  479. } else
  480. {
  481. RPF("Cannot map shared memory block %s", tszName);
  482. hres = E_FAIL;
  483. goto fail;
  484. }
  485. } else
  486. {
  487. RPF("Cannot create shared memory block %s", tszName);
  488. hres = E_FAIL;
  489. goto fail;
  490. }
  491. }
  492. /*
  493. * Create the global mutex used to gate access to joystick info.
  494. */
  495. if(g_hmtxJoy == 0)
  496. {
  497. NameFromGUID(tszName, &IID_IDirectInputDevice2A);
  498. g_hmtxJoy = CreateMutex(0, 0, tszName);
  499. if(g_hmtxJoy)
  500. {
  501. } else
  502. {
  503. RPF("Cannot create shared semaphore %s", tszName);
  504. hres = E_FAIL;
  505. goto fail;
  506. }
  507. /*
  508. * We shall steal the joystick Mutex to build the Bus list
  509. * for the first time.
  510. * It is very unlikely that the list will change. ( PCMCIA cards ! )
  511. * And when it does change we can expect the joyConfig interface will
  512. * be pinged.
  513. */
  514. DIBus_BuildList(FALSE);
  515. }
  516. /*
  517. * If we don't have a global sequence number from the driver,
  518. * then use the one in the shared memory block instead.
  519. */
  520. if(g_pdwSequence == 0)
  521. {
  522. g_pdwSequence = &g_psoh->dwSequence;
  523. }
  524. hres = S_OK;
  525. fail:;
  526. DllLeaveCrit();
  527. return hres;
  528. }
  529. /*****************************************************************************
  530. *
  531. * @doc INTERNAL
  532. *
  533. * @func LONG | Excl_UniqueGuidInteger |
  534. *
  535. * Generate a unique number used by DICreateGuid to make sure
  536. * that we don't generate two pseudoGUIDs with the same value.
  537. *
  538. * @returns
  539. *
  540. * An integer that has not been returned by this function
  541. * yet.
  542. *
  543. *****************************************************************************/
  544. LONG EXTERNAL
  545. Excl_UniqueGuidInteger(void)
  546. {
  547. LONG lRc;
  548. AssertF(g_hmtxGlobal);
  549. WaitForSingleObject(g_hmtxGlobal, INFINITE);
  550. AssertF(g_psop);
  551. lRc = ++g_psop->soh.cguid;
  552. ReleaseMutex(g_hmtxGlobal);
  553. return lRc;
  554. }
  555. /*****************************************************************************
  556. *
  557. * @doc INTERNAL
  558. *
  559. * @func DWORD | Excl_GetConfigChangedTime |
  560. *
  561. * Retrieves tmConfigChanged in g_psop->soh.
  562. *
  563. * @returns
  564. *
  565. * tmConfigChanged
  566. *****************************************************************************/
  567. DWORD EXTERNAL
  568. Excl_GetConfigChangedTime()
  569. {
  570. DWORD dwRc;
  571. AssertF(g_hmtxGlobal);
  572. WaitForSingleObject(g_hmtxGlobal, INFINITE);
  573. AssertF(g_psop);
  574. dwRc = g_psop->soh.tmConfigChanged;
  575. ReleaseMutex(g_hmtxGlobal);
  576. return dwRc;
  577. }
  578. /*****************************************************************************
  579. *
  580. * @doc INTERNAL
  581. *
  582. * @func void | Excl_SetConfigChangedTime |
  583. *
  584. * Sets tmConfigChanged in g_psop->soh.
  585. *
  586. * @returns
  587. *
  588. * void
  589. *****************************************************************************/
  590. void EXTERNAL
  591. Excl_SetConfigChangedTime(DWORD tm)
  592. {
  593. AssertF(g_hmtxGlobal);
  594. WaitForSingleObject(g_hmtxGlobal, INFINITE);
  595. AssertF(g_psop);
  596. g_psop->soh.tmConfigChanged = tm;
  597. ReleaseMutex(g_hmtxGlobal);
  598. return;
  599. }