Discussion:
CreateSolidBrush (GDI32) - how to "cache" it ?
(too old to reply)
R.Wieser
2021-04-03 08:25:29 UTC
Permalink
Hello all,

I'm currently playing around with giving a dialog different colors than the
standard one, and have ran into a bit of a problem:

I want to mix using the results of CreateSolidBrush and GetSysColorBrush.
The thing is that while the latter handles do need to be deleted, the former
ones must be (otherwise GDI resource leakage will occur).

While calling DeleteObject on a GetSysColorBrush handle doesn't do anything,
accidentily prematurily deleting a CreateSolidBrush handle causes "bad
things" to happen.

So:

tl;dr:

I would like to cache (lock?) my CreateSolidBrush handle just like the
GetSysColorBrush handle is said to be. How do I do that ?

Regards,
Rudy Wieser

P.s.
I tried to take a peek into the CreateSolidBrush result handle (in an
attempt to compare the cached and non-cached brush in search for a "lock"
bit), but alas, it seems to be stored somewhere where I am not allowed to
even look at it ....
JJ
2021-04-03 15:27:49 UTC
Permalink
Post by R.Wieser
Hello all,
I'm currently playing around with giving a dialog different colors than the
I want to mix using the results of CreateSolidBrush and GetSysColorBrush.
The thing is that while the latter handles do need to be deleted, the former
ones must be (otherwise GDI resource leakage will occur).
While calling DeleteObject on a GetSysColorBrush handle doesn't do anything,
accidentily prematurily deleting a CreateSolidBrush handle causes "bad
things" to happen.
I would like to cache (lock?) my CreateSolidBrush handle just like the
GetSysColorBrush handle is said to be. How do I do that ?
Regards,
Rudy Wieser
P.s.
I tried to take a peek into the CreateSolidBrush result handle (in an
attempt to compare the cached and non-cached brush in search for a "lock"
bit), but alas, it seems to be stored somewhere where I am not allowed to
even look at it ....
There's no way to lock GDI handles. You'll just have to conditionally delete
the handles, or create a new brush with the same color as the system brush.

The statement that system color brush handles must not be deleted, only
applies to early Windows 95. Here's the last paragraph of the Remarks
section of the GetSysColorBrush in different SDK dicumentation versions.

Windows 95 SDK:
"An application must not register a window class for a window using a system
brush."

MSDN Library 2008:
"System color brushes are owned by the system and must not be destroyed."

MSDN website:
"System color brushes are owned by the system so you don't need to destroy
them. Although you don't need to delete the logical brush that
GetSysColorBrush returns, no harm occurs by calling DeleteObject."

Notice the significant change of the statement in MSDN Library 2008. It no
longer state about window class registration. Meaning that Microsoft has
added some protection to allow the system color brush to be used with window
class registration. And in current MSDN website, it states that it's OK to
delete system color brushes. Meaning that more protection has been added.

I have no Windows 95 to test with, but in Windows 98, Windows 2000, and
newer versions, the system won't delete system color brush handles.
DeleteObject() will give a success report, but it won't produce any effect.

System brush handles and probably other system GDI handles also, are part of
the system. They're not listed in the process' GDI handles list within the
process TEB (check with Nirsoft GDIView). Processes will see the system GDI
handles as if they're predefined handles. FYI, the handle values are
identical across different process - both 32-bit and 64-bit.

When I debug DeleteObject(), the kernel function for deleting GDI objects
seems to be responsible for protecting the system GDI objects. But
DeleteObject() is the one which protects stock objects.
R.Wieser
2021-04-03 16:49:19 UTC
Permalink
JJ,
Post by JJ
There's no way to lock GDI handles.
Damn!
Post by JJ
You'll just have to conditionally delete the handles, or create
a new brush with the same color as the system brush.
Creating multiple copies of the same brush is what I'm trying to evade.
And it wouldn't really solve the problem either :

I intend to use a single brush at different places (including as the
background of controls placed on the dialog), but still want to be able to
replace the brush at some places with another. (example : WM_CTLCOLORDLG)

After such a replacement should or should I call DeleteObject on the
returned old brush ? If I don't I could get a resource leak, if I do
other places may suddenly lose their brush ... Catch22 :-(
Post by JJ
"System color brushes are owned by the system so you don't
need to destroy them. Although you don't need to delete the
logical brush that GetSysColorBrush returns, no harm occurs
by calling DeleteObject."
Thats the info I'm currently working with/by.
Post by JJ
They're not listed in the process' GDI handles list within the
process TEB (check with Nirsoft GDIView).
Thanks for the GDIView suggestion. Might come in handly as a resource-leak
viewer.
Post by JJ
But DeleteObject() is the one which protects stock objects.
Does it ? I wonder ...

The best way to "protect" something is just not to give it to the one who
can trash it. In the above case I could imagine that DeleteObject simply
doesn't get the list of stock objects, meaning that it can't find the
to-be-deleted object and thus doesn't do anything.

Than again, I was hoping I would be able to find a "don't delete" flag in
the object itself - which certainly does need the cooperation of
DeleteObject.

Regards,
Rudy Wieser
R.Wieser
2021-04-04 20:07:52 UTC
Permalink
JJ,
DeleteObject() at least, actively protects them. The kernel function
may or may not protect them. I don't bother to check because
debugging the kernel is a pain in Windows NT.
Thanks for the explanation.

Though I'll probably still try what I should have done in the first place :
disassemble that function and see what I can learn from it. :-)
Even if there is such flag, it would be within the internal data structure
of the GDI object. But in Windows NT, GDI object's data is within the
kernel address space. So, there's no way to reach it from the user mode
application without custom driver, or without undocumented function (if
there is any).
I noticed. :-\ Than again, I just tried to use the handle as a memory
pointer. Maybe looking at the disassembly of the function will shed a bit
more light on it.

Regards,
Rudy Wieser
R.Wieser
2021-04-09 08:16:16 UTC
Permalink
JJ,
Post by JJ
They're not listed in the process' GDI handles list within the
process TEB (check with Nirsoft GDIView).
FYI:

While disassembling DeleteObject I found the GDI object array (all 65536 of
them), and was allowed to read them (already tried writing, but was not
allowed :-) ).

Retrieving all GDI object records of the current process is rather easy, as
the process id is stored at offset 4. As you can recreate the GDI object
handle from that record its also easy to show the objects type.

And just now I found the functions to retrieve the above GDI object array
(GdiQueryTable) and the one which seems to recreate the GDI object handle
(GdiFixUpHandle).

IOW, at least under XPsp3 * writing a simple "leakage" detector falls under
the possibilities.

*As I (unknowingly, still) used an old gdi32.lib I found out that W98se
doesn't seem to have the GdiQueryTable function - or at least not by that
name.

Regards,
Rudy Wieser

Loading...