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.

1695 lines
55 KiB

  1. /*++
  2. *
  3. * WOW v1.0
  4. *
  5. * Copyright (c) 1991, Microsoft Corporation
  6. *
  7. * WALIAS.C
  8. * WOW32 16-bit handle alias support
  9. *
  10. * History:
  11. * Created 27-Jan-1991 by Jeff Parsons (jeffpar)
  12. * Modified 12-May-1992 by Mike Tricker (miketri) to add MultiMedia support
  13. --*/
  14. #include "precomp.h"
  15. #pragma hdrstop
  16. MODNAME(walias.c);
  17. extern HANDLE hmodWOW32;
  18. extern CRITICAL_SECTION gcsWOW;
  19. extern PTD gptdTaskHead;
  20. //BUGBUG - this must be removed once MM_MCISYSTEM_STRING is defined in MMSYSTEM.H.
  21. #ifndef MM_MCISYSTEM_STRING
  22. #define MM_MCISYSTEM_STRING 0x3CA
  23. #endif
  24. #ifdef DEBUG
  25. extern BOOL fSkipLog; // TRUE to temporarily skip certain logging
  26. #endif
  27. typedef struct _stdclass {
  28. LPSTR lpszClassName;
  29. ATOM aClassAtom;
  30. WNDPROC lpfnWndProc;
  31. INT iOrdinal;
  32. DWORD vpfnWndProc;
  33. } STDCLASS;
  34. // Some cool defines stolen from USERSRV.H
  35. #define MENUCLASS MAKEINTATOM(0x8000)
  36. #define DESKTOPCLASS MAKEINTATOM(0x8001)
  37. #define DIALOGCLASS MAKEINTATOM(0x8002)
  38. #define SWITCHWNDCLASS MAKEINTATOM(0x8003)
  39. #define ICONTITLECLASS MAKEINTATOM(0x8004)
  40. // See WARNING below!
  41. STDCLASS stdClasses[] = {
  42. NULL, 0, NULL, 0, 0, // WOWCLASS_UNKNOWN
  43. NULL, 0, NULL, 0, 0, // WOWCLASS_WIN16
  44. "BUTTON", 0, NULL, FUN_BUTTONWNDPROC, 0, // WOWCLASS_BUTTON,
  45. "COMBOBOX", 0, NULL, FUN_COMBOBOXCTLWNDPROC, 0, // WOWCLASS_COMBOBOX,
  46. "EDIT", 0, NULL, FUN_EDITWNDPROC, 0, // WOWCLASS_EDIT,
  47. "LISTBOX", 0, NULL, FUN_LBOXCTLWNDPROC, 0, // WOWCLASS_LISTBOX,
  48. "MDICLIENT", 0, NULL, FUN_MDICLIENTWNDPROC, 0, // WOWCLASS_MDICLIENT,
  49. "SCROLLBAR", 0, NULL, FUN_SBWNDPROC, 0, // WOWCLASS_SCROLLBAR,
  50. "STATIC", 0, NULL, FUN_STATICWNDPROC, 0, // WOWCLASS_STATIC,
  51. "#32769", (WORD)DESKTOPCLASS, NULL, FUN_DESKTOPWNDPROC, 0, // WOWCLASS_DESKTOP,
  52. "#32770", (WORD)DIALOGCLASS, NULL, FUN_DEFDLGPROCTHUNK, 0, // WOWCLASS_DIALOG,
  53. "#32772", (WORD)ICONTITLECLASS, NULL, FUN_TITLEWNDPROC, 0, // WOWCLASS_ICONTITLE,
  54. "#32768", (WORD)MENUCLASS, NULL, FUN_MENUWNDPROC, 0, // WOWCLASS_MENU,
  55. "#32771", (WORD)SWITCHWNDCLASS, NULL, 0, 0, // WOWCLASS_SWITCHWND,
  56. "COMBOLBOX", 0, NULL, FUN_LBOXCTLWNDPROC, 0, // WOWCLASS_COMBOLBOX
  57. };
  58. //
  59. // WARNING! The above sequence and values must be maintained otherwise the
  60. // table in WMSG16.C for message thunking must be changed. Same goes for
  61. // the #define's in WALIAS.H
  62. //
  63. // The above COMBOLBOX case is special because it is class that is
  64. // almost identical to a listbox. Therefore we lie about it.
  65. INT GetStdClassNumber(
  66. PSZ pszClass
  67. ) {
  68. INT i;
  69. if ( HIWORD(pszClass) ) {
  70. // They passed us a string
  71. for ( i = WOWCLASS_BUTTON; i < NUMEL(stdClasses); i++ ) {
  72. if ( WOW32_stricmp(pszClass, stdClasses[i].lpszClassName) == 0 ) {
  73. return( i );
  74. }
  75. }
  76. } else {
  77. // They passed us an atom
  78. for ( i = WOWCLASS_BUTTON; i < NUMEL(stdClasses); i++ ) {
  79. if ( stdClasses[i].aClassAtom == 0 ) {
  80. // RegisterWindowMessage is an undocumented way of determining
  81. // an atom value in the context of the server-side heap.
  82. stdClasses[i].aClassAtom = (ATOM)RegisterWindowMessage(stdClasses[i].lpszClassName);
  83. }
  84. if ( (ATOM)LOWORD(pszClass) == stdClasses[i].aClassAtom ) {
  85. return( i );
  86. }
  87. }
  88. }
  89. return( WOWCLASS_WIN16 ); // private 16-bit class created by the app
  90. }
  91. // Returns a 32 window proc given a class index
  92. WNDPROC GetStdClassWndProc(
  93. DWORD iClass
  94. ) {
  95. WNDPROC lpfn32;
  96. if ( iClass < WOWCLASS_WIN16 || iClass > WOWCLASS_MAX ) {
  97. WOW32ASSERT(FALSE);
  98. return( NULL );
  99. }
  100. lpfn32 = stdClasses[iClass].lpfnWndProc;
  101. if ( lpfn32 == NULL ) {
  102. WNDCLASS wc;
  103. BOOL f;
  104. f = GetClassInfo( NULL, stdClasses[iClass].lpszClassName, &wc );
  105. if ( f ) {
  106. VPVOID vp;
  107. DWORD UNALIGNED * lpdw;
  108. lpfn32 = wc.lpfnWndProc;
  109. stdClasses[iClass].lpfnWndProc = lpfn32;
  110. vp = GetStdClassThunkProc(iClass);
  111. vp = (VPVOID)((DWORD)vp - sizeof(DWORD)*3);
  112. GETVDMPTR( vp, sizeof(DWORD)*3, lpdw );
  113. WOW32ASSERT(*lpdw == SUBCLASS_MAGIC); // Are we editing the right stuff?
  114. if (!lpdw)
  115. *(lpdw+2) = (DWORD)lpfn32;
  116. FLUSHVDMCODEPTR( vp, sizeof(DWORD)*3, lpdw );
  117. FREEVDMPTR( lpdw );
  118. }
  119. }
  120. return( lpfn32 );
  121. }
  122. // Returns a 16 window proc thunk given a class index
  123. DWORD GetStdClassThunkProc(
  124. INT iClass
  125. ) {
  126. DWORD dwResult;
  127. SHORT iOrdinal;
  128. PARM16 Parm16;
  129. if ( iClass < WOWCLASS_WIN16 || iClass > WOWCLASS_MAX ) {
  130. WOW32ASSERT(FALSE);
  131. return( 0 );
  132. }
  133. iOrdinal = (SHORT)stdClasses[iClass].iOrdinal;
  134. if ( iOrdinal == 0 ) {
  135. return( (DWORD)NULL );
  136. }
  137. // If we've already gotten this proc, then don't bother calling into 16-bit
  138. dwResult = stdClasses[iClass].vpfnWndProc;
  139. if ( dwResult == (DWORD)NULL ) {
  140. // Callback into the 16-bit world asking for the 16:16 address
  141. Parm16.SubClassProc.iOrdinal = iOrdinal;
  142. if (!CallBack16(RET_SUBCLASSPROC, &Parm16, (VPPROC)NULL,
  143. (PVPVOID)&dwResult)) {
  144. WOW32ASSERT(FALSE);
  145. return( 0 );
  146. }
  147. // Save it since it is a constant.
  148. stdClasses[iClass].vpfnWndProc = dwResult;
  149. }
  150. return( dwResult );
  151. }
  152. /*
  153. * PWC GetClassWOWWords(hInst, pszClass)
  154. * is a ***private*** API for WOW only. It returns a pointer to the
  155. * WOW Class structure in the server's window class structure.
  156. * This is similar to GetClassLong(hwnd32, GCL_WOWWORDS) (see FindPWC),
  157. * but in this case we don't have a hwnd32, we have the class name
  158. * and instance handle.
  159. */
  160. PWC FindClass16(LPCSTR pszClass, HAND16 hInst)
  161. {
  162. register PWC pwc;
  163. pwc = (PWC)(pfnOut.pfnGetClassWOWWords)(HMODINST32(hInst), pszClass);
  164. WOW32WARNMSGF(
  165. pwc,
  166. ("WOW32 warning: GetClassWOWWords('%s', %04x) returned NULL\n", pszClass, hInst)
  167. );
  168. return (pwc);
  169. }
  170. #ifdef DEBUG
  171. INT nAliases;
  172. INT iLargestListSlot;
  173. PSZ apszHandleClasses[] = {
  174. "Unknown", // WOWCLASS_UNKNOWN
  175. "Window", // WOWCLASS_WIN16
  176. "Button", // WOWCLASS_BUTTON
  177. "ComboBox", // WOWCLASS_COMBOBOX
  178. "Edit", // WOWCLASS_EDIT
  179. "ListBox", // WOWCLASS_LISTBOX
  180. "MDIClient", // WOWCLASS_MDICLIENT
  181. "Scrollbar", // WOWCLASS_SCROLLBAR
  182. "Static", // WOWCLASS_STATIC
  183. "Desktop", // WOWCLASS_DESKTOP
  184. "Dialog", // WOWCLASS_DIALOG
  185. "Menu", // WOWCLASS_MENU
  186. "IconTitle", // WOWCLASS_ICONTITLE
  187. "Accel", // WOWCLASS_ACCEL
  188. "Cursor", // WOWCLASS_CURSOR
  189. "Icon", // WOWCLASS_ICON
  190. "DC", // WOWCLASS_DC
  191. "Font", // WOWCLASS_FONT
  192. "MetaFile", // WOWCLASS_METAFILE
  193. "Region", // WOWCLASS_RGN
  194. "Bitmap", // WOWCLASS_BITMAP
  195. "Brush", // WOWCLASS_BRUSH
  196. "Palette", // WOWCLASS_PALETTE
  197. "Pen", // WOWCLASS_PEN
  198. "Object" // WOWCLASS_OBJECT
  199. };
  200. BOOL MessageNeedsThunking(UINT uMsg)
  201. {
  202. switch (uMsg) {
  203. case WM_CREATE:
  204. case WM_ACTIVATE:
  205. case WM_SETFOCUS:
  206. case WM_KILLFOCUS:
  207. case WM_SETTEXT:
  208. case WM_GETTEXT:
  209. case WM_ERASEBKGND:
  210. case WM_WININICHANGE:
  211. case WM_DEVMODECHANGE:
  212. case WM_ACTIVATEAPP:
  213. case WM_SETCURSOR:
  214. case WM_MOUSEACTIVATE:
  215. case WM_GETMINMAXINFO:
  216. case WM_ICONERASEBKGND:
  217. case WM_NEXTDLGCTL:
  218. case WM_DRAWITEM:
  219. case WM_MEASUREITEM:
  220. case WM_DELETEITEM:
  221. case WM_VKEYTOITEM:
  222. case WM_CHARTOITEM:
  223. case WM_SETFONT:
  224. case WM_GETFONT:
  225. case WM_QUERYDRAGICON:
  226. case WM_COMPAREITEM:
  227. case WM_OTHERWINDOWCREATED:
  228. case WM_OTHERWINDOWDESTROYED:
  229. case WM_COMMNOTIFY:
  230. case WM_WINDOWPOSCHANGING:
  231. case WM_WINDOWPOSCHANGED:
  232. case WM_NCCREATE:
  233. case WM_NCCALCSIZE:
  234. case WM_COMMAND:
  235. case WM_HSCROLL:
  236. case WM_VSCROLL:
  237. case WM_INITMENU:
  238. case WM_INITMENUPOPUP:
  239. case WM_MENUSELECT:
  240. case WM_MENUCHAR:
  241. case WM_ENTERIDLE:
  242. case WM_CTLCOLORMSGBOX:
  243. case WM_CTLCOLOREDIT:
  244. case WM_CTLCOLORLISTBOX:
  245. case WM_CTLCOLORBTN:
  246. case WM_CTLCOLORDLG:
  247. case WM_CTLCOLORSCROLLBAR:
  248. case WM_CTLCOLORSTATIC:
  249. case WM_PARENTNOTIFY:
  250. case WM_MDICREATE:
  251. case WM_MDIDESTROY:
  252. case WM_MDIACTIVATE:
  253. case WM_MDIGETACTIVE:
  254. case WM_MDISETMENU:
  255. case WM_RENDERFORMAT:
  256. case WM_PAINTCLIPBOARD:
  257. case WM_VSCROLLCLIPBOARD:
  258. case WM_SIZECLIPBOARD:
  259. case WM_ASKCBFORMATNAME:
  260. case WM_CHANGECBCHAIN:
  261. case WM_HSCROLLCLIPBOARD:
  262. case WM_PALETTEISCHANGING:
  263. case WM_PALETTECHANGED:
  264. case MM_JOY1MOVE:
  265. case MM_JOY2MOVE:
  266. case MM_JOY1ZMOVE:
  267. case MM_JOY2ZMOVE:
  268. case MM_JOY1BUTTONDOWN:
  269. case MM_JOY2BUTTONDOWN:
  270. case MM_JOY1BUTTONUP:
  271. case MM_JOY2BUTTONUP:
  272. case MM_MCINOTIFY:
  273. case MM_MCISYSTEM_STRING:
  274. case MM_WOM_OPEN:
  275. case MM_WOM_CLOSE:
  276. case MM_WOM_DONE:
  277. case MM_WIM_OPEN:
  278. case MM_WIM_CLOSE:
  279. case MM_WIM_DATA:
  280. case MM_MIM_OPEN:
  281. case MM_MIM_CLOSE:
  282. case MM_MIM_DATA:
  283. case MM_MIM_LONGDATA:
  284. case MM_MIM_ERROR:
  285. case MM_MIM_LONGERROR:
  286. case MM_MOM_OPEN:
  287. case MM_MOM_CLOSE:
  288. case MM_MOM_DONE:
  289. LOGDEBUG(LOG_IMPORTANT,
  290. ("MessageNeedsThunking: WM_msg %04x is not thunked\n", uMsg));
  291. return TRUE;
  292. default:
  293. return FALSE;
  294. }
  295. }
  296. #endif
  297. PTD ThreadProcID32toPTD(DWORD dwThreadID, DWORD dwProcessID)
  298. {
  299. PTD ptd, ptdThis;
  300. PWOAINST pWOA;
  301. //
  302. // If we have active child instances of WinOldAp,
  303. // try to map the process ID of a child Win32 app
  304. // to the corresponding WinOldAp PTD.
  305. //
  306. ptdThis = CURRENTPTD();
  307. EnterCriticalSection(&ptdThis->csTD);
  308. pWOA = ptdThis->pWOAList;
  309. while (pWOA && pWOA->dwChildProcessID != dwProcessID) {
  310. pWOA = pWOA->pNext;
  311. }
  312. if (pWOA) {
  313. ptd = pWOA->ptdWOA;
  314. LeaveCriticalSection(&ptdThis->csTD);
  315. } else {
  316. LeaveCriticalSection(&ptdThis->csTD);
  317. //
  318. // We didn't find a WinOldAp PTD to return, see
  319. // if the thread ID matches one of our app threads.
  320. //
  321. EnterCriticalSection(&gcsWOW);
  322. ptd = gptdTaskHead;
  323. while (ptd && ptd->dwThreadID != dwThreadID) {
  324. ptd = ptd->ptdNext;
  325. }
  326. LeaveCriticalSection(&gcsWOW);
  327. }
  328. return ptd;
  329. }
  330. PTD Htask16toPTD(
  331. HTASK16 htask16
  332. ) {
  333. PTD ptd;
  334. EnterCriticalSection(&gcsWOW);
  335. ptd = gptdTaskHead;
  336. while(ptd) {
  337. if ( ptd->htask16 == htask16 ) {
  338. break;
  339. }
  340. ptd = ptd->ptdNext;
  341. }
  342. LeaveCriticalSection(&gcsWOW);
  343. return ptd;
  344. }
  345. HTASK16 ThreadID32toHtask16(
  346. DWORD ThreadID32
  347. ) {
  348. PTD ptd;
  349. HTASK16 htask16;
  350. if ( ThreadID32 == 0 ) {
  351. WOW32ASSERTMSG(ThreadID32, "WOW::ThreadID32tohTask16: Thread ID is 0\n");
  352. htask16 = 0;
  353. } else {
  354. ptd = ThreadProcID32toPTD( ThreadID32, (DWORD)-1 );
  355. if ( ptd ) {
  356. // Good, its one of our wow threads.
  357. htask16 = ptd->htask16;
  358. } else {
  359. // Nope, its is some other 32-bit thread
  360. htask16 = FindHtaskAlias( ThreadID32 );
  361. if ( htask16 == 0 ) {
  362. //
  363. // See the comment in WOLE2.C for a nice description
  364. //
  365. htask16 = AddHtaskAlias( ThreadID32 );
  366. }
  367. }
  368. }
  369. return htask16;
  370. }
  371. DWORD Htask16toThreadID32(
  372. HTASK16 htask16
  373. ) {
  374. if ( htask16 == 0 ) {
  375. return( 0 );
  376. }
  377. if ( ISTASKALIAS(htask16) ) {
  378. return( GetHtaskAlias(htask16,NULL) );
  379. } else {
  380. return( THREADID32(htask16) );
  381. }
  382. }
  383. //***************************************************************************
  384. // GetGCL_HMODULE - returns the valid hmodule if the window corresponds to
  385. // a 16bit class else returns the hmodule of 16bit user.exe
  386. // if the window is of a standard class.
  387. //
  388. // These cases are required for compatibility sake.
  389. // apps like VirtualMonitor, hDC etc depend on such behaviour.
  390. // - Nanduri
  391. //***************************************************************************
  392. WORD gUser16hInstance = 0;
  393. ULONG GetGCL_HMODULE(HWND hwnd)
  394. {
  395. ULONG ul;
  396. PTD ptd;
  397. PWOAINST pWOA;
  398. DWORD dwProcessID;
  399. ul = (ULONG)GetClassLong(hwnd, GCL_HMODULE);
  400. //
  401. // hMod32 = 0xZZZZ0000
  402. //
  403. if (ul != 0 && LOWORD(ul) == 0) {
  404. //
  405. // If we have active WinOldAp children, see if this window
  406. // belongs to a Win32 process spawned by one of the
  407. // active winoldap's. If it is, return the hmodule
  408. // of the corresponding winoldap. Otherwise we
  409. // return user.exe's hinstance (why not hmodule?)
  410. //
  411. dwProcessID = (DWORD)-1;
  412. GetWindowThreadProcessId(hwnd, &dwProcessID);
  413. ptd = CURRENTPTD();
  414. EnterCriticalSection(&ptd->csTD);
  415. pWOA = ptd->pWOAList;
  416. while (pWOA && pWOA->dwChildProcessID != dwProcessID) {
  417. pWOA = pWOA->pNext;
  418. }
  419. if (pWOA) {
  420. ul = pWOA->ptdWOA->hMod16;
  421. LOGDEBUG(LOG_ALWAYS, ("WOW32 GetClassLong(0x%x, GWW_HMODULE) returning 0x%04x\n",
  422. hwnd, ul));
  423. } else {
  424. ul = (ULONG) gUser16hInstance;
  425. WOW32ASSERT(ul);
  426. }
  427. LeaveCriticalSection(&ptd->csTD);
  428. }
  429. else {
  430. ul = (ULONG)GETHMOD16(ul); // 32-bit hmod is HMODINST32
  431. }
  432. return ul;
  433. }
  434. //
  435. // EXPORTED handle mapping functions. WOW32 code should use the
  436. // macros defined in walias.h -- these functions are for use by
  437. // third-party 32-bit code running in WOW, for example called
  438. // using generic thunks from WOW-specific 16-bit code.
  439. //
  440. HANDLE WOWHandle32 (WORD h16, WOW_HANDLE_TYPE htype)
  441. {
  442. switch (htype) {
  443. case WOW_TYPE_HWND:
  444. return HWND32(h16);
  445. case WOW_TYPE_HMENU:
  446. return HMENU32(h16);
  447. case WOW_TYPE_HDWP:
  448. return HDWP32(h16);
  449. case WOW_TYPE_HDROP:
  450. return HDROP32(h16);
  451. case WOW_TYPE_HDC:
  452. return HDC32(h16);
  453. case WOW_TYPE_HFONT:
  454. return HFONT32(h16);
  455. case WOW_TYPE_HMETAFILE:
  456. return HMETA32(h16);
  457. case WOW_TYPE_HRGN:
  458. return HRGN32(h16);
  459. case WOW_TYPE_HBITMAP:
  460. return HBITMAP32(h16);
  461. case WOW_TYPE_HBRUSH:
  462. return HBRUSH32(h16);
  463. case WOW_TYPE_HPALETTE:
  464. return HPALETTE32(h16);
  465. case WOW_TYPE_HPEN:
  466. return HPEN32(h16);
  467. case WOW_TYPE_HACCEL:
  468. return HACCEL32(h16);
  469. case WOW_TYPE_HTASK:
  470. return (HANDLE)HTASK32(h16);
  471. case WOW_TYPE_FULLHWND:
  472. return (HANDLE)FULLHWND32(h16);
  473. default:
  474. return(INVALID_HANDLE_VALUE);
  475. }
  476. }
  477. WORD WOWHandle16 (HANDLE h32, WOW_HANDLE_TYPE htype)
  478. {
  479. switch (htype) {
  480. case WOW_TYPE_HWND:
  481. return GETHWND16(h32);
  482. case WOW_TYPE_HMENU:
  483. return GETHMENU16(h32);
  484. case WOW_TYPE_HDWP:
  485. return GETHDWP16(h32);
  486. case WOW_TYPE_HDROP:
  487. return GETHDROP16(h32);
  488. case WOW_TYPE_HDC:
  489. return GETHDC16(h32);
  490. case WOW_TYPE_HFONT:
  491. return GETHFONT16(h32);
  492. case WOW_TYPE_HMETAFILE:
  493. return GETHMETA16(h32);
  494. case WOW_TYPE_HRGN:
  495. return GETHRGN16(h32);
  496. case WOW_TYPE_HBITMAP:
  497. return GETHBITMAP16(h32);
  498. case WOW_TYPE_HBRUSH:
  499. return GETHBRUSH16(h32);
  500. case WOW_TYPE_HPALETTE:
  501. return GETHPALETTE16(h32);
  502. case WOW_TYPE_HPEN:
  503. return GETHPEN16(h32);
  504. case WOW_TYPE_HACCEL:
  505. return GETHACCEL16(h32);
  506. case WOW_TYPE_HTASK:
  507. return GETHTASK16(h32);
  508. default:
  509. return(0xffff);
  510. }
  511. }
  512. extern PVOID GdiQueryTable();
  513. PVOID gpGdiHandleInfo = (PVOID)-1;
  514. //WARNING: This structure must match ENTRY in ntgdi\inc\hmgshare.h
  515. // and in ..\vdmexts\wow.c
  516. typedef struct _ENTRYWOW
  517. {
  518. LONG l1;
  519. LONG l2;
  520. USHORT FullUnique;
  521. USHORT us1;
  522. LONG l3;
  523. } ENTRYWOW, *PENTRYWOW;
  524. /*++
  525. Notes on GDI handle mapping:
  526. Since NT 3.1, GDI has been limited to handles that had values < 16K. The reason
  527. for this limitation is not known. But we do know that on Windows 3.1 the same
  528. limitation existed since GDI handles were really just hLocalMem handles which
  529. in reality were just offsets into GDI's local heap. The local heap manager
  530. always returned handles with the two lowest bits set to 0. The 2nd bit, the 2's
  531. bit, was used by the Win 3.1 heap manager to mark memory as fixed. The 1's bit
  532. we assume was not set because memory offsets probably weren't odd. Therefore
  533. they only had 14-bits available for handle values. There are notes in WOW that
  534. 16-bit applications were aware of this and used the two lowest bits for their
  535. own evil purposes. In fact, we use the lowest bit at times in WOW for evil
  536. purposes of our own! (see notes in GetDC() thunk in wuser.c)
  537. GDI32 handles are made up of two parts, the loword of the handle is the handle
  538. value which, until Windows XP, was < 16K as mentioned above. The hiword of the
  539. handle consists of a bunch of "uniqueness" (bad name for these really) bits.
  540. Prior to Windows XP, WOW thunked GDI32 handles by stripping off the hiword
  541. uniqueness bits and then left-shifted the loword handle value by 2 -- exposing
  542. the two low order bits for apps to use to their heart's content. Since the
  543. handle value was < 16K, we didn't lose any relevant handle information by left-
  544. shiftng by two. To un-thunk the handle back to 32-bits, we right-shifted it by
  545. 2 and OR'd the uniqueness bits back onto the hiword (we get the uniqueness bits
  546. from a table that GDI32 exposes to us). This scheme allowed a very nice one-to-
  547. one mapping of the handles back-and-forth so there was never any need to create
  548. a mapping table.
  549. Enter Windows XP. The GDI group, with good reason, needed to increase the
  550. number of handles system-wide, so they changed their handles to use all 16-bits
  551. in the loword handle values. Unfortunately, when we do our left-shift thunk
  552. thing, any handles values > 16K get trashed and things go south pretty quickly.
  553. We found this whole issue out very late in the XP client ship cycle (3 weeks to
  554. RTM) and couldn't do too much about it at that point. So we just tested the
  555. handles values to see if they were > 16K and told the user that the 16-bit
  556. subsystem was out of resources & that they needed to reboot their system --
  557. then we killed the VDM. Gack! Not something somebody running an enterprise
  558. server wants to see. So...
  559. For the .NET Server & SP1 releases of XP we decided that we needed to come up
  560. with a mapping algorithm that allows WOW to use handles with values above 16K.
  561. Here it is:
  562. 1. We allocate a mapping table with 64K entries to accomodate all possible
  563. 32-bit handles. The loword of an h32 is used to index directly into this
  564. table. An entry in this table is the index of the corresponding h16 in the
  565. h16 mapping table.
  566. 2. We reserve a mapping table with 16K entries in virtual memory. Each entry
  567. in the table contains the corresponding h32, an index to the next free entry
  568. in the table, and a State. The State can be one of the following: IN_USE,
  569. SLOT_FREE, H16_DELETE, and GDI_STOCK_OBJECT. Initially we only commit one
  570. "table page" of memory in the table -- enough to map 1K handles.
  571. 3. When an app calls an API such as CreateBrush(), we map the returned handle
  572. into the first available slot in the free list. The index of the selected
  573. slot is left-shifted by 2 (to open up the lower two bits as before) and that
  574. value is used as the h16 that we return to the app.
  575. 4. When the app calls an API using an h16 such as SelectObject(), we right
  576. shift the handle back into an index into our table and retreive the mapped
  577. h32 that we stored at the index location.
  578. 5. When an app calls DeleteObject(), we originally released all the mapping
  579. information and returned the associated slot index to the end of the free
  580. list. This didn't work so good. We found that many apps will try to use old
  581. handles that they already free'd. They also depend on getting back the same
  582. handle value that they had just free'd -- not good. So, we implemented a
  583. "lazy-deletion" algorithm to leave all the mapping info in the table as long
  584. as possible before finally returning it to the free list. Things got much
  585. better. We also try to re-map recycled 32-bit handles back to the same index
  586. mapping that they previously had.
  587. 6. If our free list becomes empty (most slots are marked IN_USE or H16_DELETE
  588. for lazy-deletion) we will be forced into a reclaim function to try to
  589. reclaim leaked handles (handles that an app created but never deleted) and
  590. also finally free *some* of the lazy-delete handles. Reclaimed handles are
  591. added to the end of the free list. We try not to reclaim all the lazy-delete
  592. handles during reclaim because that would kind of put a hiccup in our lazy
  593. deletion scheme. If we are unable to reclaim enough handles, we then commit
  594. a new table page from our virtual memory reserve and add the new slots to
  595. the front of the free list.
  596. 7. We do have to be careful of handle leaks in our table. One potential leak is
  597. for messages of the WM_PAINTCLIPBOARD nature. Assume that a 16-bit app put
  598. something on the clipboard in a format only understood by that app. Now, a
  599. 32-bit app wants to paste what is on the clipboard onto his client window.o After a query, the 32-bit app finds that our 16-bit can do the painting for
  600. him, so the 32-bit app sends a WM_PAINTCLIPBOARD message to the 16-bit app
  601. complete with an hDC to 32-bit client window. Problem is, GDI handles are
  602. only good in the process they were created in. User32, intercepts the
  603. message and just before dispatching it to the 16-bit app, it essentially
  604. does a CreateCompatibleDC() in the context of the 16-bit app's process and
  605. passes on the new handle. Works great. We just need to make sure that we
  606. know when to un-map the new handle from our table or we'll get a leak. See
  607. code for WM_PAINTCLIPBOARD in wmdisp32.c to see how we do this. There are
  608. other issues of this nature that aren't so easy because there are no
  609. reliable clues as to when we can delete these handles that are created by
  610. external elements on our behalf. To try to keep our table fromm leaking too
  611. badly we check all handles in our table at reclaim time to see if they are
  612. still valid. And finally, if the only running task in the VDM is wowexec,
  613. we just throw away the tables altogether & re-build them from scratch.
  614. --*/
  615. /*+++
  616. Here are some restrictions to 16-bit GDI handles:
  617. - An h16 can't = 0 since that means failure from API's that return handles.
  618. If an app specifies an hDC = 0, it usually means the DISPLAY DC.
  619. - We can't give out handles with values <= COLOR_ENDCOLORS since the
  620. hbrBackground member of WNDCLASS structs can specify ordinals in that
  621. range.
  622. - We can't give out handle values > 0x3FFF (16K).
  623. - GDI16 caches the stock objects at WOW32 boot time. We need to make sure
  624. that the stock object handles are always mapped the same -- across all
  625. WOW processes.
  626. So we reduce the size of our table to deal with handles in the range of
  627. COLOR_ENDCOLORS+1 -> 0x3FFF. Since we left shift table index values by 2 (ie.
  628. multiply by 4) to get the h16 we give to the app, we can calculate the first
  629. allowable index value by COLOR_ENDCOLORS/4 + 1.
  630. --*/
  631. #define FIRST_ALLOWABLE_INDEX ((COLOR_ENDCOLORS/4) + 1)
  632. #define LAST_ALLOWABLE_INDEX 0x3FFF // Allows for
  633. // FIRST_ALLOWABLE_INDEX -> 0x3FFF entries
  634. // These two constants give us the absolute maximum number of GDI16 handles
  635. // we can support and the maximum size of the GDI16 handle table.
  636. // If the table is static we should define MAX_GDI16_HANDLES as follows:
  637. //#define MAX_GDI16_HANDLES ((LAST_ALLOWABLE_INDEX - FIRST_ALLOWABLE_INDEX) + 1)
  638. // Since the 16-bit mapping table is to grow dynamically, we'll make all pages
  639. // the same size to simplify things and waste the first few entries in the first
  640. // page.
  641. #define MAX_GDI16_HANDLES 0x4000 // 16K handles
  642. #define MAX_GDI16_HANDLE_TABLE_SIZE (MAX_GDI16_HANDLES * sizeof(GDIH16MAP))
  643. #define GDI16_HANDLES_PER_PAGE 512
  644. #define GDI16_HANDLE_PAGE_SIZE (GDI16_HANDLES_PER_PAGE * sizeof(GDIH16MAP))
  645. // This table *has to* have 64K entries so we can index the 32-bit GDI handles
  646. // directly by the low word of the h32.
  647. #define GDI32_HANDLE_TABLE_ENTRIES 0x10000
  648. #define GDI32_HANDLE_TABLE_SIZE (GDI32_HANDLE_TABLE_ENTRIES * sizeof(GDIH32MAP))
  649. // Max number of handles to reclaim when we're short
  650. #define GDI16_RECLAIM_SIZE 64
  651. WORD MapGdi32Handle(HANDLE h32, WORD State);
  652. void RegisterStockObjects(void);
  653. BOOL ReclaimTableEntries(void);
  654. BOOL DeleteMappedGdi32Handle(HANDLE h32, WORD index, BOOL bReclaim);
  655. BOOL OkToDeleteThis(HANDLE h32, WORD index, BOOL bReclaim);
  656. BOOL CommitNewGdi16TablePage(PGDIH16MAP pTable16);
  657. PGDIH16MAP AllocGDI16Table(void);
  658. // This is a global so it can be tuned via app comaptflag if necessary.
  659. int gGdi16ReclaimSize = GDI16_RECLAIM_SIZE;
  660. WORD gH16_deleted_count = 0;
  661. WORD ghGdi16NextFree = 0;
  662. WORD ghGdi16LastFree = 0;
  663. HANDLE hGdi32TableHeap = NULL;
  664. UINT gMaxGdiHandlesPerProcess = 0;
  665. WORD gLastAllowableIndex = 0;
  666. WORD gFirstNonStockObject = 0;
  667. WORD gwNextReclaimStart = 0;
  668. DWORD gdwPageCommitSize = 0;
  669. PGDIH16MAP pGdiH16MappingTable = NULL;
  670. PGDIH32MAP pGdiH32MappingTable = NULL;
  671. #ifdef DEBUG
  672. WORD gprevNextFree = 0xFFFF;
  673. UINT gAllocatedHandleCount = 0;
  674. #endif
  675. // This routine converts a 16bit GDI handle to a GDI32 handle.
  676. HANDLE hConvert16to32(int h16)
  677. {
  678. WORD index;
  679. DWORD h32;
  680. DWORD h_32;
  681. // Apps can specify a NULL handle. For instance hDC = NULL => DISPLAY
  682. if(h16 == 0)
  683. return(0);
  684. // right shift out our left shift thing
  685. index = (WORD)(h16 >> 2);
  686. if((index < FIRST_ALLOWABLE_INDEX) || (index > gLastAllowableIndex)) {
  687. // Some WM_CTLCOLOR messages return wierd values for brushes
  688. WOW32WARNMSG((FALSE),"WOW::hConvert16to32:Bad index value!\n");
  689. // bad handles get mapped to 0
  690. return(0);
  691. }
  692. h32 = (DWORD)pGdiH16MappingTable[index].h32;
  693. WOW32WARNMSG((h32),"WOW::hConvert16to32:h32 missing from table!\n");
  694. // We might get this because an app is using a handle it already deleted.
  695. // These can probably be ignored if they come from ReleaseCachedDCs().
  696. WOW32WARNMSG((pGdiH32MappingTable[LOWORD(h32)].h16index == index),
  697. "WOW::hConvert16to32:indicies don't jive!\n");
  698. // Update the uniqueness bits to match what they currently are in GDI32's
  699. // handle table. This will give us the "previous" behavior.
  700. // See bug #498038 -- we might possibly want to remove this.
  701. h_32 = h32 & 0x0000FFFF;
  702. h_32 = h_32 | (DWORD)(((PENTRYWOW)gpGdiHandleInfo)[h_32].FullUnique) << 16;
  703. if(h32 != h_32) {
  704. WOW32WARNMSG((FALSE),"WOW::hConvert16to32:uniqueness bits !=\n");
  705. h32 = h_32;
  706. pGdiH16MappingTable[index].h32 = (HANDLE)h32;
  707. }
  708. return((HANDLE)h32);
  709. }
  710. // This routine converts a GDI32 handle to a 16bit GDI handle
  711. HAND16 hConvert32to16(DWORD h32)
  712. {
  713. WORD index;
  714. WORD State;
  715. HANDLE h_32;
  716. // A handle == 0 isn't necessarily bad, we just don't do anything with it.
  717. if(h32 == 0) {
  718. return(0);
  719. }
  720. // See if we have already mapped a 16-bit version of this handle
  721. index = pGdiH32MappingTable[LOWORD(h32)].h16index;
  722. h_32 = pGdiH16MappingTable[index].h32;
  723. State = pGdiH16MappingTable[index].State;
  724. // If it looks like we may have already registered this handle...
  725. if(index) {
  726. WOW32ASSERTMSG((index <= gLastAllowableIndex),
  727. "WOW::hConvert32to16:Bad index!\n");
  728. // Verify the handle indicies match
  729. if(LOWORD(h32) == LOWORD(h_32)) {
  730. // If the 16-bit mapping is marked "IN_USE" will be true for
  731. // two reasons:
  732. // 1. The mapping is still valid
  733. // 2. h32 got deleted without our knowledge and is comming back as
  734. // a recycled handle. We might as well use the same mapping as
  735. // before.
  736. if(State == IN_USE) {
  737. // All we need to do is effectively update the uniqueness bits
  738. // in the 16-bit table entry.
  739. if(HIWORD(h32) != HIWORD(h_32)) {
  740. LOGDEBUG(12, ("WOW::hConvert32to16:recycled handle!\n"));
  741. pGdiH16MappingTable[index].h32 = (HANDLE)h32;
  742. }
  743. }
  744. // If the mapping was marked for deletion, let's renew it. h32 has
  745. // been recycled. We might as well use the same mapping as before.
  746. else if(State == H16_DELETED) {
  747. pGdiH16MappingTable[index].h32 = (HANDLE)h32;
  748. pGdiH16MappingTable[index].State = IN_USE;
  749. gH16_deleted_count--;
  750. }
  751. // else h32 is a GDI_STOCK_OBJECT in which case the index is OK
  752. else if(State != GDI_STOCK_OBJECT) {
  753. WOW32ASSERTMSG((FALSE),"WOW::hConvert32to16:SLOT_FREE!\n");
  754. return(0); // debug this
  755. }
  756. }
  757. // Else if the handle indicies don't match, the h32 got deleted without
  758. // our knowledge and is now coming back as a recycled handle. We'll use
  759. // the same mapping as before.
  760. else {
  761. pGdiH16MappingTable[index].h32 = (HANDLE)h32;
  762. pGdiH16MappingTable[index].State = IN_USE;
  763. }
  764. }
  765. // looks like we need to create a new mapping
  766. else {
  767. index = MapGdi32Handle((HANDLE)h32, IN_USE);
  768. }
  769. // If we couldn't get an index, go see if we can reclaim some entries and
  770. // find one.
  771. if(!index) {
  772. if(ReclaimTableEntries()) {
  773. index = MapGdi32Handle((HANDLE)h32, IN_USE);
  774. }
  775. }
  776. if((index < FIRST_ALLOWABLE_INDEX) || (index > gLastAllowableIndex)) {
  777. #ifdef DEBUG
  778. if(index < FIRST_ALLOWABLE_INDEX) {
  779. WOW32ASSERTMSG((FALSE),"WOW::hConvert32to16:index too small!\n");
  780. }
  781. else {
  782. WOW32ASSERTMSG((FALSE),"WOW::hConvert32to16:index too big!\n");
  783. }
  784. #endif
  785. return(0);
  786. }
  787. // Do our left shift thing and we're done
  788. return(index << 2);
  789. }
  790. WORD MapGdi32Handle(HANDLE h32, WORD State)
  791. {
  792. WORD index = 0;
  793. // If all free entries are gone -- nothing to do
  794. if(ghGdi16NextFree != END_OF_LIST) {
  795. index = ghGdi16NextFree;
  796. ghGdi16NextFree = pGdiH16MappingTable[index].NextFree;
  797. #ifdef DEBUG
  798. if(ghGdi16NextFree == END_OF_LIST) {
  799. gprevNextFree = index;
  800. WOW32WARNMSG((FALSE),"WOW::MapGdi32Handle:Bad NextFree!\n");
  801. }
  802. #endif
  803. // Set the state (either IN_USE or GDI_STOCK_OBJECT)
  804. pGdiH16MappingTable[index].State = State;
  805. // Map the 32bit handle with the 16-bit handle
  806. pGdiH16MappingTable[index].h32 = h32;
  807. pGdiH32MappingTable[LOWORD(h32)].h16index = index;
  808. #ifdef DEBUG
  809. gAllocatedHandleCount++;
  810. #endif
  811. }
  812. return(index);
  813. }
  814. // Requires that index & h32 are both non-null.
  815. // Assumes that index is an *index* and *not* an h16 (not an index << 2).
  816. // bReclaim specifies that the handle needs to be added to the freelist.
  817. BOOL DeleteMappedGdi32Handle(HANDLE h32, WORD index, BOOL bReclaim)
  818. {
  819. BOOL bRet = FALSE;
  820. if(OkToDeleteThis(h32, index, bReclaim)) {
  821. // We don't actually want to *remove* the mapping from our table unless
  822. // we are reclaiming entries. Lame apps call us with old deleted handles
  823. // and we want to pass on the same mapping that they had when the handle
  824. // was still good. This also allows us to reuse old mappings for h32
  825. // handles that get recycled.
  826. if(bReclaim) {
  827. // re-initialized the slot
  828. pGdiH16MappingTable[index].State = SLOT_FREE;
  829. pGdiH16MappingTable[index].h32 = NULL;
  830. pGdiH16MappingTable[index].NextFree = END_OF_LIST;
  831. // add this slot to the end of the free list
  832. pGdiH16MappingTable[ghGdi16LastFree].NextFree = index;
  833. pGdiH32MappingTable[LOWORD(h32)].h16index = 0;
  834. ghGdi16LastFree = index;
  835. #ifdef DEBUG
  836. if(gAllocatedHandleCount > 0) gAllocatedHandleCount--;
  837. #endif
  838. }
  839. // else just mark this as potentially deleted
  840. else {
  841. pGdiH16MappingTable[index].State = H16_DELETED;
  842. gH16_deleted_count++;
  843. }
  844. bRet = TRUE;
  845. }
  846. return(bRet);
  847. }
  848. // Check list of "What can go wrong?'s"
  849. BOOL OkToDeleteThis(HANDLE h32, WORD index, BOOL bReclaim)
  850. {
  851. HANDLE h_32;
  852. WORD NextFree;
  853. WORD index16;
  854. WORD State;
  855. // If we see this, it may be an app error calling DeleteObject(0).
  856. if(index == 0) {
  857. WOW32WARNMSG((FALSE),"WOW::OkToDeleteThis:Null index");
  858. return(FALSE);
  859. }
  860. // Debug why we get this
  861. if(h32 == NULL) {
  862. WOW32ASSERTMSG((FALSE),"WOW::OkToDeleteThis:Null h32\n");
  863. return(FALSE);
  864. }
  865. // Debug why we get this. May be just an app being stupid
  866. if((index < FIRST_ALLOWABLE_INDEX) || (index > gLastAllowableIndex)) {
  867. WOW32ASSERTMSG((FALSE),"WOW::OkToDeleteThis:index bad\n");
  868. return(FALSE);
  869. }
  870. h_32 = pGdiH16MappingTable[index].h32;
  871. State = pGdiH16MappingTable[index].State;
  872. NextFree = pGdiH16MappingTable[index].NextFree;
  873. index16 = pGdiH32MappingTable[LOWORD(h32)].h16index;
  874. // Don't remove stock objects from the table!
  875. if(State == GDI_STOCK_OBJECT) {
  876. return(FALSE);
  877. }
  878. // Check for the "IN_USE" flag. If it isn't currently in use then it is
  879. // already in the free list and we don't want to mess with it. Apps
  880. // have been known to call DeleteObject() twice on the same handle.
  881. if(!bReclaim && (State != IN_USE)) {
  882. WOW32WARNMSG((FALSE),"WOW::OkToDeleteThis:Not IN_USE\n");
  883. return(FALSE);
  884. }
  885. // We should allow this since the index is obviously pointing to an old h32.
  886. if(h32 != h_32) {
  887. WOW32WARNMSG((FALSE),"WOW::OkToDeleteThis:h32 != h_32\n");
  888. return(TRUE);
  889. }
  890. // Don't mark it for delete if the object is still valid.
  891. if(GetObjectType(h32)) {
  892. return(FALSE);
  893. }
  894. #ifdef DEBUG
  895. // Debug this. We should probably let the table repair itself.
  896. if(index16 == 0) {
  897. WOW32ASSERTMSG((FALSE),"WOW::OkToDeleteThis:index=0 in h32 table\n");
  898. return(TRUE);
  899. }
  900. #endif
  901. return(TRUE);
  902. }
  903. // This should be called by functions outside this file to delete mapped h16's.
  904. void DeleteWOWGdiHandle(HANDLE h32, HAND16 h16)
  905. {
  906. WORD index;
  907. // convert h16 back into table index
  908. index = (WORD)(h16 >> 2);
  909. DeleteMappedGdi32Handle(h32, index, FALSE);
  910. }
  911. // This should be called by functions outside this file to retrieve the WOWInfo
  912. // associated with an h32.
  913. // Returns:
  914. // The 16-bit mapping for this h32
  915. // 0 - if h32 is valid but not mapped in our table
  916. // -1 - if h32 is 0 or bad (BAD_GDI32_HANDLE)
  917. HAND16 IsGDIh32Mapped(HANDLE h32)
  918. {
  919. WORD index;
  920. HANDLE h_32 = NULL;
  921. if(h32) {
  922. if(GetObjectType(h32)) {
  923. index = pGdiH32MappingTable[LOWORD(h32)].h16index;
  924. if(index) {
  925. h_32 = pGdiH16MappingTable[index].h32;
  926. }
  927. if(h_32 == h32) {
  928. return((HAND16)index<<2);
  929. }
  930. return(0);
  931. }
  932. }
  933. return(BAD_GDI32_HANDLE);
  934. }
  935. #if 0
  936. // This is really for a sanity check that the GDI guys haven't increased the
  937. // GdiProcessHandleQuota past 16K.
  938. int DisplayYouShouldNotDoThatMsg(int nMsg)
  939. {
  940. CHAR szWarn[512];
  941. CHAR szText[256];
  942. LoadString(hmodWOW32,
  943. iszYouShouldNotDoThat,
  944. szWarn,
  945. sizeof(szWarn)/sizeof(CHAR));
  946. LoadString(hmodWOW32,
  947. nMsg,
  948. szText,
  949. sizeof(szText)/sizeof(CHAR));
  950. if((strlen(szWarn) + strlen(szText)) < 512) {
  951. strcat(szWarn, szText);
  952. }
  953. LoadString(hmodWOW32,
  954. iszHeavyUse,
  955. szText,
  956. sizeof(szText)/sizeof(CHAR));
  957. if((strlen(szWarn) + strlen(szText)) < 512) {
  958. strcat(szWarn, szText);
  959. }
  960. return(MessageBox(NULL,
  961. szWarn,
  962. NULL,
  963. MB_YESNO |
  964. MB_DEFBUTTON2 |
  965. MB_SYSTEMMODAL |
  966. MB_ICONEXCLAMATION));
  967. }
  968. #endif
  969. BOOL InitializeGdiHandleMappingTable(void)
  970. {
  971. HKEY hKey = 0;
  972. DWORD dwType;
  973. DWORD cbSize = sizeof(DWORD);
  974. CHAR szError[256];
  975. gpGdiHandleInfo = GdiQueryTable();
  976. // The GDI per-process handle limit has to be obtained from the registry.
  977. // ie. It can be changed by a user! (not doc'd but you know how that goes)
  978. if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  979. "Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows",
  980. 0,
  981. KEY_READ,
  982. &hKey ) == ERROR_SUCCESS) {
  983. RegQueryValueEx(hKey,
  984. "GdiProcessHandleQuota",
  985. 0,
  986. &dwType,
  987. (LPBYTE)&gMaxGdiHandlesPerProcess,
  988. &cbSize);
  989. RegCloseKey(hKey);
  990. }
  991. WOW32ASSERTMSG((gMaxGdiHandlesPerProcess != 0),
  992. "WOW::InitializeGdiHandleMappingTable:Default GDI max!\n");
  993. // We have to be <= ~16K or we wouldn't have to do all this stuff.
  994. if(gMaxGdiHandlesPerProcess > MAX_GDI16_HANDLES) {
  995. // limit them at the max.
  996. gMaxGdiHandlesPerProcess = MAX_GDI16_HANDLES;
  997. }
  998. // Allocate the 32-bit handle mapping table.
  999. hGdi32TableHeap = HeapCreate(HEAP_NO_SERIALIZE,
  1000. GDI32_HANDLE_TABLE_SIZE,
  1001. GROW_HEAP_AS_NEEDED);
  1002. if(hGdi32TableHeap == NULL) {
  1003. goto IGHMT_error;
  1004. }
  1005. pGdiH32MappingTable = HeapAlloc(hGdi32TableHeap,
  1006. HEAP_ZERO_MEMORY,
  1007. GDI32_HANDLE_TABLE_SIZE);
  1008. if(pGdiH32MappingTable == NULL) {
  1009. goto IGHMT_error;
  1010. }
  1011. pGdiH16MappingTable = AllocGDI16Table();
  1012. if(pGdiH16MappingTable == NULL) {
  1013. goto IGHMT_error;
  1014. }
  1015. // Add the stock objects as the first entries in the GDI handle mapping
  1016. // tables.
  1017. RegisterStockObjects();
  1018. return(TRUE);
  1019. IGHMT_error:
  1020. cbSize = LoadString(hmodWOW32,
  1021. iszStartupFailed,
  1022. szError,
  1023. sizeof(szError)/sizeof(CHAR));
  1024. if((cbSize == 0) || (cbSize >= 256)) {
  1025. strcpy(szError, "Not enough memory to load 16-bit subsystem.");
  1026. }
  1027. WOWSysErrorBox(NULL, szError, SEB_OK, 0, 0);
  1028. if(pGdiH32MappingTable) {
  1029. HeapFree(hGdi32TableHeap, HEAP_NO_SERIALIZE, pGdiH32MappingTable);
  1030. }
  1031. if(hGdi32TableHeap) {
  1032. HeapDestroy(hGdi32TableHeap);
  1033. }
  1034. return(FALSE);
  1035. }
  1036. PGDIH16MAP AllocGDI16Table(void)
  1037. {
  1038. SIZE_T dwSize;
  1039. PGDIH16MAP pTable16;
  1040. #ifdef DEBUG
  1041. SYSTEM_INFO sSysInfo;
  1042. GetSystemInfo(&sSysInfo);
  1043. // If either of these go off we need to come up with a way to align our
  1044. // struct within memory page sizes.
  1045. WOW32ASSERTMSG((!(sSysInfo.dwPageSize % sizeof(GDIH16MAP))),
  1046. "WOW::AllocGDI16Table:Page alignment issue!\n");
  1047. WOW32ASSERTMSG(
  1048. (!((sizeof(GDIH16MAP) * GDI16_HANDLES_PER_PAGE) % sSysInfo.dwPageSize)),
  1049. "WOW::AllocGDI16Table:Page alignment issue 2!\n");
  1050. #endif
  1051. // Reserve the biggest table we'll ever need.
  1052. // (This is a call to VirtualAlloc() in disguise).
  1053. dwSize = MAX_GDI16_HANDLE_TABLE_SIZE;
  1054. pTable16 = NULL;
  1055. if(!NT_SUCCESS(NtAllocateVirtualMemory(ghProcess,
  1056. (PVOID *)&pTable16,
  1057. 0,
  1058. &dwSize,
  1059. MEM_RESERVE,
  1060. PAGE_READWRITE))) {
  1061. WOW32ASSERTMSG((FALSE),
  1062. "WOW::AllocGDI16Table:Alloc reserve failed!\n");
  1063. return(NULL);
  1064. }
  1065. // Commit the first "table page"
  1066. gdwPageCommitSize = 0;
  1067. gLastAllowableIndex = (WORD)-1; // cheese!
  1068. ghGdi16NextFree = END_OF_LIST;
  1069. if(!CommitNewGdi16TablePage(pTable16)) {
  1070. dwSize = 0;
  1071. NtFreeVirtualMemory(ghProcess,
  1072. (PVOID *)&pTable16,
  1073. &dwSize,
  1074. MEM_RELEASE);
  1075. return(NULL);
  1076. }
  1077. ghGdi16NextFree = FIRST_ALLOWABLE_INDEX; // adjust this for init case
  1078. ghGdi16LastFree = gLastAllowableIndex;
  1079. gH16_deleted_count = 0;
  1080. return(pTable16);
  1081. }
  1082. BOOL CommitNewGdi16TablePage(PGDIH16MAP pTable16)
  1083. {
  1084. WORD index;
  1085. WORD wFirstNewIndex, wLastNewIndex;
  1086. SIZE_T dwSize;
  1087. PVOID p;
  1088. // If we've allocated the last table page already -- we can't grow any more.
  1089. if(gdwPageCommitSize >= MAX_GDI16_HANDLE_TABLE_SIZE) {
  1090. WOW32WARNMSG((FALSE),
  1091. "WOW::CommitNewGDI16TablePage:End of table!\n");
  1092. return(FALSE);
  1093. }
  1094. // Try to grow the number of comitted pages in our table.
  1095. dwSize = GDI16_HANDLE_PAGE_SIZE;
  1096. p = (PVOID)(((LPBYTE)pTable16) + gdwPageCommitSize);
  1097. if(!NT_SUCCESS(NtAllocateVirtualMemory(ghProcess,
  1098. &p,
  1099. 0,
  1100. &dwSize,
  1101. MEM_COMMIT,
  1102. PAGE_READWRITE))) {
  1103. WOW32ASSERTMSG((FALSE),
  1104. "WOW::CommitNewGDI16TablePage:Commit failed!\n");
  1105. return(FALSE);
  1106. }
  1107. WOW32ASSERTMSG((dwSize == GDI16_HANDLE_PAGE_SIZE),
  1108. "WOW::CommitNewGDI16TablePage:Page boundary mismatch!\n");
  1109. // Build the free list in the new page.
  1110. // Note: NtAllocateVirtualMemory() zero init's memory, therefore the h32 and
  1111. // State members of each GDIH16MAP entry are NULL & SLOT_FREE by default.
  1112. wFirstNewIndex = gLastAllowableIndex + 1;
  1113. wLastNewIndex = wFirstNewIndex + GDI16_HANDLES_PER_PAGE - 1;
  1114. for(index = wFirstNewIndex; index < wLastNewIndex; index++) {
  1115. pTable16[index].NextFree = index + 1;
  1116. }
  1117. gLastAllowableIndex += GDI16_HANDLES_PER_PAGE;
  1118. // Put these new entries at the head of the free list.
  1119. pTable16[gLastAllowableIndex].NextFree = ghGdi16NextFree;
  1120. ghGdi16NextFree = wFirstNewIndex;
  1121. gdwPageCommitSize += GDI16_HANDLE_PAGE_SIZE;
  1122. return(TRUE);
  1123. }
  1124. // Add the list of stock objects to begining of our table.
  1125. void RegisterStockObjects(void)
  1126. {
  1127. WORD index = 0;
  1128. HANDLE h32;
  1129. int i;
  1130. for(i = WHITE_BRUSH; i <= STOCK_MAX; i++) {
  1131. h32 = GetStockObject(i);
  1132. // currently there is no stock object ordinal == 9
  1133. if(h32) {
  1134. // Marking the State as GDI_STOCK_OBJECT assures us that stock
  1135. // objects won't get deleted from the table.
  1136. index = MapGdi32Handle(h32, GDI_STOCK_OBJECT);
  1137. }
  1138. }
  1139. gFirstNonStockObject = index + 1;
  1140. gwNextReclaimStart = gFirstNonStockObject;
  1141. }
  1142. // Reclaim some of the lazily deleted handles (State == H16_DELETE) into the
  1143. // free list. Also attempt to clean up handles that may have leaked and are
  1144. // no longer valid.
  1145. BOOL ReclaimTableEntries(void)
  1146. {
  1147. WORD index;
  1148. WORD State;
  1149. WORD wReclaimStart, wReclaimEnd;
  1150. WORD PrevLastFree = ghGdi16LastFree;
  1151. HANDLE h32;
  1152. WORD i;
  1153. int cFree;
  1154. BOOL bFirst = TRUE;
  1155. // Note that we attempt to somewhat preserve the lazy-deletion scheme
  1156. // to avoid putting a large hiccup in the scheme.
  1157. // First determine if we can reclaim without deleting too many H16_DELETE
  1158. // entries. If we can't keep a minimal number around, it's time to commit
  1159. // a new table page.
  1160. if(gH16_deleted_count < (gGdi16ReclaimSize * 2)) {
  1161. if(CommitNewGdi16TablePage(pGdiH16MappingTable)) {
  1162. return(TRUE);
  1163. }
  1164. // if the commit failed, all we can do is reclaim
  1165. }
  1166. // This is an attempt to keep from always reclaiming from the front of the
  1167. // table. It might not be that important to do this as things get pretty
  1168. // well shuffled over time. Probably advisable during table's early life.
  1169. // The first time through we start at where we left off last time. The
  1170. // 2nd time through we start from the beginning.
  1171. wReclaimStart = gwNextReclaimStart;
  1172. wReclaimEnd = gLastAllowableIndex;
  1173. cFree = 0;
  1174. for(i = 0; i < 2; i++) {
  1175. for(index = wReclaimStart; index <= wReclaimEnd; index++){
  1176. State = pGdiH16MappingTable[index].State;
  1177. h32 = pGdiH16MappingTable[index].h32;
  1178. // If it is marked as deleted...
  1179. if(State == H16_DELETED) {
  1180. // ...destroy the mapping and add it to the free list
  1181. if(DeleteMappedGdi32Handle(h32, index, TRUE)) {
  1182. cFree++;
  1183. gH16_deleted_count--;
  1184. // If this is the first index we're reclaiming and the free
  1185. // list is empty, make index to the new previous lastfree
  1186. // the new start of the free list. This is kind of tricky.
  1187. // It will be the case where the call to MapGdi32Handle()
  1188. // returns index == 0 in hConvert32to16() and this function
  1189. // is called as a result. What has happened is that the
  1190. // ghGdi16NextFree ptr has caught up with ghGdi16LastFree
  1191. // ptr and then we get another call to hConvert32to16() to
  1192. // map a new h32. ghGdi16NextFree is now == 0 at this point
  1193. // so we fail the call to MapGdi32Handle(). This forces this
  1194. // reclaim function to be called. Here ghGdi16NextFree is
  1195. // set back to ghGdi16LastFree while ghGdi16LastFree will be
  1196. // set to the value of the first handle to be reclaimed via
  1197. // DeleteMappedGdi32Handle(). While in this reclaim loop,
  1198. // the freelist will quickly grow again and there will be
  1199. // plenty of room between ghGdi16NextFree & ghGdi16LastFree
  1200. // again.
  1201. if(bFirst && (ghGdi16NextFree == END_OF_LIST)) {
  1202. ghGdi16NextFree = PrevLastFree;
  1203. }
  1204. bFirst = FALSE;
  1205. // if we've reclaimed enough, we're done.
  1206. if(cFree >= gGdi16ReclaimSize) {
  1207. gwNextReclaimStart = index + 1;
  1208. goto Done;
  1209. }
  1210. }
  1211. }
  1212. // else if it is marked in use...
  1213. else if(State == IN_USE) {
  1214. // ...see if the handle is still valid, if not, it is a leak.
  1215. if(!GetObjectType(h32)) {
  1216. // Mark it H16_DELETED.
  1217. DeleteMappedGdi32Handle(h32, index, FALSE);
  1218. }
  1219. }
  1220. } // end for
  1221. wReclaimEnd = wReclaimStart;
  1222. wReclaimStart = gFirstNonStockObject;
  1223. // If we started at the beginning the first time, no sense in doing
  1224. // it again.
  1225. if(wReclaimEnd == gFirstNonStockObject) {
  1226. goto Done;
  1227. }
  1228. } // end for
  1229. gwNextReclaimStart = index;
  1230. Done:
  1231. if(gwNextReclaimStart == gLastAllowableIndex) {
  1232. gwNextReclaimStart = gFirstNonStockObject;
  1233. }
  1234. WOW32ASSERTMSG((cFree),"WOW::ReclaimTableEntries:cFree = 0!\n");
  1235. return(cFree);
  1236. }
  1237. // NOTE: This should only be called if nWOWTasks == 1!
  1238. // If the only task running is wowexec, we can cleanup any handles that
  1239. // the WOW process leaked.
  1240. // Note: This can break the debug wowexec if anybody ever changes it to use
  1241. // any GDI handles other than stock objects.
  1242. void RebuildGdiHandleMappingTables(void)
  1243. {
  1244. SIZE_T dwSize;
  1245. PGDIH16MAP pTable16 = pGdiH16MappingTable;
  1246. #if 0
  1247. // We should look at this to see if we can clean up any leaks for our process in
  1248. // GDI32. We have to be careful though because there may be handles that were
  1249. // allocated on our behalf from such components as USER32 who might have cached
  1250. // the handle. If we call DeleteObject() on a handle that is in a USER32 cache
  1251. // it might be disasterous. In short, we would like to do this but probably
  1252. // can't.
  1253. DWORD dwType;
  1254. HANDLE h32;
  1255. WORD index;
  1256. // Go free any GDI handles that this process might have.
  1257. for(index = FIRST_ALLOWABLE_INDEX; index <= gLastAllowableIndex; index++) {
  1258. h32 = pGdiH16MappingTable[index].h32;
  1259. // if the handle is still valid in our process...
  1260. dwType = GetObjectType(h32);
  1261. if(h32 && dwType) {
  1262. // formally delete the handle
  1263. switch(dwType) {
  1264. // No way to tell which DC allocation mechanism this came
  1265. // from (CreateDC(), BeginPaint(), GetxxxDC() etc). We can
  1266. // really mess things up if we call the wrong delete
  1267. // function, so we'll just let the GDI32 & USER32 process
  1268. // clean-up code deal with these.
  1269. case OBJ_DC:
  1270. break;
  1271. // The rest we can use DeleteObject()
  1272. default:
  1273. DeleteObject(h32);
  1274. break;
  1275. }
  1276. }
  1277. }
  1278. #endif
  1279. // De-commit entire 16-bit handle table & re-build both tables. This could
  1280. // burn us if the debug version of wowexec is running with its window open
  1281. // and they get a GDI handle but I don't think it is likely. We're also
  1282. // somewhat protected by the fact the GDI16 caches the stock objects.
  1283. dwSize = gdwPageCommitSize;
  1284. if(NT_SUCCESS(NtFreeVirtualMemory(ghProcess,
  1285. (PVOID *)&pTable16,
  1286. &dwSize,
  1287. MEM_DECOMMIT))) {
  1288. RtlZeroMemory(pGdiH32MappingTable, GDI32_HANDLE_TABLE_SIZE);
  1289. // Commit the first "table page" again
  1290. gdwPageCommitSize = 0;
  1291. gLastAllowableIndex = (WORD)-1; // cheese!
  1292. ghGdi16NextFree = END_OF_LIST;
  1293. #ifdef DEBUG
  1294. gAllocatedHandleCount = 0;
  1295. #endif
  1296. if(!CommitNewGdi16TablePage(pTable16)) {
  1297. dwSize = 0;
  1298. NtFreeVirtualMemory(ghProcess,
  1299. (PVOID *)&pTable16,
  1300. &dwSize,
  1301. MEM_RELEASE);
  1302. WOW32ASSERTMSG((FALSE),
  1303. "WOW::RebuildGdiHandleMappingTables:Panic!\n");
  1304. return;
  1305. }
  1306. ghGdi16NextFree = FIRST_ALLOWABLE_INDEX; // adjust this for init case
  1307. ghGdi16LastFree = gLastAllowableIndex;
  1308. gH16_deleted_count = 0;
  1309. // re-register the stock object & we're on our way!
  1310. RegisterStockObjects();
  1311. }
  1312. }
  1313. void DeleteGdiHandleMappingTables(void)
  1314. {
  1315. SIZE_T dwSize;
  1316. dwSize = 0;
  1317. NtFreeVirtualMemory(ghProcess,
  1318. (PVOID *)&pGdiH16MappingTable,
  1319. &dwSize,
  1320. MEM_RELEASE);
  1321. HeapFree(hGdi32TableHeap, HEAP_NO_SERIALIZE, pGdiH32MappingTable);
  1322. HeapDestroy(hGdi32TableHeap);
  1323. }
  1324. // We probably don't need to worry about this buffer being too small since we're
  1325. // really only interested in the predefined standard classes which tend to
  1326. // be rather short-named.
  1327. #define MAX_CLASSNAME_LEN 64
  1328. // There is a time frame (from when an app calls CreateWindow until USER32 gets
  1329. // a message at one of its WndProc's for the window - see FritzS) during which
  1330. // the fnid (class type) can't be set officially for the window. If the
  1331. // GETICLASS macro is invoked during this period, it will be unable to find the
  1332. // iClass for windows created on any of the standard control classes using the
  1333. // fast fnid index method (see walias.h). Once the time frame is passed, the
  1334. // fast fnid method will work fine for these windows.
  1335. //
  1336. // This is manifested in apps that set CBT hooks and try to subclass the
  1337. // standard classes while in their hook proc. See bug #143811.
  1338. INT GetIClass(PWW pww, HWND hwnd)
  1339. {
  1340. INT iClass;
  1341. DWORD dwClassAtom;
  1342. // if it is a standard class
  1343. if(((pww->fnid & 0xfff) >= FNID_START) &&
  1344. ((pww->fnid & 0xfff) <= FNID_END)) {
  1345. // return the class id for this initialized window
  1346. iClass = pfnOut.aiWowClass[( pww->fnid & 0xfff) - FNID_START];
  1347. }
  1348. else {
  1349. iClass = WOWCLASS_WIN16; // default return: app private class
  1350. dwClassAtom = GetClassLong(hwnd, GCW_ATOM);
  1351. if(dwClassAtom) {
  1352. iClass = GetStdClassNumber((PSZ)dwClassAtom);
  1353. }
  1354. }
  1355. return(iClass);
  1356. }