Holy cow, I wrote a book!
A question arrived from a customer
(with the rather unhelpful subject line Question for Microsoft)
wondering why, when they call GetSystemMenu
and then ask for the states of the various menu items like
the menu item states don't reflect reality.
The menu item states don't synchronize with reality until
the user actually opens the system menu.
There is no requirement that
applications keep menu item states continuously in sync.
After all, that's why we have messages like WM_INITMENU:
To tell the application,
"Whoa, we're about to show this menu, so you might want to comb its hair
and pick the food out of its teeth so it can be seen by the user."
Lazy evaluation is common, because
maintaining states continuously can be expensive,
and there's no point constantly turning items on and of and on and off
if the user can't see them anyway.
If you want to know whether the SC_MINIMIZE
menu item would be enabled if the menu were shown,
you can check the window styles:
A window can be minimized if it has a WS_MINIMIZEBOX
and is not already WS_MINIMIZEd.
Similar logic can be applied to the other menu items.
Well, except for SC_CLOSE.
While in most cases the window caption determines what is enabled
on the menu, the Close button works backward:
It is the state of the menu item that controls whether the Close
button is enabled.
So in the special case of SC_CLOSE,
you can query the state at any time,
because for that case, the menu controls the state rather than
simply reflecting it.
Why is SC_CLOSE so special?
Here come da history.
The Close button was added in Windows 95.
Since versions of Windows prior to Windows 95
didn't have a Close button, they didn't need a style to specify
whether the Close button should be enabled or not.
(You don't need a style to control something that doesn't exist.)
Windows 95 added the Close button and hooked it up to the only
thing that it had available, namely, the SC_CLOSE item
on the system menu.
Sure, Windows 95 could have have invented a new window style,
but since SC_CLOSE already existed and applications
were already using it, using SC_CLOSE to control
the Close button allowed old applications to reap the benefits
of the new Close button automatically.
It also meant that there was one less thing you had to change when porting
your program to Windows 95.
You can now answer
Alex Cohn's question:
I wonder if the EnableMenuItem method will work
for minimize and maximize, too.
After all, these buttons also have siblings in the Alt-space menu.
A colleague of mine was having trouble getting the
\b metacharacter in a regular expression to work.
Of course, when somebody asks a question like that,
you first have to establish what their definition of "work" is.
Fortunately, he provided some examples:
Regex.IsMatch("foo", @"\b" + @"foo" + @"\b")
Regex.IsMatch("%1" , @"\b" + @"%1" + @"\b")
Regex.IsMatch("%1" , @"\b" + @"\%1" + @"\b")
Regex.IsMatch("%1" , @"..")
Regex.IsMatch("%1" , @"%1")
"The last two entries are just sanity checks to make sure I didn't
make some stupid mistake like passing the parameters in the wrong order.
I want to search for a string that contains %1 with word
boundaries on either side, something I would normally use \b for.
Is there something special about the % character?
Notice that the match succeeds when I look for the word foo."
Everything is working as it should.
Recall that the \b metacharacter matches when there is a
\w on one side and a \W on the other,
where the beginning and end of the string are treated as if they
The string %1 therefore breaks down as
My colleague responded,
I keep forgetting that % won't act like a \w just because I
want it to."
A customer reported a problem with their shell extension:
We want to format a floating point number according to the user's
We do this by calling snprintf to convert the value
from floating point to text with a period (U+002E) as the decimal separator,
GetNumberFormat to apply the user's preferred
decimal separator, etc.
We found, however, that if the user is running in (say) German,
we find that sometimes (but not always)
the snprintf function follows the German locale
and uses a comma (U+002C) as the decimal separator
with no thousands separator.
This format prevents the GetNumberFormat
function from working,
since it requires the decimal separator to be U+002E.
What is the recommended way of formatting a floating point number
according to the user's locale?
The recommended way of formatting a floating point number
according to the user's locale
is indeed to use a function
like snprintf to convert it to text with U+002E as
the decimal separator (and other criteria),
GetNumberFormat to apply the user's locale preferences.
The snprintf function follows the C/C++ runtime locale to determine
how the floating point number should be converted,
and the default C runtime locale is the so-called "C" locale
which indeed uses U+002E as the decimal separator.
Since you're getting U+002C as the decimal separator,
somebody must have called setlocale to change the
locale from "C" to a German locale, most likely
by passing "" as the locale,
which means "follow the locale of the environment."
Our shell extension is running in Explorer.
Under what conditions will Explorer call
What should we do if the locale is not "C"?
As it happens, Explorer never calls setlocale.
It leaves the locale set to the default value of "C".
Therefore, the call to snprintf should have generated
a string with U+002E as the decimal separator.
Determining who was calling setlocale was tricky
since the problem was intermittent, but after a lot of work,
we found the culprit:
some other shell extension loaded before the customer's shell extension
decided to change the carpet
by calling setlocale(LC_ALL, "") in its
presumably so that its calls to snprintf would
follow the environment locale.
What made catching the miscreant more difficult was that the
rogue shell extension didn't restore the locale when it was unloaded
(not that that would have been the correct thing to do either),
so by the time the bad locale was detected,
the culprit was long gone!
That other DLL
used a global setting to solve a local problem.
Given the problem "How do I get my calls to snprintf
to use the German locale settings?"
they decided to change all calls to snprintf
to use the German locale settings,
even the calls that didn't originate from the DLL itself.
What if the program hosting the shell extension had done
a setlocale(LC_ALL, "French")?
Tough noogies; the rogue DLL just screwed up the host program,
which wants to use French locale settings
but is now being forced to use German ones.
The program probably won't notice that somebody
secretly replaced its coffee with Folgers Crystals.
It'll be a client who notices that the results are not formatted
The developers of the host program, of course, won't be able to
reproduce the problem in their labs, since they don't have the
rogue shell extension, and the problem will be classified as "unsolved."
What both the rogue shell extension and the original customer's
shell extension should be using is the _l variety
of string formatting functions (in this case
although _snprintf_s_l is probably better).
The _l variety lets you pass an explicit locale
which will be used to format that particular string.
(You create one of these _locale_t objects
by calling _create_locale with the same parameters
you would have passed to setlocale.)
Using the _l technique solves two problems:
If either the customer's DLL or the rogue DLL had followed
this principle of not using a global setting to solve a local problem,
the conflict would not have arisen.
Since my original article explaining
how to get Windows Media Player to single-step a video,
I've learned that there's an even easier way.
Backward-stepping is dependent upon the codec;
some of them will go backward to the previous keyframe.
The person who tipped me off to this feature:
The developer who implemented it.
Sharing a tip does not imply that I approve of the situation
that led to the need for the tip in the first place.
Everybody knows about the Microspeak term dogfood.
It refers to
the practice of taking the product you are working on
and using it in production.¹
For the Windows team, it means installing a recent build
of Windows on your own computer as well as onto
a heavily-used file server.
For the Office team, it means using a recent build of Office
for all your documents.
For the Exchange team, it means moving the entire product team
to a server running a recent build of Exchange.
You get the idea.
Purists would restrict the use of the word dogfood
to refer to a product group using its own product,
but in practice
the meaning has been generalized
using a prerelease product in a production environment.
The Windows team frequently dogfoods recent builds of Office
and Exchange Server.
Actually, the Exchange Server case is one of double-dogfood,²
for not only is the server running a prerelease version of Exchange Server,
it's doing so atop a prerelease version of Windows!
Dogfooding does have its costs.
For example, the prerelease version of Exchange Server might uncover a bug
in the prerelease version of Windows.
While the problem is investigated, the Windows division can't
These outages are comparatively rare,
although they are quite frustrating when they occur.
But you have to understand that the whole purpose of dogfooding is
to find exactly these sorts of problems so that our customers won't!
¹ Despite the efforts of our CIO, the term
ice-creaming has not caught on.
² I made up the term "double-dogfood" just now.
It is not part of Microspeak.
Random832 seemed horrified by the fact that it is possible to
run multiple monitors, with different color formats on each monitor.
"Seriously, why does it let you do that?"
Well, of course you can do that. Why shouldn't it let you do that?
When multiple monitors were introduced to Windows,
video hardware was nowhere near as advanced as it is today.
One common discovery was that your computer, which came with
a video card in one of the expansion slots,
actually had a video chip baked into the motherboard,
but which was disabled in the BIOS.
In other words, your computer was actually multiple-monitor-capable;
it's just that the capability was disabled.
Once you got it enabled,
you would discover that the onboard video adapter
was not as good as the one in the expansion slot.
If it were as good as the one in the expansion slot,
then the manufacturer would have saved a lot of money and
not bothered shipping a video card in the expansion slot!)
Usually, the onboard video card didn't have a lot of video RAM.
You still want to run it at 1024×768 (hey, that's high
resolution for these days), but in order to do that,
you need to reduce the color depth.
On the other hand, the card in the expansion slot has a huge
amount of video RAM (four megabytes!),
so you take advantage of it by running at a higher color depth.
You're now getting the most out of your machine;
each video card is cranked up as high as it can go.
(The lame-o card doesn't go very high, of course.)
What could possibly be wrong with that?
It so happened that some of these "secret video card" motherboards
had a feature where they would disable the ROM BIOS on the on-board
video card if they detected a plug-in video card.
To get multi-monitor support on these recalcitrant machines,
one of my colleagues wrote a tool that you used like this:
It was a bit of a hassle, but it let you squeak crappy multi-monitor support
out of these lame-o motherboards.