Holy cow, I wrote a book!
Public Service Announcement:
Daylight Saving Time ends in most parts of the United States this weekend.
Andy points out that
if you attempt to synchronize your clock
when the date is set incorrectly, the operation fails
with the error message
"An error occurred while Windows was synchronizing with time.windows.com.
For security reasons, Windows cannot synchronize with the server
because your date does not match. Please fix the date and try again."
what the security risk is.
First of all, for people who are trying to solve the problem,
the solution is to
follow the steps in the error message.
Set your date to the correct date, then try again.
If that doesn't help, also set the time to something close
to the correct time.
Once your time gets close,
the time server can nudge it the rest of the way.
Back to the original question:
What is the security risk being defended against, here?
At first glance, you might think that the server is attempting to defend
itself against a client whose time is set incorrectly,
but actually the potential attack is in reverse:
Your computer is protecting itself against a rogue time server.
The Kerberos authentication protocol relies heavily on all participants
agreeing on what time it is (with some slop tolerance).
If somebody manages to fool the client into synchronizing its time
against a rogue server (for example, by using a DNS poisoning attack),
the attacker can use that invalid date (typically a backdate)
as a foothold for the next level of attacks.
The default configuration for the Windows Time service is to reject
attempts to change the clock on domain-joined machines by more than
You can change the configuration settings by following the
this KB article
(which happens also to have been the source material
for most of this article).
First, the story as told by others:
Now the question you're all going to ask so I may as well answer it:
Why is this information kept in a desktop.ini file instead
of being attached to the file itself (say, in an alternate stream)?
If it were in an alternate stream, then it would track the file when
it was moved or copied.
First answer: Because alternate streams require NTFS.
Localized names were introduced in Windows 2000,
and Windows 2000 gave you the option of formatting your
drive as FAT or NTFS.
(It wasn't until Windows Vista that NTFS became mandatory.)
Therefore the mechanism for localized names needed to work
when your drive was formatted as FAT.
Okay, fine, maybe you tell people,
"Sorry, this feature requires NTFS."
All those multinational corporations
who use FAT-formatted drives in the year 2000 are screwed.
Well, placing the information in an alternate data stream
means that each file would have to be accessed
just to get its name.
it's more efficient when you buy in bulk.
Consider a directory with 500 files.
A simple directory listing like one provided by
takes, say, seven I/O requests
(open directory, five "give me the next 100 files", close directory).
If each file had to be opened in order to probe for an alternate
stream, you now have 507 I/O requests:
open directory, five "give me the next 100 files", close directory;
and then 500 "open alternate stream on this file" calls that fail.
And that was the optimistic case where the localized name doesn't
For the pessimistic case where every file has a localized name,
the I/O count balloons to 1507:
open directory, five "give me the next 100 files", close directory;
and then 500 × (open alternate stream, read localized name,
close alternate stream).
You increased the number of I/O requests by a factor of over 200.
And when you are looking at files on a slow network
(hello, multinational company with servers all over the world),
that factor can be deadly.
Suppose the ping time to the server is 500ms.
The cmd.exe program gets you the directory listing in
three and a half seconds.
The alternate data stream localized name version takes
On the other hand, if all the localized names are placed in a single
file, then the I/O count is only 10:
open directory, five "give me the next 100 files", close directory;
open desktop.ini, read contents, close desktop.ini.
We're down to five seconds.
Not as good as three and a half seconds, but way better than twelve minutes.
And if, in the common case, the directory contains no localized names, the
open desktop.ini step fails, and we save two I/O's,
bringing our time down to four seconds.
And then you can be clever and say,
"Wait a second, I already have the directory listing;
I can just look at the results to see if desktop.ini
is on the list.
If not, then I don't even have to bother trying to open it!"
Now you are back to three and a half seconds.
Okay, maybe you tell people,
"Sorry, this feature requires a high-speed network."
All those multinational corporations with servers around the world
The theoretically highest-speed network connection between New York
and Tokyo has a 72ms ping time because that's how fast it takes
light itself to travel that distance and back.
Your 500-file directory takes nearly two minutes to display.
Seeing as multinational corporations were the initial target audience
for the MUI feature
(since they're the ones who are most likely to have users with
different language preferences),
designing your feature so that your target audience can't use it
seems like a pretty bad execution plan.
A file's name is traditionally considered metadata,
and traditionally, metadata is visible without requiring access to the file.
A file can show up in a directory listing even though you don't have
access to it.
But if the file's localized name is stored in the file itself,
you now have the situation where you don't even know the name of the file
because you can't access it.
This is even worse than
I'm sorry, you don't have permission to know where this shortcut
file should show up in the Explorer window.
This would be I'm sorry, you don't have permission to know the name
of the file in this directory listing.
Remember that reading from an alternate data stream
triggers a recall
if the file had been archived to tape.
You don't want to have to restore an entire directory from tape
just because somebody opened the folder in Explorer.
And finally, alternate data streams are very fragile.
Any tool that processes a file has a decent chance of inadvertently
destroying the alternate data streams.
(And good luck if your backup program doesn't preserve the alternate
If you search MSDN for CtlPanelClass,
you'll find a few really old Knowledge Base articles
that include it in a list of "class names of common
I'm not sure why the Knowledge Base articles bothered to list those
there is no technical reason for applications to need to know this,
and including the information merely encourages programmers to
rely on the window class name in strange undocumented ways.
(This is another one of those cases where
a Knowledge Base article written to assist in troubleshooting
becomes interpreted as formal documentation.)
Windows Vista finally got rid of that window class.
It took only ten years.
The window class was used by the old Windows 3.1 Control Panel
Back in Windows 3.1, the Control Panel was a standalone
application and was not integrated into the shell namespace
(in large part
because there was no such thing as a shell namespace back then
for it to be a part of).
There was one program which not only searched for a window of that
specific class name,
but it also sent the window WM_COMMAND messages,
since of course it knew what the menu IDs were for the Control Panel
and it knew that the Windows developers would never change the IDs
or replace the standalone Control Panel application with anything else.
When the standalone Control Panel application was converted to
a virtual folder,
it also came with
in order to maintain compatibility with older programs that
relied on the old behavior in strange undocumented ways.
One of those decoys, the CtlPanelClass
was removed for Windows Vista.
A crash was traced back to a bug in the decoy window
which manifested itself if
a control panel took more than ten seconds to launch itself.
To fix the bug, we just removed the decoy window,
but we were prepared to write a compatibility shim in case
there were people still running that ancient application
We didn't actually turn the compatibility shim on,
but we were ready just in case.
We removed the decoy and crossed our fingers.
We got lucky.
Today is Election Day in the United States.
Some years ago, seventh grade students (age 12)
were asked to imagine they had just been elected president
and to give an address to the nation on one thing they would change.
Remember, these are only the most entertaining ideas.
Do not assume all student ideas are like these.
And this sentence came from a student destined for greatness as
"Something must be done, and I will make it happen."
A customer wanted to do one of those user-hostile things that
Windows doesn't make easy to do (the sort of thing I tend to call out
on this Web site).
After receiving an explanation that Windows doesn't provide a way
of doing what they want because it abuses the component in question
and goes against user expectations, the customer countered,
"Yes, we understand that, but our case is different."
The customer then proceeded to explain how they intended to use
this newfound power (if only they could figure out how to do it)
and under what circumstances they intend to invoke it.
Their explanation was interesting in that the description
could be applied to
any other program on the planet.
Yes, we understand that, but our case is different.
We would do this only after the user installs the program
or reconfigures it from the Add or Remove Programs control panel.
After a few days, we would stop doing it, unless triggered by
a reinstall or a reconfiguration.
So far, there's nothing here that explains why your program should be
able to do this, but not, say, Photoshop.
There is no evidence that
this program is any different from the tens of thousands of other programs
out there, many of which probably want to do that very same thing this program
wants to do.
Just because you say that your case is different doesn't make it so.
Commenter littleguru asks,
"Why does the End Now button not kill the process immediately?"
When you click the End Now button,
the process really does end now,
but not before a brief message from our sponsor.
When you kill a hung application,
Windows Error Reporting steps in to record the state of the
hung application so it can be submitted to the mother ship
(with your permission).
If you are running Windows XP or Windows Vista,
you can briefly see a process called dumprep.exe
or WerFault.exe; these are the guys who are doing
the data collection.
After being uploaded to Microsoft,
these failure reports are studied
to determine why the application stopped responding and what
could be done to fix it.
I've been asked to do quite a few of these analyses myself,
and sometimes it's something pretty mundane
(an application sends a cross-thread message while holding a
critical section, and the thread can't receive the message
because it's stuck waiting for the critical section that the
sender is holding—classic deadlock),
and sometimes it's something pretty weird
(application has a bug if
the number of sound output devices is not equal to one).
Whatever the reason, I write up my analysis,
and the people who are in charge of such things make arrangements
for the information to be sent back
to the vendors who wrote the application
(assuming the vendors are
registered with Winqual).
If you don't want Windows Error Reporting to collect application
crash and hang reports,
you can disable it from the Group Policy Editor under
Windows Error Reporting.
Of course, if you do this, then
you don't get to vote on which
program crashes and failures Microsoft should work on fixing.
This entry is an experiment:
I mentioned Windows Error Reporting and WHQL.
If people complain about digital certificate authorities,
that'll just confirm my bias against returning to those old
Experimental results obtained.
No more stories involving Windows Error Reporting and WHQL.
Bill Littlefield of NPR's sports program
Only a Game
interviews Susan Warren about competitive pumpkin-growing.
[Direct link - Real format]
An excerpt from her book
was printed in
The Wall Street Journal:
The Race to Break the Squash Barrier,
the quest to grow a one-ton pumpkin.
I'm fascinated by these subcultures of people obsessed with one thing.
Understanding what __purecall means.
I was asked to help diagnose an issue in which a program managed to stumble
into the __purecall function.
00a14509 a100000000 mov eax,dword ptr ds:[00000000h] ds:0023:00000000=????????
The stack at the point of failure looked like this:
The line at XYZ!CViewFrame::SetFrame that
called the mystic __purecall was a simple AddRef:
pSomething->AddRef(); // crashes in __purecall
From what we know of __purecall,
this means that somebody called into a virtual method on a derived
class after the derived class's destructor has run.
Okay, well, let's see if we can find the object in question.
Since the method being called is a COM method,
the __stdcall calling convention applies,
which means that the this pointer is on the stack.
0:023> dd esp+4 l1
Using our knowledge of
the layout of a COM object,
we can navigate through memory to find the vtable.
0:023> dps 06a88d58
06a88d58 009b2eac XYZ!CRegistrationSink::`vftable'
06a88d64 00998930 XYZ!CObjectWithBrush::`vftable'
06a88d6c 009c9c80 XYZ!CBrowseSite::`vftable'
06a88d70 009c9c70 XYZ!CBrowseSite::`vftable'
0:023> dps 009b2eac
009b2eac 00a14509 XYZ!_purecall // virtual QueryInterface() = 0
009b2eb0 00a14509 XYZ!_purecall // virtual AddRef() = 0
009b2eb4 00a14509 XYZ!_purecall // virtual Release() = 0
009b2eb8 009cb1e4 XYZ!CRegistrationSink::Register
009b2ebc 009b3d2d XYZ!CRegistrationSink::Unregister
We see that the object has been destructed down to the
CRegistrationSink base class,
and the attempt to increment its reference count has led us
into the abyss of __purecall.
But what was this object before it descended into madness?
Well, we know that the object was something derived from
And the other values in memory tell us that the object most
likely also derived from
Just for fun, here's the CObjectWithBrush vtable,
to confirm that we destructed down to that point:
00998930 00a14509 XYZ!_purecall // virtual QueryInterface() = 0
00998934 00a14509 XYZ!_purecall // virtual AddRef() = 0
00998938 00a14509 XYZ!_purecall // virtual Release() = 0
0099893c 0099880d XYZ!CObjectWithBrush::SetBrush
00998940 00a319ee XYZ!CObjectWithBrush::GetBrush
00998944 00a13fd9 XYZ!CObjectWithBrush::`scalar deleting destructor'
Ooh, it looks like CObjectWithBrush has a
Probably to destroy the brush.
A check of the source code tells us that nobody derives from
CBrowseSite, so that is almost certainly the
original object type.
As a cross-check, we check whether what we have matches
the memory layout of a CBrowseSite:
0:023> dt XYZ!CBrowseSite 06a88d58
+0x000 __VFN_table : 0x009b2eac
+0x004 m_prgreg : 0x06a88d58 Registration
+0x008 m_creg : 2
+0x00c __VFN_table : 0x00998930
+0x010 m_hbr : (null)
+0x014 __VFN_table : 0x009c9c80
+0x018 __VFN_table : 0x009c9c70
+0x01c m_cRef : 0
Looks not unreasonable.
(Well, aside from the fact that we have a bug...)
The object has most likely begun its destruction because its
reference count (_cRef) went to zero.
At this point, there was enough information to ask the developers
CViewFrame and CBrowseSite to work out
how the CViewFrame ended up running around with a pointer
to an object that has already been destructed.
work with owner-data listviews,
you take the responsibility for managing the data associated with each
item in the list view.
The list view control itself only knows how many items there are;
when it needs information about an item, it asks you for it.
It's the fancy name for a "virtual list view" control.
When you use an ownerdata list view,
you will receive a new notification,
The OD stands for ownerdata,
so this is an "owner data state changed" notification.
The list view sends this notification when the state of one or more
items in an owner data list view control change simultaneously.
Mind you, the list view control can also send the
if the state of an item changes,
so you need to be on the lookout for both.
If there is a notification LVN_ITEMCHANGED,
then what's the purpose of
the LVN_ODSTATECHANGED message?
It's redundant, after all.
Well yes, it's redundant, but it's faster, too.
The LVN_ODSTATECHANGED notification tells you
that the state of all items in the specified range has changed.
It's a shorthand for sending an individual LVN_ITEMCHANGED
for all items in the range [iFrom..iTo].
If you have an ownerdata list view with 500,000 items
and somebody does a select-all,
you'll be glad that you get a single
notification with iFrom=0 and
instead of a half million individual little LVN_ITEMCHANGED
A customer wanted to know if there was a way for their
application to invoke the
Aero Peek feature
so that their window appeared and all the other windows
on the system turned transparent.
No, there is no such programmatic interface exposed.
Aero Peek is a feature for the user to invoke,
not a feature for applications to invoke so they can
draw attention to themselves.
Yes, I realize you wrote a program so awesome that
all other programs pale in comparison,
and that part of your mission is to make all the other programs
literally pale in comparison to your program.
Maybe you can meet up with
that other program that is the
most awesome program in the history of the universe
and share your sorrows over a beer.