Refcounting in SelectObject

Uwe Bonnes bon at elektron.ikp.physik.tu-darmstadt.de
Thu Aug 16 19:28:18 CDT 2001


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 at 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 );




More information about the wine-devel mailing list