Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Prevent background painting in WM_PAINT

1 view
Skip to first unread message

Gili

unread,
May 24, 2004, 12:51:19 AM5/24/04
to
Hi,

I found out that the SysListView32 window's WM_PAINT handler is
responsible for telling the underlying desktop to draw the wallpaper before
it invokes its own WM_ERASEBKGND message handler. Since I am trying to
override wallpaper painting, I don't want the original wallpaper to draw at
all (currently it does and this causes flickering).

Once WM_PAINT is sent to SysListView32, is there a way I can tell it to
not erase its background before passing the message on to the original
handler? That is, is there a way to modify the fErase flag that WM_PAINT
will get back from BeginPaint() before I invoke the WM_PAINT handler? Any
other ideas are welcome.

I've noticed that when ActiveDesktop is enabled, SysListView32 is smart
enough to not bother painting the wallpaper underneath the ActiveDesktop
client region. Since I'm not Microsoft and I don't have access to the
original source-code for SysListView32's WM_PAINT handler, is there any
other way for me to achieve the same functionality without flickering?

Thanks,
Gili


John Carson

unread,
May 24, 2004, 1:13:06 AM5/24/04
to
"Gili" <sitti...@bbs.darktech.org> wrote in message
news:usbDArUQ...@TK2MSFTNGP11.phx.gbl

> Hi,
>
> I found out that the SysListView32 window's WM_PAINT handler is
> responsible for telling the underlying desktop to draw the wallpaper
> before it invokes its own WM_ERASEBKGND message handler. Since I am
> trying to override wallpaper painting, I don't want the original
> wallpaper to draw at all (currently it does and this causes
> flickering).

What was wrong with my suggestion some time ago to remove the background
wallpaper so there is nothing to paint?

http://groups.google.com/groups?q=John+Carson+wallpaper+group:microsoft.public.win32.programmer.gdi&hl=en&lr=&ie=UTF-8&group=microsoft.public.win32.programmer.gdi&selm=%23rD7rqxLEHA.2100%40TK2MSFTNGP10.phx.gbl&rnum=1

--
John Carson
1. To reply to email address, remove donald
2. Don't reply to email address (post here instead)

Severian

unread,
May 24, 2004, 1:49:15 AM5/24/04
to

If you want to prevent wallpaper display *alltogether*, just remove it
while you're running (and perhaps put it back when you exit).

If you only want to prevent it painting under a part of the desktop
(say, your drawing area), look into ValidateRect().

--
Sev

Gili

unread,
May 24, 2004, 2:23:00 AM5/24/04
to
Even if you set the wallpaper to "none", wouldn't it still paint the
desktop solid color background?

Gili

"John Carson" <donald...@datafast.net.au> wrote in message
news:%23zlIX3U...@TK2MSFTNGP09.phx.gbl...

Gili

unread,
May 24, 2004, 2:40:59 AM5/24/04
to

I overrode the WM_PAINT with my own and tried calling ValidateRect()
immediately before calling the original WM_PAINT handler, but what ends up
happening is that the wallpaper gets drawn but nothing else (icons, etc).
Whatever code is being executed, it seems to ignore the update region.

I wish I could execute the entire WM_PAINT handler, minus the code that
draws the wallpaper. Any more ideas?

Gili

"Severian" <seve...@chlamydia-is-not-a-flower.com> wrote in message
news:a133b05v2minld3qo...@4ax.com...

Severian

unread,
May 24, 2004, 2:59:13 AM5/24/04
to
> I overrode the WM_PAINT with my own and tried calling ValidateRect()
>immediately before calling the original WM_PAINT handler, but what ends up
>happening is that the wallpaper gets drawn but nothing else (icons, etc).
>Whatever code is being executed, it seems to ignore the update region.
>
> I wish I could execute the entire WM_PAINT handler, minus the code that
>draws the wallpaper. Any more ideas?

Override WM_ERASEBKGND? (sp?)

--
Sev

Severian

unread,
May 24, 2004, 3:03:42 AM5/24/04
to

Also, have you tried making your window a child of either the Progman
or SHELLDLL_DefView window, and setting its z-order so it's before
SysListView32?

--
Sev

John Carson

unread,
May 24, 2004, 11:38:55 AM5/24/04
to
"Gili" <sitti...@bbs.darktech.org> wrote in message
news:%231hAPeV...@TK2MSFTNGP09.phx.gbl

> Even if you set the wallpaper to "none", wouldn't it still paint
> the desktop solid color background?

Yes, but you still may get less flickering. You can remove the wallpaper in
the Control Panel (under Display) so it will take almost no time to test.

Is there a reason why you can't have the Desktop window draw either a bitmap
or a solid color that is consistent with the image that you want to draw
yourself?

If neither of the above suggestions is acceptable, there is a way to do what
you want. You need to subclass the SysListView32 window and process WM_PAINT
as shown below.

The idea is as follows. When BeginPaint is called, it either does or does
not send WM_ERASEBKGND messages depending on whether the erase flag has been
set. If you can't control the setting of this flag in the first instance,
then you need to clear the flag by calling BeginPaint, but with the update
region validated so that BeginPaint doesn't lead to any drawing. You can
then call InvalidateRgn using the stored update region and a third argument
of FALSE. This means that WM_ERASEBKGND messages are not sent when the
default window procedure calls BeginPaint.

A sample subclassing window procedure is given below (it would be more
efficient to call CreateRectRgn only once and destroy it at exit).

LRESULT SubProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if(message == WM_PAINT)
{
HRGN hrgn = CreateRectRgn(0,0,1,1);
GetUpdateRgn(hwnd, hrgn, FALSE);

// clear out erase flag
ValidateRgn(hwnd, hrgn);
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
EndPaint(hwnd, &ps);

// restore update region so default processing draws icons
InvalidateRgn(hwnd, hrgn, FALSE);
DeleteObject(hrgn);

// fall through to default processing
}
return CallWindowProc(oldProc, hwnd, message, wParam, lParam);

Gili

unread,
May 24, 2004, 5:40:11 PM5/24/04
to

The problem is if I place my window on top of SysListView32, it'll draw
the icon drawing as well as block mouse clicks.

As for overriding WM_ERASEBKGND, I already did that but aparently this
code isn't the one causing the wallpaper to paint. SysListView32's WM_PAINT
tells the wallpaper to paint because calling WM_ERASEBKGND on itself so
overriding WM_ERASEBKGND does not help. I need to somehow override WM_PAINT
or tell it not to paint the wallpaper background.

Gili

"Severian" <seve...@chlamydia-is-not-a-flower.com> wrote in message

news:o773b0lcm49tn7601...@4ax.com...

Gili

unread,
May 24, 2004, 6:31:01 PM5/24/04
to
Hi John,

That was a good idea on validating the update area prior to WM_PAINT and
it worked except that now the icon text background is non-transparent.
Aparently unless WM_PAINT of SysListView32 draws the background it refuses
to make the text background transparent. Dang. I really wish I could tell it
to use WM_ERASEBKGND as its reference background instead of the wallpaper. I
was told sometime in the past that transparent icon text backgrounds only
work if you have a reference background to work against, which is probably
why I'm seeing what I'm seeing. Any ideas?

Gili

"John Carson" <donald...@datafast.net.au> wrote in message

news:etKuTVa...@TK2MSFTNGP12.phx.gbl...

Gili

unread,
May 24, 2004, 6:51:11 PM5/24/04
to

I stand corrected, your code works with transparent icon text
backgrounds. There is just one final bug I've got to iron out. When I shake
a window across the desktop, sometimes the wallpaper is drawn in some
regions and in other places it draws my background instead. That is, if I
figure out why this painting anamoly is taking place I'm done.

Can you please explain to me how the bErase flag works for the
GetUpdateRgn() function? To me, the update region is the update region
regardless of if I erase the background or not. What is the difference
between setting this flag to TRUE or FALSE? Also, if it does somehow
internally know which parts of the update region belong to the background
and non-client area, aren't I making it "forget" by calling ValidateRgn()
and InvalidateRgn()? Won't it then think that the entire region specified by
InvalidateRgn() is a client-area? Maybe this is somehow responsible for the
painting problems I am seeing.

My code now reads:

LRESULT SubProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if(message == WM_PAINT)
{
HRGN hrgn = CreateRectRgn(0,0,1,1);
GetUpdateRgn(hwnd, hrgn, FALSE);

// clear out erase flag
ValidateRgn(hwnd, hrgn);
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);

// *** paint my own background here by calling WM_ERASEBKGND (which
I override) using the aforementioned hdc
EndPaint(hwnd, &ps);

// restore update region so default processing draws icons
InvalidateRgn(hwnd, hrgn, FALSE);
DeleteObject(hrgn);

// fall through to default processing
return CallWindowProc(oldProc, hwnd, message, wParam, lParam);
}
return CallWindowProc(oldProc, hwnd, message, wParam, lParam);
}

Maybe I am calling WM_ERASEBKGND using the wrong hdc or calling it in
the wrong place. What do you think?

Thanks,
Gili

"John Carson" <donald...@datafast.net.au> wrote in message

news:etKuTVa...@TK2MSFTNGP12.phx.gbl...

John Carson

unread,
May 25, 2004, 12:34:27 AM5/25/04
to
"Gili" <sitti...@bbs.darktech.org> wrote in message
news:OjlSaGeQ...@TK2MSFTNGP10.phx.gbl

> I stand corrected, your code works with transparent icon text
> backgrounds. There is just one final bug I've got to iron out. When I
> shake a window across the desktop, sometimes the wallpaper is drawn
> in some regions and in other places it draws my background instead.
> That is, if I figure out why this painting anamoly is taking place
> I'm done.
>
> Can you please explain to me how the bErase flag works for the
> GetUpdateRgn() function? To me, the update region is the update region
> regardless of if I erase the background or not. What is the difference
> between setting this flag to TRUE or FALSE?

If you set it to TRUE, then calling the function will by itself cause
drawing to occur, including the sending of WM_ERASEBKGND messages, which is
exactly the opposite of what you are trying to achieve. Try it and you will
see that all of the previous drawing problems have returned.

> Also, if it does somehow
> internally know which parts of the update region belong to the
> background and non-client area, aren't I making it "forget" by
> calling ValidateRgn() and InvalidateRgn()? Won't it then think that
> the entire region specified by InvalidateRgn() is a client-area?
> Maybe this is somehow responsible for the painting problems I am
> seeing.


ValidateRgn and InvalidateRgn cancel each other out so it is as if neither
has been called.

On my computer, I don't have your code for drawing a new background. I just
drag a window around the desktop. If the code works, then all I should see
is the trail of the window I am dragging. The desktop bitmap should never
appear. In my experiments, the code is almost 100% effective. In fact, with
a casual test it appears to work completely. However, if I vary the pace of
the dragging/shaking then, with some effort, I can get sections of the
desktop bitmap to appear.

I suspect that the reason for this is that the time between the subclass
code and the call of BeginPaint in the default processing provides enough
time for new areas of the desktop to become invalid. In that regard,
creating the region once and deleting it when the program ends might speed
up things a little and reduce the problem.

As far as your code goes, I wouldn't be overly concerned if, in extreme
circumstances, you see a flash of the underlying desktop (you could lessen
its impact by setting an unobtrusive desktop color). If, however, a part of
the desktop is sometimes visible after the shaking of the window is
complete, then that is more of a problem. I would try something like this:

LRESULT SubProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if(message == WM_PAINT)
{

GetUpdateRgn(hwnd, hrgn, FALSE);

// clear out erase flag
ValidateRgn(hwnd, hrgn);
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
EndPaint(hwnd, &ps);

// restore update region so default processing draws icons
InvalidateRgn(hwnd, hrgn, FALSE);

// get the most recent version of the update rgn
GetUpdateRgn(hwnd, hrgn, FALSE);
// call default processig
LRESULT lr = CallWindowProc(oldProc, hwnd, message, wParam, lParam);

//
// do own drawing using the most recently saved update region
//

return lr;


}
return CallWindowProc(oldProc, hwnd, message, wParam, lParam);
}


This is still not guaranteed to work perfectly, but may be an improvement. I
don't have any ideas for doing better. It might require a more fundamental
type of hacking, i.e., intercepting API calls. If you want to go that route,
the first thing I would look at is this:

http://research.microsoft.com/sn/detours/

Gili

unread,
May 25, 2004, 12:57:26 AM5/25/04
to
Hi John,

I have noticed the following:

1) If I overriden WM_PAINT to do nothing but return 0, my background draws
perfectly with no wallpaper leak; however no icons are drawn. This
demonstrates that the default WM_PAINT causes the wallpaper to draw.

2) If I use your original code and return 0 right before calling the
original WM_PAINT handler, it still leaks the wallpaper. However, I have
noticed that if I remove the call to ValidateRgn() the leak disappears. It
seems that calling ValidateRgn() causes a problem so I removed it
altogether. Furthermore, I don't see why we need to call it when
BeginPaint()'s documentation says it resets the update region on our behalf.

3) On the issue of seeing "some wallpaper leaks" as you reported when you
tested the code on your end, this
return I have noticed that there is something wrong with calling
ValidateRgn() I don't think the "pace" of calls is an issue. I remember
reading that while the WM_PAINT handler blocks the message queue, the update
region cannot get updated. All changes are queued and only get added to the
update region once you quit. Hence, I believe the wallpaper leak issues you
mentioned are caused by another separate bug. I believe I fixed this issue
by removing ValidateRgn()... try it on your end and see if it helps.

4) I have noticed that even if I follow your original source code, the
wallpaper paints when I call the original WM_PAINT at the end of the winproc
function. It seems that the original code ignores that fErase==false and
paints the background anyway. I am totally at a loss as to what to try next
:(

I have tested this issue on slower machines (1.7GHz at work) and the
flickering is a major problem (very noticable) even though 1/2 a second
later, the repaint completes and there are no regions left on the screen
with painting anamolies. Still, the flickering is a show-stopper for my
product because a lot of people have machines under 2GHz and it is
reasonable for them to expect this to work.

It's ticks me off to see how close I get to finishing this problem yet I
never finish. I truly hope someone comes up with an idea to make this work.
Let me know if you have any more ideas.

Is there a way for me to perhaps detect the region containing the icons
and limit the old WM_PAINT calls to only those regions and nowhere else?
Flickering then only occur in the icons themselves, but ActiveDesktop
experiences the exact same problem so I am ok with it. Any idea as to how to
detect the icons + text region?

Thanks,
Gili

"John Carson" <donald...@datafast.net.au> wrote in message

news:OrIa6GhQ...@tk2msftngp13.phx.gbl...

John Carson

unread,
May 25, 2004, 4:28:22 AM5/25/04
to
"Gili" <sitti...@bbs.darktech.org> wrote in message
news:uBi$DThQEH...@TK2MSFTNGP09.phx.gbl

> Hi John,
>
> I have noticed the following:
>
> 1) If I overriden WM_PAINT to do nothing but return 0, my background
> draws perfectly with no wallpaper leak; however no icons are drawn.
> This demonstrates that the default WM_PAINT causes the wallpaper to
> draw.

Yes. I didn't think this was in doubt (of course, WM_PAINT processing causes
the wallpaper to be drawn by making the Desktop window draw it).

> 2) If I use your original code and return 0 right before calling the
> original WM_PAINT handler, it still leaks the wallpaper.

I don't follow. If you return 0, then that ends the function call so how can
you be subsequently calling the original WM_PAINT handler? How is this case
different from the one you describe in 1) above?

> However, I
> have noticed that if I remove the call to ValidateRgn() the leak
> disappears. It seems that calling ValidateRgn() causes a problem so I
> removed it altogether. Furthermore, I don't see why we need to call
> it when BeginPaint()'s documentation says it resets the update region
> on our behalf.

Because calling BeginPaint causes WM_ERASEBKGND messages to be sent unless
you have already validated the update region. If I remove the ValidateRgn
call, then the desktop wallpaper gets drawn as usual and nothing has been
accomplished. I don't understand what you are doing.

> 3) On the issue of seeing "some wallpaper leaks" as you reported when
> you tested the code on your end, this
> return I have noticed that there is something wrong with calling
> ValidateRgn() I don't think the "pace" of calls is an issue. I
> remember reading that while the WM_PAINT handler blocks the message
> queue, the update region cannot get updated. All changes are queued
> and only get added to the update region once you quit.

I am aware of this supposed locking of the update region, but I don't
believe it lasts until the WM_PAINT handler returns. More likely, it lasts
until BeginPaint returns (and the update region applies unchanged to the HDC
retrieved with that BeginPaint call).

You can confirm ongoing updating with the following experiment. Using a
vanilla application, the idea is that we create an infinite loop in the
WM_PAINT handler so it never returns and see if the update region
nevertheless changes. In order to be sure of what is happening, we don't
start the infinite loop immediately. We set it to start when a WM_PAINT
message is received at least 5 seconds after the first WM_PAINT message. We
then start the application and, in the 5 seconds available, move another
window so that it obscures half of our application. We wait for 5 seconds to
elapse and then move our overlay window just slightly. This triggers a
WM_PAINT message in our application (signified with a beep) and we enter the
infinite loop. If we now move our overlay window away to fully uncover our
application, then the newly uncovered client area should not be erased if
the update region is locked. You can easily confirm that the newly uncovered
client area IS erased, thus proving that the update region is not locked.

The code for handling WM_PAINT is given below:


case WM_PAINT:
{
static bool enterLoop = false;
static double start = (double)clock();
double current = (double)clock();
if ((current - start)/CLOCKS_PER_SEC > 5)
{
enterLoop = true;
MessageBeep(0);
}
HDC hdc;
PAINTSTRUCT ps;
if(enterLoop)
{
while(1)
{


hdc = BeginPaint(hwnd, &ps);
EndPaint(hwnd, &ps);
}
}

else
{


hdc = BeginPaint(hwnd, &ps);
EndPaint(hwnd, &ps);

return 0;
}
}


> Hence, I
> believe the wallpaper leak issues you mentioned are caused by another
> separate bug. I believe I fixed this issue by removing
> ValidateRgn()... try it on your end and see if it helps.

As commented above, this makes no sense to me. Removing the ValidateRgn call
has the effect of restoring normal drawing of the wallpaper.

I also don't understand how you can say you have fixed the issue and yet you
still say below that there is a problem. If I were to make a wild guess, it
would be that by junking my code in its entirety and just painting over what
the Desktop does, you can ensure that your application paints last and
therefore you don't have any permanent traces of the wallpaper. If this
guess is correct, then I would think that there should be a way to combine
the two. The code that paints over the wallpaper should be retained in order
to paint over whatever small traces of the wallpaper continue to get
painted.

> 4) I have noticed that even if I follow your original source code, the
> wallpaper paints when I call the original WM_PAINT at the end of the
> winproc function. It seems that the original code ignores that
> fErase==false and paints the background anyway. I am totally at a
> loss as to what to try next :(

I am (almost) totally at a loss too. You earlier reported that the code
almost worked. Now you seem to be saying that it doesn't work at all. I
suspect that what doesn't work is my original source code minus the
ValidateRgn call.

> I have tested this issue on slower machines (1.7GHz at work) and
> the flickering is a major problem (very noticable) even though 1/2 a
> second later, the repaint completes and there are no regions left on
> the screen with painting anamolies. Still, the flickering is a
> show-stopper for my product because a lot of people have machines
> under 2GHz and it is reasonable for them to expect this to work.

If it takes 1/2 a second for the repaint to complete, then, even without
flickering, this strikes me as very sluggish performance (I have a 400 MHz
computer myself), suggesting that a different approach may be needed.

> It's ticks me off to see how close I get to finishing this
> problem yet I never finish. I truly hope someone comes up with an
> idea to make this work. Let me know if you have any more ideas.
>
> Is there a way for me to perhaps detect the region containing the
> icons and limit the old WM_PAINT calls to only those regions and
> nowhere else? Flickering then only occur in the icons themselves, but
> ActiveDesktop experiences the exact same problem so I am ok with it.
> Any idea as to how to detect the icons + text region?

I still suspect that the problem with the code I supplied is one of lags. If
it was me, I would be looking to hack BeginPaint so that the erase flag
problem could be addressed directly without incurring a lag. I don't know
anything about hacking API calls, but (as I said) I would be starting my
researches at

Gili

unread,
May 25, 2004, 7:12:33 AM5/25/04
to

Ok, let me answer some of your questions:

1) I didn't know that BeginPaint() invokes WM_ERASEBKGND if you don't
ValidateRgn(). All I saw from experimentation is that if I called it, the
desktop wallpaper was being drawn and if I removed it it didn't seem to get
drawn. There still seems to be some sort of confusion because what I
reported in step 4 is that even if I ValidateRgn() then InvalidateRgn() with
fErase = FALSE, when we then call WM_PAINT, it still drew the wallpaper.
Given the original source code you posted, I was expecting to see 100%
"window tracing" as I dragged the window and no desktop leaking and there
was no expection that UpdateRgn locking would wear off at the 1st call of
BeginPaint. I will have to experiment to see if this is true like you
suggested.

2) I originally reported your original code worked, then not because it
turned out that depending on the size of the window I was dragging across
the desktop, I could or not could see wallpaper leaks. I find a maximized
console window dragged across the Desktop almost always shows wallpaper
leaks if they exist. Sorry for this misunderstanding.

3) 1/2 second to repaint the desktop: not sure why. I really didn't do
anything special in my code. I simply called Rectangle(hdc, 0, 0, 1000,
1000) within the overriden WM_ERASEBKGND inside the SysListView32 winProc
and it still leaked. To me, calling a single Rectangle() from WM_ERASEBKGND
is as minimimalist as I could go: if that is slow then I can forget about
building a more complicated app around it.

4) I'm not sure that lag is necessarily at fault. Even on slower machines
such as your own, you should be able to verify that ActiveDesktop does not
have this problem at all. Enable some app on ActiveDesktop, make it cover
most of the Desktop, then drag a maximized console window across it and you
shouldn't notice any leaks *except* under the icons but these are so minimal
I don't mind it. I think the key here is that ActiveDesktop "does it right"
and prevents the wallpaper being drawn in the first place whereby my code
still draws the wallpaper *then* draws on top of it. No matter how fast your
machine you will almost always see this flicker. It is simply bad code.

Assuming BeginPaint() releases the UpdateRgn lock like you mentioned,
how would we fix your original code so no wallpaper leaks occur? You mention
reducing the lag, but there must be an absolute better solution that isn't
time-dependant. ActiveDesktop seems to place pretty expensive rendering
windows inside it and yet this lag does not cause repaint problems for it.
Maybe the best solution that is lag-indenedant is what I suggested: only
invoke the original WM_PAINT on the icon regions and use my own WM_PAINT
everywhere else. This seems the safest solution to me, and looks to be what
ActiveDesktop is doing anyway; what do you think?

Gili

"John Carson" <donald...@datafast.net.au> wrote in message

news:un4GFJj...@TK2MSFTNGP10.phx.gbl...

Gili

unread,
May 25, 2004, 8:36:45 AM5/25/04
to

I took a quick look at the Detours project. It seems to allow API
redirection via function pointer table rewriting but the problem there (if I
understand it correctly) is that we'd like to rewrite BeginPaint()
specifically for SysListView32, not system-wide and Detours cannot do that.
Even if we could rewrite this function system-wide, I don't think we would
have enough information to know when to replace fErase with FALSE and when
to leave it alone. Correct me if I'm wrong on any of these points.

Thanks,
Gili

"John Carson" <donald...@datafast.net.au> wrote in message

news:un4GFJj...@TK2MSFTNGP10.phx.gbl...

Gili

unread,
May 25, 2004, 8:54:04 AM5/25/04
to

Further update, in my last post I was incorrect that there was no way to
use Detours to override BeginPaint() because I wouldn't know when to force
fErase==FALSE -- BeginPaint() takes hWnd as an argument hence I could always
force it only for SysListView32 and ignore it for other windows. The
problem, though, is that intercepting BeginPaint() isn't enough.
BeginPaint() takes in two arguments: a hWnd and a pointer to PAINTSTRUCT.
The second argument is an output that indicates the painting configuration
(includes fErase) but at this point (once BeginPaint has returned) it is
already too late because it has already invoked WM_ERASEBKGND internally.

Even if I call ValidateRgn() and InvalidateRgn() somehow within the
BeginPaint() intercept function, I'm not sure this would help the situation
in any way. If the real BeginPaint() unlocks the update region, then there
is still a chance of someone updating the region between me calling the real
BeginPaint() and InvalidateRgn().

Maybe you meant I should use Detours another way and I didn't
understand. Let me know.

Gili

"John Carson" <donald...@datafast.net.au> wrote in message

news:un4GFJj...@TK2MSFTNGP10.phx.gbl...

John Carson

unread,
May 25, 2004, 1:24:18 PM5/25/04
to
It appears that things are more complicated that I thought and, accordingly,
I have not correctly interpreted what I was seeing.

The BeginPaint documentation says:

"If the update region is marked for erasing, BeginPaint sends a
WM_ERASEBKGND message to the window."

Taking my cue from this, I added the ValidateRgn call before BeginPaint so
that the update region would not be marked for erasing. This seemed to
succeed in that the wallpaper was not drawn. Moreover, Spy++ confirms that
WM_ERASEBKGND messages are not sent.

However, with a vanilla application, I used the following code:

case WM_PAINT:
{
ValidateRect(hwnd, NULL);
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
EndPaint(hwnd, &ps);
return 0;
}

I expected that this would stop the WM_ERASEBKGND messages, but it doesn't.
Accordingly, the mechanism by which WM_ERASEBKGND messages and drawing in
general is stopped in the code that I supplied is more complicated than I
thought.

Further, I see when dragging a console window that, as you suggest, my
code doesn't work at all.

I agreed earlier when your wrote:

"If I overriden WM_PAINT to do nothing but return 0, my background draws
perfectly with no wallpaper leak; however no icons are drawn. This
demonstrates that the default WM_PAINT causes the wallpaper to draw."

I now see that this is untrue. Simply returning 0 from a WM_PAINT message
does NOT stop the wallpaper from being drawn. It only stops the icons from
being drawn (your code may overwrite the wallpaper fast enough for it not to
be seen, but it is very clearly seen if there is nothing to overwrite the
wallpaper).

To stop the painting of the wallpaper, it is necessary to somehow influence
the behaviour of the Desktop parent. How to do that effectively is a mystery
for which, alas, I have no answer.

I am sorry to have led you up some unrewarding paths.

Gili

unread,
May 25, 2004, 1:42:01 PM5/25/04
to

How did you come to the conclusion that Desktop painting is occuring
even if you override WM_PAINT in SysListView32 to do nothing but return 0? I
personally ran such code, dragged a maximized console window around very
fast and never saw a desktop leak. Did you? Maybe I didn't see it because my
home machine is fast? I'm curious as to how you deduced that Desktop
painting occurs beforehand. If this is the case I am pretty much screwed :)

Please run one final test on your 400MHz machine. Hit
http://bbs.darktech.org/DesktopBeautifier.cdf in a browser. It'll load up an
ActiveDesktop app on your machine (it does nothing but pop up a Java applet
that prints out the version of Java you have installed). Then, enlarge this
app so it covers most of your desktop. Next shake a maximized console across
your desktop very fast. In my personal experience, ActiveDesktop never
experiences wallpaper leak, whereas our code does. If they can do it, we
should be able to do it too! For me, the only visible leak ActiveDesktop has
is under the icons but nowhere else. Let me know what you get.

Gili

"John Carson" <donald...@datafast.net.au> wrote in message

news:O%23lpc0nQ...@TK2MSFTNGP11.phx.gbl...

Gili

unread,
May 25, 2004, 1:56:35 PM5/25/04
to
John,

I think I may have found a fix for your vanilla code (although I haven't


tested it on my end). In your code, you write:

case WM_PAINT:
{
ValidateRect(hwnd, NULL);
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
EndPaint(hwnd, &ps);
return 0;
}

Note that the documentation for ValidateRect() states that passing NULL
as the 2nd argument validates the entire *client* area. You need to validate
the *entire* window area (including the non-client area) in order to prevent
a WM_PAINT. Now, the odd thing is that I don't think that WM_ERASEBKGND
should ever get invoked if the client-area is empty and some non-client area
needs repainting ... but maybe it does for whatever crazy reason.

I would suggest modifying your vanilla code such that it Validates the
entire window, including non-client area and try again. I believe this will
block WM_EASEBKGND as you proposed.

Gili

"John Carson" <donald...@datafast.net.au> wrote in message

news:O%23lpc0nQ...@TK2MSFTNGP11.phx.gbl...

Gili

unread,
May 25, 2004, 3:17:38 PM5/25/04
to

You know. I don't make any sense :) I can't think up of any reason for
WM_ERASEBKGND to ever get invoked on a non-client region. Although, I am
still curious as to whether validating the non-client region somehow makes
WM_ERASEBKGND firing go away. As well, I cannot find any documentation
anywhere that guarantees that WM_ERASEBKGND is only fired from within
WM_PAINT (via BeginPaint or otherwise).

Although I should point out, WM_ERASEBKGND is not the problem. The
wallpaper is drawn within WM_PAINT, not within WM_ERASEBKGND. I know this
for certainty because I overriden WM_ERASEBKGND so the original code is not
executing and we still see the wallpaper being painted by WM_PAINT. So all
this goes to say is that you don't need to worry about WM_ERASEBKGND being
fired. You only need to worry about WM_PAINT being fired in a state where it
thinks it needs to clear the background. WM_PAINT then seems to invoke
WM_ERASEBKGND both on the root window (real wallpaper) and SysListView32
WM_ERASEBKGND. That is the root of the problem, not WM_ERASEBKGND on its
own.

I don't think calling BeginPaint() with background clearing enabled ever
causes wallpaper painting to occur simply because BeginPaint() is a general
function used by everyone and unless WM_PAINT explicitly tells the wallpaper
to paint itself it won't magically happen (in BeginPaint or otherwise). I
doubt Microsoft checks hWnd inside BeginPaint() and if its equal to
SysListView32 they tell the wallpaper to paint itself as well. At least from
exprimentation I beleive all the painting occurs in WM_PAINT.

Gili

"Gili" <sitti...@bbs.darktech.org> wrote in message

news:OCEa8goQ...@TK2MSFTNGP11.phx.gbl...

Gili

unread,
May 25, 2004, 6:11:51 PM5/25/04
to

Ok, a followup to my earlier posts. I have now established that stubbing
out SysListView32's WM_PAINT and simply returning 0 is not enough to prevent
the wallpaper from being painted (as you have reported earlier).
Furthermore, I confirmed that ActiveDesktop does not exibit this problem.
Even on slower machines if you drag a maximized console window over it, it
won't allow the wallpaper to leak through. Somehow, ActiveDesktop tells the
wallpaper not to paint itself.

Is there anyway to sniff on the root window message queue (doubtful
since you can't subclass it) or ActiveDesktop window as it becomes active
(Spy++ doesn't seem to do this job because it can only sniff on preexisting
windows, not ones that will be generated some time in the future)? I think
the key point here is to figure out what ActiveDesktop does and duplicate
it. It must be sending some sort of message to the Window management system
or the desktop itself and telling them not to paint the wallpaper underneath
it. I assume this "3rd party" must exist because normally the root window
isn't supposed to paint itself at all since windows are painted from top to
bottom.

Odd.. odd.. odd. Any ideas?

Gili


John Carson

unread,
May 26, 2004, 2:05:39 PM5/26/04
to
The desktop window will only paint if it thinks it is exposed, e.g, if you
have a maximised Outlook Express window and you drag a console window on top
of it, then the desktop does not repaint because it is completely hidden the
whole time by the Outlook Express window.

Accordingly, one solution is to create a window to overlay the desktop. For
the icons to display, you could leave holes in the window at the appropriate
places using SetWindowRgn. What makes this interesting is that it appears to
be exactly what ActiveDesktop does (at least under Windows 2000) --- this
would explain why the wallpaper can show through where the icons are.

Severian suggested something similar (without the holes). I think that it
may be the most promising line of inquiry.

Gili

unread,
May 27, 2004, 8:37:25 AM5/27/04
to
Hi,

My current plans are try out an overlay window. Severian's suggestion to
modify the z-order of my window so it appears under SysListView32 won't
work. I remember trying this in the past and for whatever reason you cannot
go under SysListView32. The API simply ignores your call.

Gili

"John Carson" <donald...@datafast.net.au> wrote in message

news:eFTM5w0Q...@TK2MSFTNGP11.phx.gbl...

Gili

unread,
May 27, 2004, 9:06:48 AM5/27/04
to

Actually maybe I am wrong.

There might be a way to set the z-order. My original interpretation of
SetWindowPos() is that you can use it to set any window underneath any other
window. My new interpretation is that you can only move sibling windows
around, but you cannot move a window under another window if the two have
different parent windows (correct me if I'm wrong). I guess I could try
SetParent() on my window and get it to show up under Progman. I will try
this out later on today.

The overlay method might be preferable for me because my app needs to
capture all mouse clicks, repaints on the desktop except on the icons. If I
place my window underneath Progman painting might work but then I'd still
not get any mouse clicks. I guess I haven't mentioned this requirement in a
while, sorry :)

I'll let you know how it goes later on today.

Gili

"Gili" <sitti...@bbs.darktech.org> wrote in message

news:eQXved%23QEH...@TK2MSFTNGP12.phx.gbl...

Gili

unread,
May 27, 2004, 1:38:16 PM5/27/04
to

Ok, I've just confirmed. As far as I can tell, it is impossible to move
yourself underneath Progman. The operation returns success but you get
places immediately over SysListView32. Either the function simply ignores
the command or when Progman/SysListView32 notice another window have moved
underneath them they move themselves back over it. Either way, this is not a
workable solution (I don't think I can move under them without them finding
out).

I will try the overlay window approach.

Gili

"Gili" <sitti...@bbs.darktech.org> wrote in message

news:ufHa3t%23QEH...@TK2MSFTNGP10.phx.gbl...

0 new messages