Hallo,
we discussed this January 2001 with no final outcome.
Some application (gcprevue version 9) does
080673a0:Call gdi32.SelectObject(000011d4,000011d8) ret=5f485bb1 080673a0:Ret gdi32.SelectObject() retval=00000044 ret=5f485bb1 080673a0:Call gdi32.DeleteObject(000011d8) ret=5f487813 080673a0:Ret gdi32.DeleteObject() retval=00000001 ret=5f487813 080673a0:Call gdi32.BitBlt(000011d4,00000000,00000000,0000008c,000001a7,000009d0,00000000,00000000,00cc0020) ret=0048d1e6
and gets an XErrorEvent and fails to Debugbreak. Starting theapplication with winedbg still gets this error, but DebugBreak isn't executed and the application proceeds.
The Windows Api explicitly tells for DeleteObject:
Return Value: ... If the specified handle is not valid or is currently selected into a device context, the return value is FALSE.
Windows Internals tells about some elements in GDIOBJHDR in debugbuild of windows and has pseudo code for incrementing some refrence counters in SelectObject. Appended patch tries to implement this scheme.
The original discussion dissolved into nothing after Andi saw the need for such refcounting, but also mentioned possible resource leakage.
Appended code tries to clean up better by: - Remembering an unsuccessfull DeleteObject by adding 0x8000 to the Refcount - Call DeleteObject in SelectObject when the RefCount of the released handle is 0x8000 - Decrementing the RefCount when a hBitmap is still selected into a DC when DeleteDC is called for that DC - Call DeleteObject in DeleteDC when RefCount is 0x8000
For some applications I tested, I always saw the delayed DeleteObject done after the refused DeleteObject in the first place. Is this approach better?
Uwe Bonnes bon@elektron.ikp.physik.tu-darmstadt.de
Free Software: If you contribute nothing, expect nothing -- Index: wine/include/gdi.h =================================================================== RCS file: /home/wine/wine/include/gdi.h,v retrieving revision 1.50 diff -u -r1.50 gdi.h --- wine/include/gdi.h 2001/08/15 23:33:20 1.50 +++ wine/include/gdi.h 2001/08/16 23:23:12 @@ -45,6 +45,9 @@ HANDLE16 hNext; WORD wMagic; DWORD dwCount; + WORD wMetaList; + WORD wSelCount;/* only in debug builds of windows */ + WORD wObjTask;/* only in debug builds of windows */ } GDIOBJHDR;
Index: wine/objects/gdiobj.c =================================================================== RCS file: /home/wine/wine/objects/gdiobj.c,v retrieving revision 1.54 diff -u -r1.54 gdiobj.c --- wine/objects/gdiobj.c 2001/08/16 19:13:52 1.54 +++ wine/objects/gdiobj.c 2001/08/16 23:23:13 @@ -374,6 +374,7 @@ obj->hNext = 0; obj->wMagic = magic|OBJECT_NOSYSTEM; obj->dwCount = ++count; + obj->wSelCount = 0;
TRACE_SEC( *handle, "enter" ); return obj; @@ -545,6 +546,15 @@ GDI_ReleaseObj( obj ); return TRUE; } + + if(header->wSelCount) + { + TRACE("delayed for %04x because object in use, count %d\n", + obj,header->wSelCount); + header->wSelCount+=0x8000; /* mark for delete */ + GDI_ReleaseObj( obj ); + return FALSE; + } TRACE("%04x\n", obj );
@@ -814,12 +824,32 @@ HGDIOBJ WINAPI SelectObject( HDC hdc, HGDIOBJ handle ) { HGDIOBJ ret = 0; + GDIOBJHDR *header; + DC * dc = DC_GetDCUpdate( hdc ); if (!dc) return 0; TRACE("hdc=%04x %04x\n", hdc, handle ); if (dc->funcs->pSelectObject) ret = dc->funcs->pSelectObject( dc, handle ); GDI_ReleaseObj( hdc ); + if (ret) + { + if (!(header = GDI_GetObjPtr( handle, MAGIC_DONTCARE ))) return 0; + header->wSelCount++; + GDI_ReleaseObj(handle); + if (!(header = GDI_GetObjPtr( ret, MAGIC_DONTCARE ))) return 0; + if(header->wSelCount) + header->wSelCount--; + if(header->wSelCount == 0x8000) /* handle delayed DeleteObject*/ + { + header->wSelCount = 0; + GDI_ReleaseObj(ret); + TRACE("executing delayed DeleteObject for %04x\n",ret); + DeleteObject(ret); + } + else + GDI_ReleaseObj(ret); + } return ret; }
Index: wine/objects/dc.c =================================================================== RCS file: /home/wine/wine/objects/dc.c,v retrieving revision 1.62 diff -u -r1.62 dc.c --- wine/objects/dc.c 2001/08/16 19:01:23 1.62 +++ wine/objects/dc.c 2001/08/16 23:23:14 @@ -766,6 +766,25 @@ if (dc->hGCClipRgn) DeleteObject( dc->hGCClipRgn ); if (dc->pAbortProc) THUNK_Free( (FARPROC)dc->pAbortProc ); if (dc->hookThunk) THUNK_Free( (FARPROC)dc->hookThunk ); + if (dc->hBitmap) + { + GDIOBJHDR * header; + if ((header = GDI_GetObjPtr( dc->hBitmap, MAGIC_DONTCARE ))) + { + if(header->wSelCount ) + header->wSelCount--; + if(header->wSelCount == 0x8000) + { + header->wSelCount = 0; + GDI_ReleaseObj(dc->hBitmap); + TRACE("executing delayed DeleteObject for %04x\n",dc->hBitmap); + DeleteObject(dc->hBitmap); + } + else + GDI_ReleaseObj(dc->hBitmap); + + } + } PATH_DestroyGdiPath(&dc->path);
GDI_FreeObject( hdc, dc );