diff --git a/dlls/gdi32/bitmap.c b/dlls/gdi32/bitmap.c index e6d1dbd996..c1d0859a13 100644 --- a/dlls/gdi32/bitmap.c +++ b/dlls/gdi32/bitmap.c @@ -587,7 +587,7 @@ static HGDIOBJ BITMAP_SelectObject( HGDIOBJ handle, void *obj, HDC hdc ) if (handle) { dc->hBitmap = handle; - dc->flags &= ~DC_DIRTY; + dc->dirty = 0; SetRectRgn( dc->hVisRgn, 0, 0, bitmap->bitmap.bmWidth, bitmap->bitmap.bmHeight); DC_InitDC( dc ); } diff --git a/dlls/gdi32/clipping.c b/dlls/gdi32/clipping.c index b33543dd1a..9229bf9201 100644 --- a/dlls/gdi32/clipping.c +++ b/dlls/gdi32/clipping.c @@ -58,8 +58,6 @@ void CLIPPING_UpdateGCRegion( DC * dc ) exit(1); } - if (dc->flags & DC_DIRTY) ERR( "DC is dirty. Please report this.\n" ); - /* update the intersection of meta and clip regions */ if (dc->hMetaRgn && dc->hClipRgn) { @@ -177,7 +175,7 @@ INT16 WINAPI SelectVisRgn16( HDC16 hdc16, HRGN16 hrgn ) TRACE("%p %04x\n", hdc, hrgn ); - dc->flags &= ~DC_DIRTY; + dc->dirty = 0; retval = CombineRgn( dc->hVisRgn, HRGN_32(hrgn), 0, RGN_COPY ); CLIPPING_UpdateGCRegion( dc ); @@ -547,7 +545,6 @@ INT16 WINAPI RestoreVisRgn16( HDC16 hdc16 ) dc->saved_visrgn = saved->next; DeleteObject( saved->hrgn ); HeapFree( GetProcessHeap(), 0, saved ); - dc->flags &= ~DC_DIRTY; CLIPPING_UpdateGCRegion( dc ); done: DC_ReleaseDCPtr( dc ); diff --git a/dlls/gdi32/dc.c b/dlls/gdi32/dc.c index 222da2da26..adf88cc992 100644 --- a/dlls/gdi32/dc.c +++ b/dlls/gdi32/dc.c @@ -20,6 +20,7 @@ #include "config.h" +#include #include #include #include @@ -50,6 +51,25 @@ static const struct gdi_obj_funcs dc_funcs = DC_DeleteObject /* pDeleteObject */ }; + +static inline DC *get_dc_obj( HDC hdc ) +{ + DC *dc = GDI_GetObjPtr( hdc, MAGIC_DONTCARE ); + if (!dc) return NULL; + + if ((GDIMAGIC(dc->header.wMagic) != DC_MAGIC) && + (GDIMAGIC(dc->header.wMagic) != MEMORY_DC_MAGIC) && + (GDIMAGIC(dc->header.wMagic) != METAFILE_DC_MAGIC) && + (GDIMAGIC(dc->header.wMagic) != ENHMETAFILE_DC_MAGIC)) + { + GDI_ReleaseObj( hdc ); + SetLastError( ERROR_INVALID_HANDLE ); + dc = NULL; + } + return dc; +} + + /*********************************************************************** * DC_AllocDC */ @@ -63,6 +83,9 @@ DC *DC_AllocDC( const DC_FUNCTIONS *funcs, WORD magic ) dc->hSelf = hdc; dc->funcs = funcs; dc->physDev = NULL; + dc->thread = GetCurrentThreadId(); + dc->refcount = 1; + dc->dirty = 0; dc->saveLevel = 0; dc->saved_dc = 0; dc->dwHookData = 0; @@ -136,16 +159,22 @@ DC *DC_AllocDC( const DC_FUNCTIONS *funcs, WORD magic ) */ DC *DC_GetDCPtr( HDC hdc ) { - GDIOBJHDR *ptr = GDI_GetObjPtr( hdc, MAGIC_DONTCARE ); - if (!ptr) return NULL; - if ((GDIMAGIC(ptr->wMagic) == DC_MAGIC) || - (GDIMAGIC(ptr->wMagic) == MEMORY_DC_MAGIC) || - (GDIMAGIC(ptr->wMagic) == METAFILE_DC_MAGIC) || - (GDIMAGIC(ptr->wMagic) == ENHMETAFILE_DC_MAGIC)) - return (DC *)ptr; - GDI_ReleaseObj( hdc ); - SetLastError( ERROR_INVALID_HANDLE ); - return NULL; + DC *dc = get_dc_obj( hdc ); + if (!dc) return NULL; + + if (!InterlockedCompareExchange( &dc->refcount, 1, 0 )) + { + dc->thread = GetCurrentThreadId(); + } + else if (dc->thread != GetCurrentThreadId()) + { + GDI_ReleaseObj( hdc ); + SetLastError( ERROR_ACCESS_DENIED ); + return NULL; + } + else InterlockedIncrement( &dc->refcount ); + + return dc; } /*********************************************************************** @@ -159,10 +188,9 @@ DC *DC_GetDCUpdate( HDC hdc ) { DC *dc = DC_GetDCPtr( hdc ); if (!dc) return NULL; - while (dc->flags & DC_DIRTY) + while (InterlockedExchange( &dc->dirty, 0 )) { DCHOOKPROC proc = dc->hookThunk; - dc->flags &= ~DC_DIRTY; if (proc) { DWORD_PTR data = dc->dwHookData; @@ -181,6 +209,7 @@ DC *DC_GetDCUpdate( HDC hdc ) */ void DC_ReleaseDCPtr( DC *dc ) { + release_dc_ptr( dc ); GDI_ReleaseObj( dc->hSelf ); } @@ -190,10 +219,66 @@ void DC_ReleaseDCPtr( DC *dc ) */ BOOL DC_FreeDCPtr( DC *dc ) { + assert( dc->refcount == 1 ); return GDI_FreeObject( dc->hSelf, dc ); } +/*********************************************************************** + * get_dc_ptr + * + * Retrieve a DC pointer but release the GDI lock. + */ +DC *get_dc_ptr( HDC hdc ) +{ + DC *dc = get_dc_obj( hdc ); + if (!dc) return NULL; + + if (!InterlockedCompareExchange( &dc->refcount, 1, 0 )) + { + dc->thread = GetCurrentThreadId(); + } + else if (dc->thread != GetCurrentThreadId()) + { + WARN( "dc %p belongs to thread %04x\n", hdc, dc->thread ); + GDI_ReleaseObj( hdc ); + return NULL; + } + else InterlockedIncrement( &dc->refcount ); + + GDI_ReleaseObj( hdc ); + return dc; +} + + +/*********************************************************************** + * release_dc_ptr + */ +void release_dc_ptr( DC *dc ) +{ + LONG ref; + + dc->thread = 0; + ref = InterlockedDecrement( &dc->refcount ); + assert( ref >= 0 ); + if (ref) dc->thread = GetCurrentThreadId(); /* we still own it */ +} + + +/*********************************************************************** + * update_dc + * + * Make sure the DC vis region is up to date. + * This function may call up to USER so the GDI lock should _not_ + * be held when calling it. + */ +void update_dc( DC *dc ) +{ + if (InterlockedExchange( &dc->dirty, 0 ) && dc->hookThunk) + dc->hookThunk( dc->hSelf, DCHC_INVALIDVISRGN, dc->dwHookData, 0 ); +} + + /*********************************************************************** * DC_DeleteObject */ @@ -355,6 +440,8 @@ HDC WINAPI GetDCState( HDC hdc ) newdc->BoundsRect = dc->BoundsRect; newdc->hSelf = (HDC)handle; + newdc->thread = GetCurrentThreadId(); + newdc->refcount = 1; newdc->saveLevel = 0; newdc->saved_dc = 0; @@ -415,7 +502,7 @@ void WINAPI SetDCState( HDC hdc, HDC hdcs ) } TRACE("%p %p\n", hdc, hdcs ); - dc->flags = dcs->flags & ~(DC_SAVED | DC_DIRTY); + dc->flags = dcs->flags & ~DC_SAVED; dc->layout = dcs->layout; dc->hDevice = dcs->hDevice; dc->ROPmode = dcs->ROPmode; @@ -801,6 +888,12 @@ BOOL WINAPI DeleteDC( HDC hdc ) GDI_CheckNotLock(); if (!(dc = DC_GetDCPtr( hdc ))) return FALSE; + if (dc->refcount != 1) + { + FIXME( "not deleting busy DC %p refcount %u\n", dc->hSelf, dc->refcount ); + DC_ReleaseDCPtr( dc ); + return FALSE; + } /* Call hook procedure to check whether is it OK to delete this DC */ if (dc->hookThunk) @@ -1419,25 +1512,22 @@ DWORD WINAPI GetDCHook16( HDC16 hdc16, FARPROC16 *phookProc ) WORD WINAPI SetHookFlags16(HDC16 hdc16, WORD flags) { HDC hdc = HDC_32( hdc16 ); - DC *dc = DC_GetDCPtr( hdc ); + DC *dc = get_dc_obj( hdc ); /* not get_dc_ptr, this needs to work from any thread */ + LONG ret = 0; - if( dc ) - { - WORD wRet = dc->flags & DC_DIRTY; + if (!dc) return 0; - /* "Undocumented Windows" info is slightly confusing. - */ + /* "Undocumented Windows" info is slightly confusing. */ - TRACE("hDC %p, flags %04x\n",hdc,flags); + TRACE("hDC %p, flags %04x\n",hdc,flags); - if( flags & DCHF_INVALIDATEVISRGN ) - dc->flags |= DC_DIRTY; - else if( flags & DCHF_VALIDATEVISRGN || !flags ) - dc->flags &= ~DC_DIRTY; - DC_ReleaseDCPtr( dc ); - return wRet; - } - return 0; + if (flags & DCHF_INVALIDATEVISRGN) + ret = InterlockedExchange( &dc->dirty, 1 ); + else if (flags & DCHF_VALIDATEVISRGN || !flags) + ret = InterlockedExchange( &dc->dirty, 0 ); + + GDI_ReleaseObj( dc ); + return ret; } /*********************************************************************** diff --git a/dlls/gdi32/enhmfdrv/init.c b/dlls/gdi32/enhmfdrv/init.c index ab6208ad96..d272f7520d 100644 --- a/dlls/gdi32/enhmfdrv/init.c +++ b/dlls/gdi32/enhmfdrv/init.c @@ -444,6 +444,12 @@ HENHMETAFILE WINAPI CloseEnhMetaFile(HDC hdc) /* [in] metafile DC */ DC_ReleaseDCPtr( dc ); return NULL; } + if (dc->refcount != 1) + { + FIXME( "not deleting busy DC %p refcount %u\n", dc->hSelf, dc->refcount ); + DC_ReleaseDCPtr( dc ); + return NULL; + } physDev = (EMFDRV_PDEVICE *)dc->physDev; if(dc->saveLevel) diff --git a/dlls/gdi32/gdi_private.h b/dlls/gdi32/gdi_private.h index 44764247b5..74cca26de9 100644 --- a/dlls/gdi32/gdi_private.h +++ b/dlls/gdi32/gdi_private.h @@ -269,6 +269,9 @@ typedef struct tagDC HDC hSelf; /* Handle to this DC */ const struct tagDC_FUNCS *funcs; /* DC function table */ PHYSDEV physDev; /* Physical device (driver-specific) */ + DWORD thread; /* thread owning the DC */ + LONG refcount; /* thread refcount */ + LONG dirty; /* dirty flag */ INT saveLevel; HDC saved_dc; DWORD_PTR dwHookData; @@ -335,7 +338,6 @@ typedef struct tagDC /* DC flags */ #define DC_SAVED 0x0002 /* It is a saved DC */ -#define DC_DIRTY 0x0004 /* hVisRgn has to be updated */ #define DC_BOUNDS_ENABLE 0x0008 /* Bounding rectangle tracking is enabled */ #define DC_BOUNDS_SET 0x0010 /* Bounding rectangle has been set */ @@ -397,6 +399,9 @@ extern DC * DC_GetDCUpdate( HDC hdc ); extern DC * DC_GetDCPtr( HDC hdc ); extern void DC_ReleaseDCPtr( DC *dc ); extern BOOL DC_FreeDCPtr( DC *dc ); +extern DC *get_dc_ptr( HDC hdc ); +extern void release_dc_ptr( DC *dc ); +extern void update_dc( DC *dc ); extern void DC_InitDC( DC * dc ); extern void DC_UpdateXforms( DC * dc ); diff --git a/dlls/gdi32/mfdrv/init.c b/dlls/gdi32/mfdrv/init.c index 235b1cd1b6..a73c701326 100644 --- a/dlls/gdi32/mfdrv/init.c +++ b/dlls/gdi32/mfdrv/init.c @@ -314,6 +314,12 @@ static DC *MFDRV_CloseMetaFile( HDC hdc ) DC_ReleaseDCPtr( dc ); return NULL; } + if (dc->refcount != 1) + { + FIXME( "not deleting busy DC %p refcount %u\n", dc->hSelf, dc->refcount ); + DC_ReleaseDCPtr( dc ); + return NULL; + } physDev = (METAFILEDRV_PDEVICE *)dc->physDev; /* Construct the end of metafile record - this is documented