diff --git a/reactos/subsystems/win32/win32k/include/dce.h b/reactos/subsystems/win32/win32k/include/dce.h index 40bc48fdaef..6c0e58b17cf 100644 --- a/reactos/subsystems/win32/win32k/include/dce.h +++ b/reactos/subsystems/win32/win32k/include/dce.h @@ -61,5 +61,7 @@ void FASTCALL DceEmptyCache(void); VOID FASTCALL DceResetActiveDCEs(PWINDOW_OBJECT Window); void FASTCALL DceFreeClassDCE(HDC); HWND FASTCALL UserGethWnd(HDC,PWNDOBJ*); +void FASTCALL DceFreeWindowDCE(PWINDOW_OBJECT); +void FASTCALL DceFreeThreadDCE(PTHREADINFO); #endif /* _WIN32K_DCE_H */ diff --git a/reactos/subsystems/win32/win32k/main/dllmain.c b/reactos/subsystems/win32/win32k/main/dllmain.c index 346974a261f..8d106e725a3 100644 --- a/reactos/subsystems/win32/win32k/main/dllmain.c +++ b/reactos/subsystems/win32/win32k/main/dllmain.c @@ -288,6 +288,7 @@ Win32kThreadCallback(struct _ETHREAD *Thread, DPRINT("Destroying W32 thread TID:%d at IRQ level: %lu\n", Thread->Cid.UniqueThread, KeGetCurrentIrql()); Win32Thread->TIF_flags |= TIF_INCLEANUP; + DceFreeThreadDCE(Win32Thread); HOOK_DestroyThreadHooks(Thread); UnregisterThreadHotKeys(Thread); /* what if this co_ func crash in umode? what will clean us up then? */ diff --git a/reactos/subsystems/win32/win32k/ntuser/painting.c b/reactos/subsystems/win32/win32k/ntuser/painting.c index ef5aceeb585..aa1b769e00d 100644 --- a/reactos/subsystems/win32/win32k/ntuser/painting.c +++ b/reactos/subsystems/win32/win32k/ntuser/painting.c @@ -1111,8 +1111,6 @@ CLEANUP: END_CLEANUP; } - - static INT FASTCALL UserScrollDC(HDC hDC, INT dx, INT dy, const RECTL *prcScroll, @@ -1208,19 +1206,21 @@ UserScrollDC(HDC hDC, INT dx, INT dy, const RECTL *prcScroll, return Result; } - - - /* * NtUserScrollDC * * Status * @implemented */ - BOOL APIENTRY -NtUserScrollDC(HDC hDC, INT dx, INT dy, const RECT *prcUnsafeScroll, - const RECT *prcUnsafeClip, HRGN hrgnUpdate, LPRECT prcUnsafeUpdate) +NtUserScrollDC( + HDC hDC, + INT dx, + INT dy, + const RECT *prcUnsafeScroll, + const RECT *prcUnsafeClip, + HRGN hrgnUpdate, + LPRECT prcUnsafeUpdate) { DECLARE_RETURN(DWORD); RECTL rcScroll, rcClip, rcUpdate; @@ -1258,10 +1258,13 @@ NtUserScrollDC(HDC hDC, INT dx, INT dy, const RECT *prcUnsafeScroll, RETURN(FALSE); } - Result = UserScrollDC(hDC, dx, dy, - prcUnsafeScroll? &rcScroll : 0, - prcUnsafeClip? &rcClip : 0, hrgnUpdate, - prcUnsafeUpdate? &rcUpdate : NULL); + Result = UserScrollDC( hDC, + dx, + dy, + prcUnsafeScroll? &rcScroll : 0, + prcUnsafeClip? &rcClip : 0, + hrgnUpdate, + prcUnsafeUpdate? &rcUpdate : NULL); if(Result == ERROR) { /* FIXME: Only if hRgnUpdate is invalid we should SetLastError(ERROR_INVALID_HANDLE) */ @@ -1303,8 +1306,15 @@ CLEANUP: */ DWORD APIENTRY -NtUserScrollWindowEx(HWND hWnd, INT dx, INT dy, const RECT *prcUnsafeScroll, - const RECT *prcUnsafeClip, HRGN hrgnUpdate, LPRECT prcUnsafeUpdate, UINT flags) +NtUserScrollWindowEx( + HWND hWnd, + INT dx, + INT dy, + const RECT *prcUnsafeScroll, + const RECT *prcUnsafeClip, + HRGN hrgnUpdate, + LPRECT prcUnsafeUpdate, + UINT flags) { RECTL rcScroll, rcClip, rcCaret, rcUpdate; INT Result; @@ -1378,7 +1388,14 @@ NtUserScrollWindowEx(HWND hWnd, INT dx, INT dy, const RECT *prcUnsafeScroll, rcCaret = rcScroll; hwndCaret = co_IntFixCaret(Window, &rcCaret, flags); - Result = UserScrollDC(hDC, dx, dy, &rcScroll, &rcClip, hrgnOwn, prcUnsafeUpdate? &rcUpdate : NULL); + Result = UserScrollDC( hDC, + dx, + dy, + &rcScroll, + &rcClip, + hrgnOwn, + prcUnsafeUpdate? &rcUpdate : NULL); + UserReleaseDC(Window, hDC, FALSE); /* diff --git a/reactos/subsystems/win32/win32k/ntuser/vis.c b/reactos/subsystems/win32/win32k/ntuser/vis.c index cf9c725e323..04d2b238b53 100644 --- a/reactos/subsystems/win32/win32k/ntuser/vis.c +++ b/reactos/subsystems/win32/win32k/ntuser/vis.c @@ -47,6 +47,8 @@ VIS_ComputeVisibleRegion( return NULL; } + VisRgn = NULL; + if (ClientArea) { VisRgn = IntSysCreateRectRgnIndirect(&Window->Wnd->rcClient); @@ -71,13 +73,14 @@ VIS_ComputeVisibleRegion( CurrentWindow->state & WINDOWSTATUS_DESTROYED ) { DPRINT1("ATM the Current Window or Parent is dead!\n"); + if (VisRgn) REGION_FreeRgnByHandle(VisRgn); return NULL; } CurrentWnd = CurrentWindow->Wnd; if (!CurrentWnd || !(CurrentWnd->style & WS_VISIBLE)) { - REGION_FreeRgnByHandle(VisRgn); + if (VisRgn) REGION_FreeRgnByHandle(VisRgn); return NULL; } diff --git a/reactos/subsystems/win32/win32k/ntuser/windc.c b/reactos/subsystems/win32/win32k/ntuser/windc.c index fdf8f488e94..8f9966dfcdb 100644 --- a/reactos/subsystems/win32/win32k/ntuser/windc.c +++ b/reactos/subsystems/win32/win32k/ntuser/windc.c @@ -19,9 +19,10 @@ int FASTCALL CLIPPING_UpdateGCRegion(DC* Dc); /* GLOBALS *******************************************************************/ /* NOTE - I think we should store this per window station (including gdi objects) */ +/* Answer: No, use the DCE pMonitor to compare with! */ -static PDCE FirstDce = NULL; -//static INT DCECount = 0; // Count of DCE in system. +static LIST_ENTRY LEDce; +static INT DCECount = 0; // Count of DCE in system. #define DCX_CACHECOMPAREMASK (DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN | \ DCX_NORESETATTRS | DCX_LOCKWINDOWUPDATE | \ @@ -51,6 +52,7 @@ DceCreateDisplayDC(VOID) defaultDCstate->pdcattr = &defaultDCstate->dcattr; DC_vCopyState(dc, defaultDCstate, TRUE); DC_UnlockDc( dc ); + InitializeListHead(&LEDce); } return hDC; } @@ -90,7 +92,8 @@ DceAllocDCE(PWINDOW_OBJECT Window OPTIONAL, DCE_TYPE Type) ExFreePoolWithTag(pDce, TAG_PDCE); return NULL; } - + DCECount++; + DPRINT("Alloc DCE's! %d\n",DCECount); pDce->hwndCurrent = (Window ? Window->hSelf : NULL); pDce->pwndOrg = Wnd; pDce->pwndClip = Wnd; @@ -99,15 +102,7 @@ DceAllocDCE(PWINDOW_OBJECT Window OPTIONAL, DCE_TYPE Type) pDce->hrgnSavedVis = NULL; pDce->ppiOwner = NULL; - KeEnterCriticalRegion(); - if (FirstDce == NULL) - { - FirstDce = pDce; - InitializeListHead(&FirstDce->List); - } - else - InsertTailList(&FirstDce->List, &pDce->List); - KeLeaveCriticalRegion(); + InsertTailList(&LEDce, &pDce->List); DCU_SetDcUndeletable(pDce->hDC); @@ -204,7 +199,6 @@ DceReleaseDC(DCE* dce, BOOL EndPaint) } /* restore previous visible region */ - if ((dce->DCXFlags & (DCX_INTERSECTRGN | DCX_EXCLUDERGN)) && ((dce->DCXFlags & DCX_CACHE) || EndPaint)) { @@ -213,30 +207,48 @@ DceReleaseDC(DCE* dce, BOOL EndPaint) if (dce->DCXFlags & DCX_CACHE) { - if (!(dce->DCXFlags & DCX_NORESETATTRS)) - { - /* make the DC clean so that SetDCState doesn't try to update the vis rgn */ - IntGdiSetHookFlags(dce->hDC, DCHF_VALIDATEVISRGN); + if (!(dce->DCXFlags & DCX_NORESETATTRS)) + { + /* make the DC clean so that SetDCState doesn't try to update the vis rgn */ + IntGdiSetHookFlags(dce->hDC, DCHF_VALIDATEVISRGN); - // Clean the DC - if (!IntGdiCleanDC(dce->hDC)) return 0; + // Clean the DC + if (!IntGdiCleanDC(dce->hDC)) return 0; - if (dce->DCXFlags & DCX_DCEDIRTY) - { - /* don't keep around invalidated entries - * because SetDCState() disables hVisRgn updates - * by removing dirty bit. */ - dce->hwndCurrent = 0; - dce->DCXFlags &= DCX_CACHE; - dce->DCXFlags |= DCX_DCEEMPTY; - } - } - dce->DCXFlags &= ~DCX_DCEBUSY; - DPRINT("Exit!!!!! DCX_CACHE!!!!!! hDC-> %x \n", dce->hDC); - if (!IntGdiSetDCOwnerEx( dce->hDC, GDI_OBJ_HMGR_NONE, FALSE)) - return 0; - dce->ptiOwner = NULL; // Reset ownership. - dce->ppiOwner = NULL; + if (dce->DCXFlags & DCX_DCEDIRTY) + { + /* don't keep around invalidated entries + * because SetDCState() disables hVisRgn updates + * by removing dirty bit. */ + dce->hwndCurrent = 0; + dce->DCXFlags &= DCX_CACHE; + dce->DCXFlags |= DCX_DCEEMPTY; + } + } + dce->DCXFlags &= ~DCX_DCEBUSY; + DPRINT("Exit!!!!! DCX_CACHE!!!!!! hDC-> %x \n", dce->hDC); + if (!IntGdiSetDCOwnerEx( dce->hDC, GDI_OBJ_HMGR_NONE, FALSE)) + return 0; + dce->ptiOwner = NULL; // Reset ownership. + dce->ppiOwner = NULL; + +#if 0 // Need to research and fix before this is a "growing" issue. + if (++DCECache > 32) + { + pLE = LEDce.Flink; + pDCE = CONTAINING_RECORD(pLE, DCE, List); + do + { + if (!(pDCE->DCXFlags & DCX_DCEBUSY)) + { /* Free the unused cache DCEs. */ + pDCE = DceFreeDCE(pDCE, TRUE); + if (!pDCE) break; + continue; + } + } + while (pLE != &LEDce ); + } +#endif } return 1; // Released! } @@ -335,6 +347,7 @@ UserGetDCEx(PWINDOW_OBJECT Window OPTIONAL, HANDLE ClipRegion, ULONG Flags) PWND Wnd = NULL; HDC hDC = NULL; PPROCESSINFO ppi; + PLIST_ENTRY pLE; if (NULL == Window) { @@ -445,7 +458,8 @@ UserGetDCEx(PWINDOW_OBJECT Window OPTIONAL, HANDLE ClipRegion, ULONG Flags) DCE* DceEmpty = NULL; DCE* DceUnused = NULL; KeEnterCriticalRegion(); - Dce = FirstDce; + pLE = LEDce.Flink; + Dce = CONTAINING_RECORD(pLE, DCE, List); do { // The reason for this you may ask? @@ -471,8 +485,10 @@ UserGetDCEx(PWINDOW_OBJECT Window OPTIONAL, HANDLE ClipRegion, ULONG Flags) break; } } - Dce = (PDCE)Dce->List.Flink; - } while (Dce != FirstDce); + pLE = Dce->List.Flink; + Dce = CONTAINING_RECORD(pLE, DCE, List); + } + while (pLE != &LEDce); KeLeaveCriticalRegion(); Dce = (DceEmpty == NULL) ? DceUnused : DceEmpty; @@ -488,14 +504,17 @@ UserGetDCEx(PWINDOW_OBJECT Window OPTIONAL, HANDLE ClipRegion, ULONG Flags) else // If we are here, we are POWNED or having CLASS. { KeEnterCriticalRegion(); - Dce = FirstDce; + pLE = LEDce.Flink; + Dce = CONTAINING_RECORD(pLE, DCE, List); do { // Check for Window handle than HDC match for CLASS. if ((Dce->hwndCurrent == Window->hSelf) || (Dce->hDC == hDC)) break; - Dce = (PDCE)Dce->List.Flink; - } while (Dce != FirstDce); + pLE = Dce->List.Flink; + Dce = CONTAINING_RECORD(pLE, DCE, List); + } + while (pLE != &LEDce); KeLeaveCriticalRegion(); if ( (Flags & (DCX_INTERSECTRGN|DCX_EXCLUDERGN)) && @@ -526,11 +545,10 @@ UserGetDCEx(PWINDOW_OBJECT Window OPTIONAL, HANDLE ClipRegion, ULONG Flags) http://www.reactos.org/archives/public/ros-dev/2008-July/010498.html http://www.reactos.org/archives/public/ros-dev/2008-July/010499.html */ - if ((Dce != FirstDce)) + if (pLE != &LEDce) { RemoveEntryList(&Dce->List); - InsertHeadList(&FirstDce->List, &Dce->List); - FirstDce = Dce; + InsertHeadList(&LEDce, &Dce->List); } /* Introduced in rev 6691 and modified later. */ @@ -595,15 +613,13 @@ PDCE FASTCALL DceFreeDCE(PDCE pdce, BOOLEAN Force) { DCE *ret; + PLIST_ENTRY pLE; BOOL Hit = FALSE; if (NULL == pdce) return NULL; - ret = (PDCE) pdce->List.Flink; - -#if 0 /* FIXME */ - SetDCHook(pdce->hDC, NULL, 0L); -#endif + pLE = pdce->List.Flink; + ret = CONTAINING_RECORD(pLE, DCE, List); if (Force && !GDIOBJ_OwnedByCurrentProcess(pdce->hDC)) { @@ -625,6 +641,7 @@ DceFreeDCE(PDCE pdce, BOOLEAN Force) if (pdce->hrgnClip && ! (pdce->DCXFlags & DCX_KEEPCLIPRGN)) { REGION_FreeRgnByHandle(pdce->hrgnClip); + pdce->hrgnClip = NULL; } RemoveEntryList(&pdce->List); @@ -632,47 +649,158 @@ DceFreeDCE(PDCE pdce, BOOLEAN Force) if (IsListEmpty(&pdce->List)) { DPRINT1("List is Empty! DCE! -> %x\n" , pdce); - FirstDce = NULL; - ret = NULL; + return NULL; } ExFreePoolWithTag(pdce, TAG_PDCE); + DCECount--; + DPRINT("Freed DCE's! %d \n", DCECount); + return ret; } +/*********************************************************************** + * DceFreeWindowDCE + * + * Remove owned DCE and reset unreleased cache DCEs. + */ +void FASTCALL +DceFreeWindowDCE(PWINDOW_OBJECT Window) +{ + PDCE pDCE; + PLIST_ENTRY pLE; + + if (DCECount <= 0) + { + DPRINT1("No Entry!\n"); + return; + } + + pLE = LEDce.Flink; + pDCE = CONTAINING_RECORD(pLE, DCE, List); + do + { + if (!pDCE) break; + if (IsListEmpty(&pDCE->List)) break; + if (pDCE->hwndCurrent == Window->hSelf) + { + if (!(pDCE->DCXFlags & DCX_CACHE)) /* owned or Class DCE*/ + { + if (Window->Wnd->pcls->style & CS_CLASSDC) /* Test Class first */ + { + if (pDCE->DCXFlags & (DCX_INTERSECTRGN | DCX_EXCLUDERGN)) /* Class DCE*/ + DceDeleteClipRgn(pDCE); + // Update and reset Vis Rgn and clear the dirty bit. + // Should release VisRgn than reset it to default. + DceUpdateVisRgn(pDCE, Window, pDCE->DCXFlags); + pDCE->DCXFlags = DCX_DCEEMPTY|DCX_CACHE; + pDCE->hwndCurrent = 0; + + DPRINT("POWNED DCE going Cheap!! DCX_CACHE!! hDC-> %x \n", pDCE->hDC); + if (!IntGdiSetDCOwnerEx( pDCE->hDC, GDI_OBJ_HMGR_NONE, FALSE)) + return; + /* Do not change owner so thread can clean up! */ + } + else if (Window->Wnd->pcls->style & CS_OWNDC) /* owned DCE*/ + { + pDCE = DceFreeDCE(pDCE, FALSE); + if (!pDCE) break; + continue; + } + else + { + ASSERT(FALSE); + } + } + else + { + if (pDCE->DCXFlags & DCX_DCEBUSY) /* shared cache DCE */ + { + /* FIXME: AFAICS we are doing the right thing here so + * this should be a DPRINT. But this is best left as an ERR + * because the 'application error' is likely to come from + * another part of Wine (i.e. it's our fault after all). + * We should change this to DPRINT when ReactOS is more stable + * (for 1.0?). + */ + DPRINT1("[%p] GetDC() without ReleaseDC()!\n", Window->hSelf); + DceReleaseDC(pDCE, FALSE); + } + pDCE->DCXFlags |= DCX_DCEEMPTY; + pDCE->hwndCurrent = 0; + } + } + pLE = pDCE->List.Flink; + pDCE = CONTAINING_RECORD(pLE, DCE, List); + } + while (pLE != &LEDce); +} + void FASTCALL DceFreeClassDCE(HDC hDC) { - PDCE pDCE = FirstDce; - KeEnterCriticalRegion(); + PDCE pDCE; + PLIST_ENTRY pLE; + pLE = LEDce.Flink; + pDCE = CONTAINING_RECORD(pLE, DCE, List); + do { if(!pDCE) break; if (pDCE->hDC == hDC) { - pDCE = DceFreeDCE(pDCE, FALSE); - if(!pDCE) break; + pDCE = DceFreeDCE(pDCE, TRUE); // Might have gone cheap! + if (!pDCE) break; continue; } - pDCE = (PDCE)pDCE->List.Flink; - } while (pDCE != FirstDce); - KeLeaveCriticalRegion(); + pLE = pDCE->List.Flink; + pDCE = CONTAINING_RECORD(pLE, DCE, List); + } + while (pLE != &LEDce); +} + +void FASTCALL +DceFreeThreadDCE(PTHREADINFO pti) +{ + PDCE pDCE; + PLIST_ENTRY pLE; + pLE = LEDce.Flink; + pDCE = CONTAINING_RECORD(pLE, DCE, List); + + do + { + if(!pDCE) break; + if (pDCE->ptiOwner == pti) + { + if (pDCE->DCXFlags & DCX_CACHE) + { + pDCE = DceFreeDCE(pDCE, TRUE); + if (!pDCE) break; + continue; + } + } + pLE = pDCE->List.Flink; + pDCE = CONTAINING_RECORD(pLE, DCE, List); + } + while (pLE != &LEDce); } VOID FASTCALL DceEmptyCache(VOID) { - PDCE pDCE = FirstDce; - KeEnterCriticalRegion(); + PDCE pDCE; + PLIST_ENTRY pLE; + pLE = LEDce.Flink; + pDCE = CONTAINING_RECORD(pLE, DCE, List); + do { if(!pDCE) break; pDCE = DceFreeDCE(pDCE, TRUE); if(!pDCE) break; - } while (pDCE != FirstDce); - KeLeaveCriticalRegion(); - FirstDce = NULL; + } + while (pLE != &LEDce); } VOID FASTCALL @@ -683,16 +811,19 @@ DceResetActiveDCEs(PWINDOW_OBJECT Window) PWINDOW_OBJECT CurrentWindow; INT DeltaX; INT DeltaY; + PLIST_ENTRY pLE; if (NULL == Window) { return; } - pDCE = FirstDce; + pLE = LEDce.Flink; + pDCE = CONTAINING_RECORD(pLE, DCE, List); if(!pDCE) return; // Another null test! do { if(!pDCE) break; + if(pLE == &LEDce) break; if (0 == (pDCE->DCXFlags & DCX_DCEEMPTY)) { if (Window->hSelf == pDCE->hwndCurrent) @@ -704,7 +835,8 @@ DceResetActiveDCEs(PWINDOW_OBJECT Window) CurrentWindow = UserGetWindowObject(pDCE->hwndCurrent); if (NULL == CurrentWindow) { - pDCE = (PDCE) pDCE->List.Flink; + pLE = pDCE->List.Flink; + pDCE = CONTAINING_RECORD(pLE, DCE, List); continue; } } @@ -712,7 +844,8 @@ DceResetActiveDCEs(PWINDOW_OBJECT Window) if (!GDIOBJ_ValidateHandle(pDCE->hDC, GDI_OBJECT_TYPE_DC) || (dc = DC_LockDc(pDCE->hDC)) == NULL) { - pDCE = (PDCE) pDCE->List.Flink; + pLE = pDCE->List.Flink; + pDCE = CONTAINING_RECORD(pLE, DCE, List); continue; } if (Window == CurrentWindow || IntIsChildWindow(Window, CurrentWindow)) @@ -751,17 +884,21 @@ DceResetActiveDCEs(PWINDOW_OBJECT Window) // UserDerefObject(CurrentWindow); } } - pDCE =(PDCE) pDCE->List.Flink; - } while (pDCE != FirstDce); + pLE = pDCE->List.Flink; + pDCE = CONTAINING_RECORD(pLE, DCE, List); + } + while (pLE != &LEDce); } HWND FASTCALL IntWindowFromDC(HDC hDc) { DCE *Dce; + PLIST_ENTRY pLE; HWND Ret = NULL; - KeEnterCriticalRegion(); - Dce = FirstDce; + + pLE = LEDce.Flink; + Dce = CONTAINING_RECORD(pLE, DCE, List); do { if(Dce->hDC == hDc) @@ -769,9 +906,10 @@ IntWindowFromDC(HDC hDc) Ret = Dce->hwndCurrent; break; } - Dce = (PDCE)Dce->List.Flink; - } while (Dce != FirstDce); - KeLeaveCriticalRegion(); + pLE = Dce->List.Flink; + Dce = CONTAINING_RECORD(pLE, DCE, List); + } + while (pLE != &LEDce); return Ret; } @@ -779,12 +917,13 @@ INT FASTCALL UserReleaseDC(PWINDOW_OBJECT Window, HDC hDc, BOOL EndPaint) { PDCE dce; + PLIST_ENTRY pLE; INT nRet = 0; BOOL Hit = FALSE; DPRINT("%p %p\n", Window, hDc); - dce = FirstDce; - KeEnterCriticalRegion(); + pLE = LEDce.Flink; + dce = CONTAINING_RECORD(pLE, DCE, List); do { if(!dce) break; @@ -793,11 +932,10 @@ UserReleaseDC(PWINDOW_OBJECT Window, HDC hDc, BOOL EndPaint) Hit = TRUE; break; } - - dce = (PDCE) dce->List.Flink; + pLE = dce->List.Flink; + dce = CONTAINING_RECORD(pLE, DCE, List); } - while (dce != FirstDce ); - KeLeaveCriticalRegion(); + while (pLE != &LEDce ); if ( Hit && (dce->DCXFlags & DCX_DCEBUSY)) { diff --git a/reactos/subsystems/win32/win32k/ntuser/window.c b/reactos/subsystems/win32/win32k/ntuser/window.c index 209bed97ef9..52708c43ea5 100644 --- a/reactos/subsystems/win32/win32k/ntuser/window.c +++ b/reactos/subsystems/win32/win32k/ntuser/window.c @@ -478,6 +478,7 @@ static LRESULT co_UserFreeWindow(PWINDOW_OBJECT Window, Window->SystemMenu = (HMENU)0; } + DceFreeWindowDCE(Window); /* Always do this to catch orphaned DCs */ #if 0 /* FIXME */ WINPROC_FreeProc(Window->winproc, WIN_PROC_WINDOW);