Now that we know how LockWindowUpdate
works, we can look at what it is for.
Actually, the intended purpose of LockWindowUpdate
can be captured in one word: dragging. But we'll get to that a little later.
The purpose of LockWindowUpdate
is to allow a program to temporarily take over the responsibility of drawing a window. Of course, in order to do this, you have to prevent the window procedure (or anybody else) from doing their normal drawing activities; otherwise, the two pieces of code (the code that normally draws the window and the code that's trying to take over) fight for control of the window and you get an ugly mess since neither knows what the other is doing.
But if you've locked the window for updating, how do you draw to it? You use the DCX_LOCKWINDOWUPDATE
flag to the GetDCEx
function. This flag means, "Let me draw to the window even though it is locked for update." Only the code that locked the window for update should pass this flag, of course, since it would otherwise create the conflict that LockWindowUpdate
was intended to avoid in the first place.
Since people like tables so much, I've made one that summarizes what changes when a window is locked for update:
|
Normal behavior |
Window locked for update |
BeginPaint , GetDC , etc. |
Drawing operations paint the window |
Drawing operations paint nothing, but the affected area is remembered for future invalidation |
GetDCEx with DCX_LOCKWINDOWUPDATE flag |
(do not use) |
Drawing operations paint the window |
In other words, when a window is locked for update, the ability to draw to the window is taken away from the normal DC-acquisition functions (BeginPaint
and friends) and given to GetDC(DCX_LOCKWINDOWUPDATE)
. Note that if no window is locked for update, you should not pass the DCX_LOCKWINDOWUPDATE
flag; the purpose of that flag is to indicate "I'm the guy who called LockWindowUpdate
. Let me in!"
It's sort of the window manager equivalent of the old comedy routine where you tell the guard, "Nobody is allowed into this room." And then you come back an hour later and the guard won't let you in.
"I'm sorry, sir, but I'm not allowed to let anyone into this room."
"But I'm the one who told you not to let anyone into the room."
"That's right sir, and I'm following your orders. Nobody is allowed into the room."
The mistake was in the initial order to the guard. You should have said, "Nobody except me is allowed into this room." And DCX_LOCKWINDOWUPDATE
is how you tell the window manager, "It's me. Let me in."
If you go back and look at the way the LockWindowUpdate
function works, you'll see that if the window that was locked doesn't try to draw, then when the window is unlocked, nothing is invalidated. Whereas the CS_SAVEBITS
class style automatically saves the original pixels and restores them when the window is removed from the screen, the LockWindowUpdate
doesn't do any such thing. It is your responsibility to ensure that any pixels you changed while the window was locked for update have been restored to their original values when you call LockWindowUpdate(NULL)
. This is typically done by saving the original pixels into an off-screen bitmap before you do your custom painting and putting them back when you're done.
Okay, so here's the intended usage pattern:
- When you want to take over drawing from another window, call
LockWindowUpdate
on that window.
- Save the pixels from that window that you're going to overwrite.
- Draw your new pixels. (These pixels are often a modification of the original pixels. For example, you might add the image of an object that is being dragged over that window.)
- Repeat as necessary as long as your operation is still in progress. (Doing so may require you to "back up" more pixels of the screen if the region of the screen you're modifying is different from the region you modified originally. You can do this backup/restore incrementally. For example, instead of accumulating the set of pixels you need to restore, you can restore all the original pixels, compute the new position of the drag image, save those new pixels, and draw the new image. That way, you have only one set of "backup pixels" to deal with.)
- When the operation is complete, restore the original pixels and call
LockWindowUpdate(NULL)
.
Next time, we'll look more at that one word "dragging" and how it is closely tied to the whole concept of LockWindowUpdate
.
Even though we've only started talking about LockWindowUpdate
, you should already know enough to answer this question.
(Note: The purpose of this series is to describe the way the LockWindowUpdate
was intended to be used, not to discuss whether it was a good design in the first place.)
"Make sure the prince doesn’t leave this room until I come and get him."
"Not to leave the room, even if you come and get him."
"No, no, until. Until I come and get him."
"Until you come and get him, we’re not to enter the room."
"No, no, no. You stay in the room, and make sure he doesn’t leave."
…
:-P
"Yesterday, I could left click the corner of a rectangle,
move the corner, and the rectangle would resize as the corner
was moved, until the left mouse button was released. "
…then…
"Today the rectangle doesn’t change size until I release
the left mouse button. It then snaps into position as I
want."
I wonder what crazy stuff will happen to him tomorrow?
It is interesting that you say LockWindowUpdate was intended for dragging. A while ago I wrote a .NET regular expression testing app (http://luke.breuer.com/images/regextest.png); it will execute the regex you are forming every time the regex changes or the text to be searched changes. Due to the Win32 RichTextBox’s (RTB) slowness, I call LockWindowUpdate on the lower RTB when matching. I discovered that if the amount of text in the lower (locked) RTB exceeds its visible area, every time it is updated it flickers, as well as several other windows on my multiple screens. Outlook 2007’s e-mail listview and toolbars flicker, quite a bit of Visual Studio flickers, as do the groupbox labels in Process Explorer’s System Information window. Neither Firefox nor Foobar2000 flicker.
I’ve noticed this flickering in other circumstances, like minimizing and then maximizing Visual Studio. Might this all have to do with LockWindowUpdate being used incorrectly? Is there much hope for a) figuring out what is going on and b) fixing it? (I am using XP SP2.)
SDK documentation often doesn’t state the intended purpose of various things, leaving the users to guess. This is a big problem.
So the answer to my question from yesterday is that "dragging" is the semaphore that synchronizes between different apps.
I wonder how this woud work with MultiPoint (http://www.microsoft.com/presspass/features/2006/dec06/12-14MultiPoint.mspx)
Few questions about LockWindowUpdate:
1) Does it affect the non-client area? The documentation makes no mention of it.
2) The docs say "A locked window cannot be moved.". Does that mean I’m not allowed to move it, or that the move will have no effect, or that it will be deferred until I unlock it?
I also tried to use LockWindowUpdate when adding a lot of items to a ListView. Nevermind if this was wise or not. The point is that the whole desktop and windows on it were flickering like crazy. I quickly learned not to use that function.
Sorry Raymond, but some type of comment in the documentation as to the intended use of the function is definintely called for here. What is the purpose of documentation? Should you intentionally hide information just because it doesnt fit in correctly with your world view?
Just the number of sheer developers having problems with this function should make that obvious to you, unless you are just plain stubborn.
Is there a chance you will demonstrate the proper use of LockWindowUpdate as described above in a modification to your scratch sample during this series on LWU or will you leave this as an exercise to your readers?
Well, it seems clear that many people actually think you WRITE the documentation…
There is a better description of LockWindowUpdate, if you start at the documentation page for LockWindowUpdate, click on "Painting and Drawing Overview" then "About Painting and Drawing" then "Window Update Lock".
I’ll be the first to admit, however, that this extra description is a) really tough to find (unless you already know what you’re looking for) and b) rather cryptic.
Mind you, Raymond’s post from yesterday is already the #3 result when you search for LockWindowUpdate on google…
Actualy its number #2 now.
Hey. Who let that nitpicker into a blog whose owner scolds nitpickers?
Hey! Who let that nitpicker into Win32?
Metanitpicker: Um, what are you talking about? Are you trying to say that computers should talk "casual English" (like a blog), rather than assembly language (like they currently do)?
A place I used to work used the LockWindowsUpdate with a semaphore and VB6. It was to prevent the screen from drawing while we did lots of work behind the scenes.
It wasn’t pretty, but it worked pretty well.
I really love seeing these posts from Raymond (and other MS blogs too) with all sorts of cool stuff about how windows does what it does (and why it does it that way). All the cool stuff you just don’t see in MSDN :)
Now all we need is for someone from the DirectX team to start blogging about cool stuff to do with that part of windows :P
<<Due to the Win32 RichTextBox’s (RTB) slowness, I call LockWindowUpdate on the lower RTB when matching.>>
In order to temporary "freeze" the RTF you must get the IRichEditOle object (using EM_GETOLEINTERFACE).
IRichEditOle also supports the ITextDocument interface. On that one you call Freeze and Unfreeze.
<blockquote>Few questions about LockWindowUpdate:
1) Does it affect the non-client area? The documentation makes no mention of it.</blockquote>
Ivo, that’s easy enough to answer: start with Raymond’s scratch application, and test it yourself. Lock the window and attempt to draw on the nonclient area.