Parameter confusion #2

by Michael S. Kaplan, published on 2005/10/21 14:10 -04:00, original URI:

It seems like it was just ages ago that I talked about Parameter Confusion in regard to the LCMapString function when used for sort keys.

Oh wait, it was just yesterday. :-)

There is another direction in which the confusion can exist, though -- this time not with the difference but with the consistency.

Let's take a look at the GetLocaleInfo function. its prototype is simple enough:

int GetLocaleInfo(
  LCID Locale,      // locale identifier
  LCTYPE LCType,    // information type
  LPTSTR lpLCData,  // information buffer
  int cchData       // size of buffer

Simple enough, right? That cchData has a simple set of rules that it goes by:


[in] Specifies the size, in TCHARs, of the lpLCData buffer. If cchData is zero, the function returns the number of TCHARs required to hold the information, including the terminating null character, and the buffer pointed to by lpLCData is not used.

Well, not completely simple, but close enough. Since you are passing a string, it is just saying to use the count of characters and not the count of bytes.

Of course, when you include the LOCALE_RETURN_NUMBER flag in with your LCType, is when things get a bit more complicated:


Windows 98/Me, Windows NT 4.0 and later: This causes the function to return the value as a number instead of as a string. The buffer that receives the value must be at least the length of a DWORD. This flag can be combined with any specifier beginning with LOCALE_I by using the binary-OR operator.

Yikes, now that certainly does put a cat among the pigeons. What should the value in cchData be now, with this buffer that must be the length of a DWORD?

The answer is deceptively simple -- it should be the same value -- the size in TCHARs. The easiest way to do this is to generically pass sizeof(DWORD) / sizeof(TCHAR) any time you are using the LOCALE_RETURN_NUMBER flag on any of the LOCALE_I* LCType values.

You can be a little fancier in cases where the number is typed as something different -- like with LOCALE_ICALENDARTYPE (which returns a CALID). But then if you look at the definition of CALID in winnls.h you will see why I am not too concerned about the need pass sizeof(CALID) / sizeof(TCHAR) for that case:

//  Calendar ID.
typedef DWORD CALID;

Ok, perhaps this ends up being kind of simple, after this part about LOCALE_RETURN_NUMBER is explained. Lots of people still find it to be kind of confusing....


This post brought to you by "ޝ" (U+079d, a.k.a. THAANA LETTER SHEENU)

# Gabe on 22 Oct 2005 3:27 AM:

Wow, I don't think reading an MSDN article has ever made me feel so ill as the one for GetLocaleInfo.

For example, "the values in Win.ini take precedence over the database settings when getting values for the current system default locale." Does it really get values from Win.ini, or is this an entry that should have been updated 10 years ago?

Why is the default to return integers encoded as strings? Are we to assume that they're localized?

And what's with taking a character count? I can't think of any situation where that's really warranted instead of an actual byte count. To me this is just asking for buffer overflow bugs.

No wonder people get confused!

# Michael S. Kaplan on 22 Oct 2005 1:39 PM:

Well, what used to be in win.ini is what is now in the registry -- that is being updated now.

The default is stringd because when the function was originally introdued the LOCALE_RETURN_NUMBER option was not available. Changing the default would have broken all existing code.

The character count/byte count issue is a valid one to consider, though honestly the registry API are the only notable example that went that way versus cch values for strings; almost everything in kernel32, user32, and gdi32 followed a different model.

# Gabe on 26 Oct 2005 6:01 PM:

It think it is a valid question to ask why on earth did anybody (i.e. the original author of the function) think that it would be a good idea to return integers as strings. Was this API originally written in COBOL?

# Michael S. Kaplan on 26 Oct 2005 6:07 PM:

No Gabe, it is written in C. But like I said the values for overrides used to be in places where they were strings, so it just sort of happened that way.

Was it a bad idea to go that way? Probably, which is why a few years later a flag was added to return numbers. But we could not break the old behavior, so we were stuck with it -- the hazard of having code bases that stick around this long....

Please consider a donation to keep this archive running, maintained and free of advertising.
Donate €20 or more to receive an offline copy of the whole archive including all images.

referenced by

2006/01/12 The creation of sort keys does not always make sense

2005/10/22 Parameter confusion #2a

go to newer or older post, or back to index or month or day