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.

806 lines
23 KiB

  1. /******************************Module*Header*******************************\
  2. * Module Name: cleanup.cxx
  3. *
  4. * Process termination - this file cleans up objects when a process
  5. * terminates.
  6. *
  7. * Created: 22-Jul-1991 12:24:52
  8. * Author: Eric Kutter [erick]
  9. *
  10. * Copyright (c) 1990-1999 Microsoft Corporation
  11. \**************************************************************************/
  12. #include "precomp.hxx"
  13. #include "muclean.hxx"
  14. extern BOOL bDeleteBrush(HBRUSH,BOOL); // brushobj.cxx
  15. extern VOID vCleanupPrivateFonts(); // pftobj.cxx
  16. extern HCOLORSPACE ghStockColorSpace; // icmapi.cxx
  17. #if DBG
  18. ULONG
  19. DbgPrint(
  20. PCH Format,
  21. ...
  22. );
  23. #define DBGPRINT(x) DbgPrint(x)
  24. #else
  25. #define DBGPRINT(x)
  26. #endif
  27. ULONG gInitialBatchCount = 0x14;
  28. /******************************Public*Routine******************************\
  29. *
  30. * History:
  31. * 24-Jul-1991 -by- Eric Kutter [erick]
  32. * Wrote it.
  33. \**************************************************************************/
  34. VOID vCleanupDCs(W32PID pid)
  35. {
  36. HOBJ hobj = HmgNextOwned((HOBJ) 0, pid);
  37. for (;(hobj != (HOBJ) NULL);hobj = HmgNextOwned(hobj, pid))
  38. {
  39. if (HmgObjtype(hobj) == DC_TYPE)
  40. {
  41. HmgSetLock(hobj, 0);
  42. bDeleteDCInternal((HDC)hobj,TRUE,TRUE);
  43. }
  44. }
  45. }
  46. /******************************Public*Routine******************************\
  47. *
  48. * History:
  49. * Sat 20-Jun-1992 -by- Patrick Haluptzok [patrickh]
  50. * Wrote it.
  51. \**************************************************************************/
  52. VOID vCleanupBrushes(W32PID pid)
  53. {
  54. HOBJ hobj = HmgNextOwned((HOBJ) 0, pid);
  55. for (;(hobj != (HOBJ) NULL);hobj = HmgNextOwned(hobj, pid))
  56. {
  57. if (HmgObjtype(hobj) == BRUSH_TYPE)
  58. {
  59. bDeleteBrush((HBRUSH)hobj,TRUE);
  60. }
  61. }
  62. }
  63. /******************************Public*Routine******************************\
  64. *
  65. * History:
  66. * Sat 20-Jun-1992 -by- Patrick Haluptzok [patrickh]
  67. * Wrote it.
  68. \**************************************************************************/
  69. VOID vCleanupSurfaces(W32PID pid, CLEANUPTYPE cutype)
  70. {
  71. HOBJ hobj = HmgNextOwned((HOBJ) 0, pid);
  72. for (;(hobj != (HOBJ) NULL);hobj = HmgNextOwned(hobj, pid))
  73. {
  74. if (HmgObjtype(hobj) == SURF_TYPE)
  75. {
  76. SURFREF so((HSURF)hobj);
  77. //
  78. // Skip PDEV surfaces; they are cleaned up with the PDEV
  79. // (see PDEVOBJ::vUnreferencePdev and PDEVOBJ::vDisableSurface).
  80. //
  81. // Also cleanup if this is a UMPD surface. This is to handle the
  82. // case when a DrvDisableSurface call to usermode printer driver
  83. // is nuked due to a terminate APC and we start process GDI cleanup.
  84. // If we did not have the || so.ps->bUMPD() we will leak the handle.
  85. if (!so.ps->bPDEVSurface() || so.ps->bUMPD())
  86. so.bDeleteSurface(cutype);
  87. }
  88. }
  89. }
  90. /******************************Public*Routine******************************\
  91. *
  92. * History:
  93. * 24-Jul-1991 -by- Eric Kutter [erick]
  94. * Wrote it.
  95. \**************************************************************************/
  96. VOID vCleanupFonts(W32PID pid)
  97. {
  98. HOBJ hobj = HmgNextOwned((HOBJ) 0, pid);
  99. for (;(hobj != (HOBJ) NULL);hobj = HmgNextOwned(hobj, pid))
  100. {
  101. if (HmgObjtype(hobj) == LFONT_TYPE)
  102. {
  103. bDeleteFont((HLFONT) hobj, FALSE);
  104. }
  105. }
  106. }
  107. /******************************Public*Routine******************************\
  108. * vCleanupLCSPs
  109. *
  110. * History:
  111. *
  112. * 9/20/1996 Mark Enstrom [marke]
  113. *
  114. \**************************************************************************/
  115. VOID vCleanupLCSPs(W32PID pid)
  116. {
  117. HOBJ hobj = HmgNextOwned((HOBJ) 0, pid);
  118. for (;(hobj != (HOBJ) NULL);hobj = HmgNextOwned(hobj, pid))
  119. {
  120. if (HmgObjtype(hobj) == ICMLCS_TYPE)
  121. {
  122. bDeleteColorSpace((HCOLORSPACE)hobj);
  123. }
  124. }
  125. }
  126. /******************************Public*Routine******************************\
  127. *
  128. * Eliminate user pregion to make sure delete succceds
  129. *
  130. \**************************************************************************/
  131. VOID vCleanupRegions(W32PID pid)
  132. {
  133. HOBJ hobj = HmgNextOwned((HOBJ) 0, pid);
  134. for (;(hobj != (HOBJ) NULL);hobj = HmgNextOwned(hobj, pid))
  135. {
  136. if (HmgObjtype(hobj) == RGN_TYPE)
  137. {
  138. RGNOBJ ro;
  139. ro.prgn = (PREGION)HmgLock(hobj,RGN_TYPE);
  140. if (ro.prgn)
  141. {
  142. PENTRY pent = PENTRY_FROM_POBJ(ro.prgn);
  143. if (pent)
  144. {
  145. pent->pUser = NULL;
  146. }
  147. DEC_EXCLUSIVE_REF_CNT(ro.prgn);
  148. }
  149. else
  150. {
  151. WARNING("vCleanupRegions: locked region has bad pEntry");
  152. }
  153. bDeleteRegion((HRGN)hobj);
  154. }
  155. }
  156. }
  157. /******************************Public*Routine******************************\
  158. *
  159. * History:
  160. * 01-Feb-2001 -by- Xudong Wu [tessiew]
  161. * Wrote it.
  162. \**************************************************************************/
  163. VOID vRemoveRefPalettes(W32PID pid)
  164. {
  165. HOBJ hobj = HmgNextOwned((HOBJ) 0, pid);
  166. for (;(hobj != (HOBJ) NULL);hobj = HmgNextOwned(hobj, pid))
  167. {
  168. if (HmgObjtype(hobj) == PAL_TYPE)
  169. {
  170. SEMOBJ semo(ghsemPalette);
  171. EPALOBJ palobj((HPALETTE)hobj);
  172. palobj.apalResetColorTable();
  173. }
  174. }
  175. }
  176. /******************************Public*Routine******************************\
  177. *
  178. * History:
  179. * 07-Aug-1992 -by- Gilman Wong [gilmanw]
  180. * Wrote it.
  181. \**************************************************************************/
  182. BOOL bEnumFontClose(ULONG_PTR ulEnum)
  183. {
  184. EFSOBJ efso((HEFS) ulEnum);
  185. if (!efso.bValid())
  186. {
  187. WARNING("gdisrv!bDeleteFontEnumState(): bad HEFS handle\n");
  188. return FALSE;
  189. }
  190. efso.vDeleteEFSOBJ();
  191. return TRUE;
  192. }
  193. /******************************Public*Routine******************************\
  194. * NtGdiInit()
  195. *
  196. * This routine must be called before any other GDI routines. Currently
  197. * it doesn't actualy do anything, just forces a kernel mode transition
  198. * which will cause GdiProcessCallout to get called.
  199. *
  200. * History:
  201. * 07-Sep-1995 -by- Eric Kutter [erick]
  202. * Wrote it.
  203. \**************************************************************************/
  204. BOOL NtGdiInit()
  205. {
  206. return(TRUE);
  207. }
  208. /******************************Public*Routine******************************\
  209. * NtGdiCloseProcess
  210. *
  211. * Release the resources held by the specified process.
  212. *
  213. * The cutype is set to CLEANUP_SESSION only for MultiUserGreCleanup
  214. * (Hydra) processing. It is used to do extra work to allow cleanup
  215. * of cross-process data not normally cleaned up on process termination,
  216. * just GRE termination (e.g., global and default data such as the
  217. * default bitmap and the public font tables).
  218. *
  219. * History:
  220. * 03-Feb-1998 -by- Gilman Wong [gilmanw]
  221. * Wrote it.
  222. \**************************************************************************/
  223. BOOL FASTCALL
  224. NtGdiCloseProcess(W32PID W32Pid, CLEANUPTYPE cutype)
  225. {
  226. BOOL bRes = TRUE;
  227. ASSERTGDI(cutype != CLEANUP_NONE, "NtGdiCloseProcess: illegal cutype\n");
  228. //
  229. // Enum all the objects for the process and kill them.
  230. //
  231. // For MultiUserGreCleanup, MultiUserGreCleanupHmgRemoveAllLocks is
  232. // called for each object type to force all such objects in the
  233. // handle manager to be unlocked and deletable.
  234. //
  235. //
  236. // Cleanup DCs.
  237. //
  238. vCleanupDCs(W32Pid);
  239. //
  240. // Cleanup fonts.
  241. //
  242. if (cutype == CLEANUP_SESSION)
  243. MultiUserGreCleanupHmgRemoveAllLocks((OBJTYPE)LFONT_TYPE);
  244. else if (cutype == CLEANUP_PROCESS)
  245. MultiUserGreCleanupHmgOwnRemoveAllLocks((OBJTYPE)LFONT_TYPE);
  246. vCleanupFonts(W32Pid);
  247. //
  248. // Cleanup brushes.
  249. //
  250. if (cutype == CLEANUP_SESSION)
  251. MultiUserGreCleanupHmgRemoveAllLocks((OBJTYPE)BRUSH_TYPE);
  252. else if(cutype == CLEANUP_PROCESS)
  253. MultiUserGreCleanupHmgOwnRemoveAllLocks((OBJTYPE)BRUSH_TYPE);
  254. vCleanupBrushes(W32Pid);
  255. //
  256. // Clean up the ddraw & d3d types
  257. //
  258. DxDdCloseProcess(W32Pid);
  259. //
  260. // Cleanup surfaces.
  261. //
  262. if (cutype == CLEANUP_SESSION)
  263. {
  264. //
  265. // Need to forget about some global/default objects so they
  266. // can be deleted.
  267. //
  268. SURFACE::pdibDefault = NULL;
  269. ppalDefault = NULL;
  270. ppalMono = NULL;
  271. hpalMono = (HPALETTE) 0;
  272. MultiUserGreCleanupHmgRemoveAllLocks((OBJTYPE)SURF_TYPE);
  273. }
  274. else if (cutype == CLEANUP_PROCESS)
  275. MultiUserGreCleanupHmgOwnRemoveAllLocks((OBJTYPE)SURF_TYPE);
  276. vCleanupSurfaces(W32Pid, cutype);
  277. //
  278. // Cleanup regions.
  279. //
  280. if (cutype == CLEANUP_SESSION)
  281. {
  282. //
  283. // Need to forget about some global/default objects so they
  284. // can be deleted.
  285. //
  286. hrgnDefault = NULL;
  287. prgnDefault = NULL;
  288. MultiUserGreCleanupHmgRemoveAllLocks((OBJTYPE)RGN_TYPE);
  289. }
  290. else if (cutype == CLEANUP_PROCESS)
  291. MultiUserGreCleanupHmgOwnRemoveAllLocks((OBJTYPE)RGN_TYPE);
  292. vCleanupRegions(W32Pid);
  293. //
  294. // Cleanup ICM color spaces.
  295. //
  296. if (cutype == CLEANUP_SESSION)
  297. {
  298. //
  299. // Need to forget about some global/default objects so they
  300. // can be deleted.
  301. //
  302. ghStockColorSpace = NULL;
  303. MultiUserGreCleanupHmgRemoveAllLocks((OBJTYPE)ICMLCS_TYPE);
  304. }
  305. else if (cutype == CLEANUP_PROCESS)
  306. MultiUserGreCleanupHmgOwnRemoveAllLocks((OBJTYPE)ICMLCS_TYPE);
  307. vCleanupLCSPs(W32Pid);
  308. //
  309. // Cleanup private fonts.
  310. //
  311. if (cutype == CLEANUP_SESSION)
  312. {
  313. //
  314. // Relinquish locks on ALL remaining objects in handle manager.
  315. //
  316. MultiUserGreCleanupHmgRemoveAllLocks((OBJTYPE)DEF_TYPE);
  317. }
  318. else if (cutype == CLEANUP_PROCESS)
  319. {
  320. MultiUserGreCleanupHmgOwnRemoveAllLocks((OBJTYPE)DEF_TYPE);
  321. }
  322. if (cutype == CLEANUP_PROCESS)
  323. {
  324. // Private fonts should be cleaned up when a process goes away.
  325. vCleanupPrivateFonts();
  326. }
  327. //
  328. // Remove the ppalColor reference in the palettes
  329. //
  330. vRemoveRefPalettes(W32Pid);
  331. // Clean up the rest
  332. //
  333. HOBJ hobj = HmgNextOwned((HOBJ) 0, W32Pid);
  334. for (;(hobj != (HOBJ) NULL);hobj = HmgNextOwned(hobj, W32Pid))
  335. {
  336. switch (HmgObjtype(hobj))
  337. {
  338. case PAL_TYPE:
  339. bRes = bDeletePalette((HPAL)hobj, TRUE, cutype);
  340. break;
  341. case EFSTATE_TYPE:
  342. bRes = bEnumFontClose((ULONG_PTR)hobj);
  343. break;
  344. case DRVOBJ_TYPE:
  345. {
  346. HmgSetLock(hobj, 0);
  347. //
  348. // Free the DRIVEROBJ.
  349. //
  350. DRIVEROBJ *pdriv = EngLockDriverObj((HDRVOBJ)hobj);
  351. PDEVOBJ po(pdriv->hdev);
  352. ASSERTGDI(po.bValid(), "ERROR invalid PDEV in DRIVEROBJ");
  353. BOOL bRet = EngDeleteDriverObj((HDRVOBJ)hobj, TRUE, TRUE);
  354. ASSERTGDI(bRet, "Cleanup driver objects failed in process termination");
  355. }
  356. break;
  357. case CLIENTOBJ_TYPE:
  358. GreDeleteClientObj(hobj);
  359. break;
  360. default:
  361. bRes = FALSE;
  362. break;
  363. }
  364. #if DBG
  365. if (bRes == FALSE)
  366. {
  367. //
  368. // During shutdown, fonts are handled later so that public
  369. // fonts and tables can be safely deleted (see function
  370. // MultiUserGreCleanupAllFonts).
  371. //
  372. if ((cutype != CLEANUP_SESSION) ||
  373. ((HmgObjtype(hobj) != PFE_TYPE) &&
  374. (HmgObjtype(hobj) != PFT_TYPE)))
  375. {
  376. DbgPrint("GDI ERROR: vCleanup couldn't delete "
  377. "obj = %lx, type j=%lx\n", hobj, HmgObjtype(hobj));
  378. DbgBreakPoint();
  379. }
  380. }
  381. #endif
  382. }
  383. return bRes;
  384. }
  385. /******************************Public*Routine******************************\
  386. *
  387. * History:
  388. * 24-Jul-1991 -by- Eric Kutter [erick]
  389. * Wrote it.
  390. \**************************************************************************/
  391. extern "C"
  392. NTSTATUS
  393. GdiProcessCallout(
  394. IN PW32PROCESS Process,
  395. IN BOOLEAN Initialize
  396. )
  397. {
  398. BOOL bRes = TRUE;
  399. if (Initialize)
  400. {
  401. NTSTATUS ntStatus = STATUS_SUCCESS;
  402. PPEB Peb;
  403. RtlInitializeGenericTableAvl(&Process->GDIEngUserMemAllocTable,
  404. GDIEngUserMemAllocNodeCompare,
  405. GDIEngUserMemAllocNodeAlloc,
  406. GDIEngUserMemAllocNodeFree,
  407. 0);
  408. InitializeListHead(&Process->GDIDcAttrFreeList);
  409. InitializeListHead(&Process->GDIBrushAttrFreeList);
  410. //
  411. // check if the PEB is valid, if not then this is the SYSTEM process
  412. // and has a NULL PEB. This process has no user-mode access so it
  413. // is not neccessary to map in the shared handle table.
  414. //
  415. Peb = PsGetProcessPeb(Process->Process);
  416. if (Peb != NULL)
  417. {
  418. //
  419. // Temporary entry to allow setting GDI
  420. // batch limit before each process startup.
  421. //
  422. Peb->GdiDCAttributeList = gInitialBatchCount;
  423. ASSERTGDI(sizeof(Peb->GdiHandleBuffer) >= sizeof(GDIHANDLECACHE),
  424. "Handle cache not large enough");
  425. RtlZeroMemory(Peb->GdiHandleBuffer, sizeof(GDIHANDLECACHE));
  426. //
  427. // map a READ_ONLY view of the hmgr shared handle table into the
  428. // process's address space
  429. //
  430. PVOID BaseAddress = NULL;
  431. SIZE_T CommitSize = 0;
  432. OBJECT_ATTRIBUTES ObjectAttributes;
  433. UNICODE_STRING UnicodeString;
  434. HANDLE SectionHandle = NULL;
  435. ntStatus = ObOpenObjectByPointer( gpHmgrSharedHandleSection,
  436. OBJ_KERNEL_HANDLE,
  437. (PACCESS_STATE) NULL,
  438. SECTION_ALL_ACCESS,
  439. (POBJECT_TYPE) NULL,
  440. KernelMode,
  441. &SectionHandle);
  442. if (NT_SUCCESS(ntStatus))
  443. {
  444. ntStatus = ZwMapViewOfSection(
  445. SectionHandle,
  446. NtCurrentProcess(),
  447. &BaseAddress,
  448. 0L,
  449. 0L,
  450. NULL,
  451. &CommitSize,
  452. ViewUnmap,
  453. 0L,
  454. PAGE_READONLY
  455. );
  456. if (NT_SUCCESS(ntStatus))
  457. {
  458. //
  459. // set table address
  460. //
  461. // we must set the GdiSharedHandleTable value
  462. // to the shared table pointer so that if GDI32 gets
  463. // unloaded and re-loaded it can still get the pointer.
  464. //
  465. // NOTE: we also depend on this pointer being initialized
  466. // *BEFORE* we make any GDI or USER call to the kernel
  467. // (which has the automatic side-effect of calling this
  468. // routine.
  469. //
  470. Peb->GdiSharedHandleTable =
  471. (PVOID)BaseAddress;
  472. }
  473. else
  474. {
  475. KdPrint(("ZwMapViewOfSection fails, status = 0x%lx\n",ntStatus));
  476. ntStatus = STATUS_DLL_INIT_FAILED;
  477. }
  478. ZwClose(SectionHandle);
  479. }
  480. else
  481. {
  482. KdPrint(("ObOpenObjectByPointer fails, status = 0x%lx\n",ntStatus));
  483. ntStatus = STATUS_DLL_INIT_FAILED;
  484. }
  485. }
  486. return ntStatus;
  487. }
  488. else
  489. {
  490. //
  491. // This call takes place when the last thread of a process goes away.
  492. // Note that such thread might not be a w32 thread
  493. //
  494. //
  495. // first lets see if this is the spooler and if so, clean him up
  496. vCleanupSpool();
  497. W32PID W32Pid = W32GetCurrentPID();
  498. bRes = NtGdiCloseProcess(W32Pid, CLEANUP_PROCESS);
  499. if (bRes)
  500. {
  501. if(Process->GDIHandleCount != 0)
  502. {
  503. WARNING("GdiProcessCallout: handle count != 0 at termination\n");
  504. }
  505. }
  506. if (Process)
  507. {
  508. PGDIENGUSERMEMALLOCNODE pNode;
  509. PVOID Restart = NULL;
  510. for (pNode = (PGDIENGUSERMEMALLOCNODE)RtlEnumerateGenericTableAvl(&Process->GDIEngUserMemAllocTable,TRUE);
  511. pNode;
  512. pNode = (PGDIENGUSERMEMALLOCNODE)RtlEnumerateGenericTableAvl(&Process->GDIEngUserMemAllocTable,FALSE))
  513. {
  514. RtlDeleteElementGenericTableAvl(&Process->GDIEngUserMemAllocTable,pNode);
  515. }
  516. PLIST_ENTRY ListHead = &Process->GDIDcAttrFreeList;
  517. PLIST_ENTRY NextEntry = ListHead->Flink;
  518. // Note in these loops we check for non NULL NextEntry also.
  519. // Why? Sometimes it seems GDIProcessCallout is called for
  520. // process termination even though it was not called for
  521. // initialization. If this is the case then the NextEntry will
  522. // be NULL instead of pointing to List Anchor. Hence to handle
  523. // this we do this extra check.
  524. if (NextEntry)
  525. {
  526. while (NextEntry != ListHead)
  527. {
  528. PLIST_ENTRY pFree = NextEntry;
  529. NextEntry = NextEntry->Flink;
  530. VFREEMEM(pFree);
  531. }
  532. }
  533. ListHead = &Process->GDIBrushAttrFreeList;
  534. NextEntry = ListHead->Flink;
  535. if (NextEntry)
  536. {
  537. while (NextEntry != ListHead)
  538. {
  539. PLIST_ENTRY pFree = NextEntry;
  540. NextEntry = NextEntry->Flink;
  541. VFREEMEM(pFree);
  542. }
  543. }
  544. }
  545. }
  546. return (bRes ? STATUS_SUCCESS : STATUS_CANNOT_DELETE);
  547. }
  548. /******************************Public*Routine******************************\
  549. * GdiThreadCallout
  550. *
  551. * For Inintialize case, set initial values for W32THREAD elements.
  552. * For rundown case, move all thread DCATTR memory blocks to the process
  553. * list.
  554. *
  555. * History:
  556. *
  557. * 15-May-1995 -by- Mark Enstrom [marke]
  558. *
  559. \**************************************************************************/
  560. extern "C"
  561. VOID GdiThreadCallout(
  562. IN PETHREAD pEThread,
  563. IN PSW32THREADCALLOUTTYPE CalloutType
  564. )
  565. {
  566. switch (CalloutType)
  567. {
  568. case PsW32ThreadCalloutInitialize:
  569. {
  570. PW32THREAD pThread;
  571. pThread = (PW32THREAD) PsGetThreadWin32Thread(pEThread);
  572. InitializeListHead(&pThread->GdiTmpAllocList);
  573. break;
  574. }
  575. case PsW32ThreadCalloutExit:
  576. {
  577. PDC_ATTR pdca;
  578. PW32THREAD pThread;
  579. //
  580. // Flush the TEB batch. KiSystemService flushes the batch
  581. // only if the service is a win32k.sys service. During thread
  582. // termination, the transition to kernel mode is not one of
  583. // these, and so we can get here without the batch being
  584. // flushed. Bug #338052 demonstrated this.
  585. //
  586. GdiThreadCalloutFlushUserBatch();
  587. //
  588. // W32 thread execution end. Note that the thread
  589. // object can be locked so it might be used after
  590. // this call returns.
  591. //
  592. pdca = (PDC_ATTR)((PW32THREAD)PsGetThreadWin32Thread(pEThread))->pgdiDcattr;
  593. if (pdca != NULL)
  594. {
  595. //
  596. // Thread->pgdiDcattr is not NULL so HmgFreeDcAttr will
  597. // not put pdca back on thread but will place it on the
  598. // process list.
  599. //
  600. HmgFreeDcAttr(pdca);
  601. }
  602. #if !defined(_GDIPLUS_)
  603. //
  604. // Clean up user mode printer driver related stuff
  605. //
  606. pThread = (PW32THREAD) PsGetThreadWin32Thread(pEThread);
  607. {
  608. PUMPDOBJ pumpdobj;
  609. while (pumpdobj = (PUMPDOBJ)pThread->pUMPDObjs)
  610. {
  611. pumpdobj->Cleanup();
  612. VFREEMEM(pumpdobj);
  613. }
  614. }
  615. if (pThread->pUMPDHeap != NULL)
  616. DestroyUMPDHeap((PUMPDHEAP) pThread->pUMPDHeap);
  617. #if defined(_WIN64)
  618. //
  619. // Cleanup Proxy port
  620. //
  621. if(pThread->pProxyPort)
  622. {
  623. PROXYPORT proxyport((ProxyPort*)pThread->pProxyPort);
  624. proxyport.Close();
  625. pThread->pProxyPort = NULL;
  626. }
  627. #endif
  628. #ifdef VALIDATE_LOCKS
  629. //
  630. // Cleanup any debug block that may have been allocated
  631. //
  632. if(pThread->pSemTable)
  633. {
  634. VFREEMEM(pThread->pSemTable);
  635. pThread->pSemTable = NULL;
  636. }
  637. #endif
  638. // Cleanup per thread Tmp Allocations
  639. {
  640. PLIST_ENTRY ListHead = &pThread->GdiTmpAllocList;
  641. PLIST_ENTRY NextEntry = ListHead->Flink;
  642. while(NextEntry != ListHead)
  643. {
  644. PLIST_ENTRY pFree = NextEntry;
  645. NextEntry = NextEntry->Flink;
  646. VFREEMEM(pFree);
  647. }
  648. }
  649. #endif // !_GDIPLUS_
  650. break;
  651. }
  652. }
  653. }