To Ex or not to Ex? THAT is the question.

by Michael S. Kaplan, published on 2005/10/31 00:01 -08:00, original URI: http://blogs.msdn.com/michkap/archive/2005/10/31/486870.aspx


(Although the title may suggest it to some people, this is not a post about whether I should be giving my ex-girlfriend a call; it is not!)

There are many functions in the Windows API set that have Ex or Ex-style varations that get added.

In lots of cases, the Ex version acts as extension, and extends the functionality of the original function. But in some cases it is simply one that changes the functionality in a particular way.

So it is important not to try to pick the one to call by assuming that the old function just calls the new one.

Sometimes such an assumption will work -- TextOutW basically just calls ExtTextOutW, and FindResourceW does little more than call FindResourceExW. And in most cases on NT platforms the "A" functions convert and call their "W" equivalents.

But other times, it will not -- ToUnicode and ToUnicodeEx actually both call the same internal function and thus all you get for trying to short circuit what appears to be the wrapper is that you push an extra 4-8 bytes of stack.

The code for the Unicode and ANSI versions of CharNext are entirely different bits of code, trying to solve entirely different kinds of issues. You will actually cause more problems than you solve by trying to convert yourself and calling one of them.

And many people feel like you need a spirit dancer, a Magic 8-Ball and a ouija board if you want to know the difference between GetTextExtentPointGetTextExtentPoint32 and GetTextExtentExPoint, or between the EnumFonts, EnumFontFamilies, and EnumFontFamiliesEx functions. Every time you think you get a handle on the differences in the Platform SDK documentation, a topic like this one or this one makes you start wondering again.

Now on top of all that, in a particular very special case, the Ex version is actually a wrapper around the non-Ex version.

That case is GetStringTypeA, GetStringTypeW, and GetStringTypeEx, the main subjects of this post.

These three functions have an interesting, some would say the professional equivalent of sordid, history.

You see, there is no GetStringType function in the documentation or the header files, because the decorated functions (GetStringTypeA and GetStringTypeW) take different parameters -- the former takes an LCID and the latter does not.

The story varies when asking oldtimers about how this all came about.

Perhaps more will chime in, ones without bias especially welcome -- bias clearly is what makes so many people have such different recollections!

But the most consistent story I have been able to glean from various sources is that GetStringTypeW was done by the NT team for NT 3.1 and GetStringTypeA was done by the Win9x team for Win95. Now NT 3.1 shipped first, and it was only when the Win95 project hit this bit of code that the need for an LCID became obvious. The NT team could not change their function signature and the 9x team could not proceed without a context for the conversion, so they split the difference and kept them separate, and Win95/NT 3.5 both shipped with the GetStringTypeEx function.

(No one could recall if NT 3.1 ever was supposed to support a GetStringTypeA that had no LCID parameter, but I have checked the NT 3.1 kernel32.dll and it did not export such a function (the NT 3.5 version matches the current function, as the documentation indicates). One person hypothesized that the Win9x issue came up in time to yank out the LCID-less GetStringTypeA but too late to change GetStringTypeW, but admitted that this was pure guessing!)

Now, looking at all of the functions to explain what they do:

Now 'uses the locale' means looking it up in our list of 'loaded' locales, an if it is not there, loading it and adding it to that list. Maybe GetStringTypeExW could have skipped looking at the locale (since it does not use it) but it was not written that way originally (at this point who knows what kind of dependencies might be in customer code, so we can't change the behavior.

Notice what happened here though -- GetStringTypeW (which technically came first) is the main function, and the other two that came later are the wrappers that are doing extra work just to call the original function. The other functions provide no additional funtionality unless you want to consider it as a confusing way to get IsValidLocale-esque functionality (which seems crazy, but is the reason we can't take out the behavior, unfortunately).

So, the answer to the original question "To Ex or not to Ex?" in this case is (ironically enough, just as with the ex-girlfriend?) probably not to Ex. And for the wider question, it is probably better to decide based on the functionality you need and not on which one looks newer, unless you have someone writing a blog entry to explain why you might want to do something different.

And how often does that really happen, anyway? :-)

 

This post brought to you by "¿" (U+00bf, INVERTED QUESTION MARK)


# PatriotB on Monday, October 31, 2005 4:07 AM:

There's even some ExEx functions out there. The COM folks had the right idea with using a number at the end...

# Michael S. Kaplan on Monday, October 31, 2005 7:20 AM:

I kind of like ExEx -- it puts it all in perspective. :-)

# Skywing on Monday, October 31, 2005 11:19 AM:

I always thought it was silly to release "Ex" and "plain" versions of functions at the same time.

For instance, consider VirtualAlloc/VirtualAllocEx and WaitForSingleObject/WaitForSingleObjectEx - as far as I know they have both always existed, and the non-Ex functions just provide the C equivalent of a default argument.

# Michael S. Kaplan on Monday, October 31, 2005 11:21 AM:

That is not so bad, Skywing -- the 'Ex' does not always mean for a future version; sometimes it is just extended functionality.

# Vorn on Monday, October 31, 2005 2:05 PM:

Perhaps you should call your ex-girlfriend anyway. :)

I don't get it - why on earth is there an ex version of GetStringTypeA if it does nothing extra?

Vorn

# Michael S. Kaplan on Monday, October 31, 2005 9:07 PM:

No way, Vorn -- some wounds are best not picked at. :-) As to why -- so it could be like every other Win32 API function with "W" and "A" versions and can be doc'ed as GetStringTypeEx.

# Yuhong Bao on Thursday, December 29, 2005 3:12 PM:

How about including definations like this in Windows.h or any header file that is included in it:
#ifdef UNICODE
#define GetStringType(Locale, dwInfoType, lpSrcStr, cchSrc, lpCharType) GetStringTypeW(dwInfoType, lpSrcStr, cchSrc, lpCharType)
#else
#define GetStringType(Locale, dwInfoType, lpSrcStr, cchSrc, lpCharType) GetStringTypeA(Locale, dwInfoType, lpSrcStr, cchSrc, lpCharType)
#endif

# Michael S. Kaplan on Thursday, December 29, 2005 3:19 PM:

Personally I think this would have been okay, but people are (apparently) very sensitive about dropping parameters on Win32 API function calls....

referenced by

2012/04/05 WideCharToMultiByte vs. DrawTextW? In tennis terms, 15-Love!

2010/11/08 Header files are the wrong place to be less than helpful

2008/12/08 Lt is TC (and TC is Title Case, or Total Crap -- take your pick!)

2008/07/26 Don't sneak a BOM in on someone who promises to ignore free space

2007/07/15 Neutral? I do not think that word means what you think it means!

2007/06/11 The difference between C1_SPACE-ing out and drawing a C1_BLANK

2006/10/07 GetStringTypeW almost understands the Bidirectional Algorithm

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