by Michael S. Kaplan, published on 2010/03/19 07:01 -04:00, original URI: http://blogs.msdn.com/b/michkap/archive/2010/03/19/9980203.aspx
I get a lot of email.
Like at least 400 and sometimes as many as 1500 pieces of email a day, not counting spam (if I accept the premise that email is communication then I won't consider spam to be something I will call email).
Th organization system of rules that files stuff is staggeringly complex and allows me to scan through mail I need to see very quickly, and to triage items as they come in.
Not sleeping very much helps too. :-)
Sometimes people send me mail to report issues or ask questions about a problem they are having.
I will wade in and look into the problem and help them if I can or point them in a more hopeful direction if I can't.
Like the other day when someone reported a problem that didn't seem like his bug or even a bug in the platform he was using (Delphi), but on the OS underneath it (Windows, or Windows 7 to be precise).
I looked into it and sure enough his analysis was spot on.
But he didn't have the backstory, so I thought about it and figured it would be worth a blog to talk about the problem he is seeing. Maybe it will help someone else....
So there was this bug.
Wait, I should start at the beginning.
Like, before the bug.
Sound good?
You know how there has been this bug push that started in Vista and has continued unabated since that time to move people off of LCIDs and into Locale Names?
Like some of what is behind blogs I have written like Your LCID sucks?
Well under the covers, one of the big changes was to have the name stored in the registry and not just the LCID, right here:
Now obviously you should not depend on this LocaleName any more than you used to depend on Locale. Which some people did occasionally, even though this wasn't documented or supported, even though there have been documented and supported ways to do this since the earliest days -- functions like GetUserDefaultLCID.
Anyway, this new value was added, this LocaleName. And all of the code inside was changed to be name based, so that none of the NLS code was depending on the LCID anymore.
Everyone knew that some people were still reading that darn Locale registry value, so the value was not removed. But the Locale value was only there and being kept in sync with the LocaleName value for backward compatibility for these not entirely well-behaved apps that really were doing something that has been wrong since the earliest days.
All well and good, everyone can be happy, right?
Well, not entirely.
Like I said, there was this bug.
It turned out that in Windows 7, in some RUNAS/Impersonation scenarios, where one runs as another user, loads their HKCU registry, and does some work (as in the Impersonation and NLS blog I wrote back in 2005) to change the user locale, that the work to keep the LocaleName and Locale registry values in sync was not happening.
So you get something different; something like this:
(00000809 is the en-GB locale.)
Maybe slightly different than this case, but the two values would definitely not be in sync.
So in this one obscure case, the effort to support this unsupported scenario was not, in fact, being supported.
A few early cases that were reported, I myself gave advice that they update the application, because even if/when this was fixed it wouldn't change the fact that what they were doing was wrong -- so their fix should happen and it should be independent of the Windows fix.
Now soon after that, as later cases proved, it turns out that the impersonation scenario was not entirely 100% obscure, as many OEMs using tools to image their machines with specific locales were using impersonation this way.
But there wasn't such a good reason to bend over backward to make this all work, since no supported code in Windows was using the Locale registry value. It was still these outlier cases.
There were some complaints, but the customers who were complaining would readily admit that they were doing the wrong thing.
So it seemed like the case was kind of closed....
Or maybe not.
Because although all the NLS and Regional and Language Options code was no longer using HKCU\Control Panel\International,Locale in any of its initialization routines, that there was at least one bit of supported code that was still [indirectly] using this registry value, at the time of session initialization for the user.
That one function is GetThreadLocale (the one I mention in blogs like Why I think the thread locale really stinks).
At session initialization (as in at logon) for a user, this registry value gets put in the TEB, and unless you change the user locale and change it back, both HKCU\Control Panel\International,Locale and GetThreadLocale will contain that wrong value in this scenario.
And not as a coincidence; one is causing the other.
Strange how unsympathetic I was to people hitting the unsupported registry value and how sympathetic I am to people calling a documented function!
This bug causes some of the random problems Delphi users are seeing like this one and this one -- and probably others if you look; although I find the thread locale to be in its own way evil it may not be the best policy to punish people for using the evil we're giving out just because it is evil.
I've been calling the thread locale evil for years and rally haven't scared very many people off of it yet. Certainly Delphi never got the memo (they really shouldn't be using the thread locale anyway, even if this bug was not happening).
Anyway, not sure yet what will happen here eventually.
For now, the officially sanctioned workaround (via Shawn):
But if I hear anything else, I'll holler....
# Mike on 20 Mar 2010 6:20 AM:
SetThreadLocale is used by both MFC and ATL 2010, so maybe Microsoft didn't get the memo either. A simple find in files on the MFC and ATL source code will show you these areas. Someone has to go through them and see if this bug has an impact on the specifc areas that GetThreadLocale is used in. Here's a summary:
Steve on 8 Mar 2011 2:22 AM:
As Mike mentioned, using MFC/ATL's CString can become a problem. For example, my computer is a en-US version of Windows 7 with the default system locale set to ja-JP. In a non-unicode program, converting from CStringA to BSTR via the AllocSysString() function will incorrectly convert the string to unicode.
One simple(although not perfect) workaround is to call SetThreadLocal(GetSystemDefaultLCID()) in InitInstance().
Michael S. Kaplan on 8 Mar 2011 8:06 AM:
Even better is to use CStringW, instead!
Daniel on 30 Aug 2012 2:25 AM:
What a relief! Thanks, I no longer need to select a language from the drop down in SQL Server Management Studio all the time. SSMS does need reopening for it to take affect.
Michael S. Kaplan on 30 Aug 2012 7:26 AM:
Cool!
SQL Server UI almost always religiously sets the thread locale, which is why that worked. But I agree that chaging it once is easier than having to change it every time! :-)
referenced by