CreateSolidBrush (GDI32) - how to "cache" it ?

3 views
Skip to first unread message

R.Wieser

unread,
Apr 3, 2021, 4:25:38 AMApr 3
to
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

unread,
Apr 3, 2021, 11:27:59 AMApr 3
to
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

unread,
Apr 3, 2021, 12:49:43 PMApr 3
to
JJ,

> There's no way to lock GDI handles.

Damn!

> 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 :-(

> "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.

> 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.

> 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


JJ

unread,
Apr 4, 2021, 3:22:38 PMApr 4
to
On Sat, 3 Apr 2021 18:49:19 +0200, R.Wieser wrote:
>> But DeleteObject() is the one which protects stock objects.
>
> Does it ? I wonder ...

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.

> 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.

Apparently, DeleteObject() can actually detect whether a handle is a stock
object or not. The detection is not based on whether the handle is in the
process TEB or not. Because if it is, system GDI objects would be detected
too. But they don't. When system GDI handle is passed to DeleteObject(),
DeleteObject() will pass it to the kernel function. This doesn't apply if
the handle is a stock object.

> 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.

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).

R.Wieser

unread,
Apr 4, 2021, 4:07:59 PMApr 4
to
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

unread,
Apr 9, 2021, 4:16:39 AMApr 9
to
JJ,

You wrote a few days ago :

> 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


Reply all
Reply to author
Forward
0 new messages