The only thing you can do with display names is display them


There are many functions that return strings called "display names". And the only thing you can do with display names is display them. Don't assume that the string actually means anything, because it doesn't. Theoretically, a function like SHGetFileInfo could be implemented as

...
 if (uFlags & SHGFI_DISPLAYNAME) {
  StringCchCopy(psfi->szDisplayName, MAX_PATH, TEXT("Booga!"));
 }
...

and your program should still work.

(Of course, this is merely a ground rule. Specific functions may have exceptions. For example, the IShellFolder::GetDisplayNameOf has a special flag SHGDN_FORPARSING flag which explicitly indicates that the string returned is designed to be parsed.)

The purpose of a "display name" is to be a string suitable for displaying to the user. The display name for a file, for example, might be the file name, or it might have the extension removed, or the name might even be translated into the user's preferred language! For example, on an English system with the German multilanguage pack installed and active, asking for the display name of C:\Documents and Settings\Raymond\My Documents will return Eigene Dateien because that's the name for My Documents in German.

If your program assumed that the display name of C:\Documents and Settings\Raymond\My Documents would be something like "My Documents", your program is in for a big surprise when I run it.

One of my colleagues was investigating a bug reported against a program that wouldn't run. It claimed that the CD-ROM was not in the drive, even though it was. Why can't the program find its CD-ROM?

After a few days' investigation, my colleague found the reason. The program wanted to find its CD-ROM, so it walked through all 26 drive letters and called SHGetFileInfo passing the SHGFI_DISPLAYNAME flag. Something like this:

// The actual code was much, much more convoluted than this.
char LookForCD(LPCTSTR pszVolumeLabel)
{
 for (TCHAR chDrive = TEXT('A'); chDrive <= TEXT('Z'); chDrive++) {
  TCHAR szRoot[4];
  wsprintf(szRoot, TEXT("%c:\\"), chDrive);
  SHFILEINFO sfi;
  if (SHGetFileInfo(szRoot, 0, &sfi, sizeof(sfi), SHGFI_DISPLAYNAME)) {
   TCHAR szExpected[MAX_PATH];
   wsprintf(szExpected, TEXT("%s (%c:)"), pszVolumeLabel, chDrive);
   if (strcmp(szExpected, sfi.szDisplayName) == 0) {
    return chDrive; // Found it!
   }
  }
 }
 return 0; // not found
}

The program asked for the display name of each drive and looked for one whose display name was of the form LABEL (D:), where LABEL is the volume label they're looking for and D: is the drive letter.

In othe words, they were trying to interpret the display name.

Don't do that. There is no guarantee that the display name for a CD-ROM will be of any particular form. The default in Windows XP happens to be LABEL (D:), but there are a variety of system policies that can be used to change this, and if any of those policies is in effect, the program can't find its CD-ROM and refuses to run.

(For the record, the correct way of doing this would be to pass each drive letter to the GetDriveType function to see if it is a DRIVE_CDROM; for each such drive, call GetVolumeInformation to get the CD-ROM's volume label.)

So remember, display names are just for display purposes.†

Nitpicker's corner

†See parenthetical remark at the top of this entry for clarification. The sentence intentionally overlooked the exceptions in order to provide a punchier ending to the story. It's called writing style and is a valid literary technique.

Comments (23)
  1. C Gomez says:

    I once had to write something that figured out if our particular CD was in a drive to do something with it.

    The whole time I was doing this, it felt like a hack, but I actually did look for CD-ROM drives using GetDriveType and GetVolumeInformation to check the volume label.

    It felt like a hack from the point of view of "I know Windows will tell me this stuff, but aren’t there cases this ‘detection’ will fail?" and not being sure I was thinking of the non-obvious ones (Obvious one being: "Other CD with same volume label").

    So I added a few more ways to determine it was really ours.  Maybe it was good, maybe it wasn’t.  It wasn’t running code off the CD and the data was checked to be well-formed and valid before using it.  Seemed pretty safe…

    Well, in any case, good tip…  What I especially liked about it was the note that this is a ground rule, and some APIs actually do return strings that can be used for parsing operations, etc.  That is good to remember.

  2. Joe Butler says:

    This was worth the read, if only to find the gem that was today’s Nitpicker’s Corner.

    I used to work with someone that needed such qualifications for every damn thing one would say.  Very frustrating experience indeed.

  3. ac says:

    I cant believe a developer would choose SHGetFileInfo over GetDriveType+GetVolumeInformation (The first hit on google for —get volume label— is http://vbnet.mvps.org/index.html?code/disk/volumelabel.htm , it might be in VB but atleast its using the correct function)

    Does this break on win9x, IIRC those systems had the drive letter before the label

  4. nksingh says:

    I guess it’s only a minor issue, but you can also call "GetLogicalDeviceStrings" or use "QueryDosDevice" and look for cdromN to find the names of all attached CDROM drives.  I don’t see any advantage to my method though, since it takes ~15-20 lines of code whereas just searching every drive letter takes 4-5.

  5. David Walker says:

    C Gomez:  Couldn’t you also end up finding a disk drive with the same volume label as the CD-ROM?  This might affect the original problem Raymond mentioned, too.

  6. Arno says:

    Among the exceptions, I believe there is

    IMoniker::GetDisplayName

    of a file moniker (for which IMoniker::IsSystemMoniker returns MKSYS_FILEMONIKER), which is documented to return the path to the file. I do not think there is any other, better way to get to the path pointed to by the moniker.

  7. Ben Cooke says:

    I get paid to design and implement APIs, and I make a point of maintaining conventions like these. getDisplayName on an object will always return something that is ONLY for display to the user. If a function returns HTML as opposed to plain text it’ll be called getHTMLWhatever or getWhateverAsHTML, which implies that anything that doesn’t have HTML in its name needs to be HTML-escaped before it can be included in an HTML document. Etc, etc…

    But my users pay no attention. So many times I’ve had users whine at me when I change what’s returned by getDisplayName in certain cases, and bugs where developers have escaped stuff they shouldn’t or have not escaped stuff they should.

    I’ve concluded that there is a class of developers that has a bad attitude: the attitude of "get it done as quickly as possible by any means necessary". They won’t read the API docs, they’ll code to the implementation rather than the contract, and they’ll do cringeworthy things like using reflection or other tricks to grovel around in object internals. (Or, if they’ve got commit access, they’ll make member variables public with no explanation and then get all pissy with me when I revert it.)

    I really hate this sort of developer. Fortunately, the vast majority of the consumers of my APIs are my co-workers, so if they do things wrong I can call them on it. I’d hate to work at Microsoft. :)

  8. KJK::Hyperion says:

    nksingh: no way, the device name could be an arbitrary string automatically generated by PnP! You should use SetupDi to enumerate the instances of the CD-ROM device interface! this is basic stuff, dude!

    (if you were wondering: it’s GUID_DEVINTERFACE_CDROM, or {53F56308-B6BF-11D0-94F2-00A0C91EFB8B})

    (it’s actually a good exercise for when you’ll have to deal with more unusual devices)

  9. SM says:

    >C:Documents and SettingsRaymondMy Documents will return Eigene Dateien because that’s the name for My Documents in German.

    Does it return ‘Raimund’ instead of ‘Raymond’?

  10. Jonathan says:

    It’s unfortunate the the "net" command mixes service display names with their real names. I can imagine a lot of scripts doing things like:

      net stop "Automatic Updates"

    and breaking on non-English systems.

    I find it amusing that the display namme of lanmanserver is "Server".

    -Jonathan

  11. BryanK says:

    SM — I sure hope it doesn’t change the user name.  Proper names aren’t translatable in general, and almost always should be left alone.  Certainly names that the user gave you (when you set up that machine, you specified a user name) should be left alone.

    Of course this only applies on the way back out to the user (not on the way to disk), so maybe it’s a bit less critical.  However, I still don’t think translating the user’s name is a good idea.

    (OTOH, translating "Documents and Settings" may be possible.)

  12. Centaur says:

    I’d say looping through all drive letters to find the CD would be wrong no matter what was inside the loop. Because the CD may be mounted in a subdirectory on an NTFS volume, and not assigned a drive letter.

  13. Ken Hagan says:

    In addition to IShellFolder’s display name, COM has the IParseDisplayName interface precisely to allow the parsing of moniker display names. Sadly, these two probably "exceptions" probably account for 99% of the "display names" most programmers will ever encounter.

    Having said that…

    I assume that the program in question wasn’t run from the CD-ROM (since GetModuleFileName would then suffice). I also assume that the program wasn’t installed from the drive it is now looking for, since saving the drive in the registry during installation would be too easy. With those assumptions, I have to wonder why the program was looking for its (sic) drive, why it thought it might still be there, and what it thought it might find there.

    In short, I bet the program didn’t actually need to solve this problem.

  14. Miral says:

    On a related note, I’ve seen a lot of confusion recently about what programs (installers, especially) should display when faced with folders like the Program Files folder in Vista — where the English form is used on disk and the localised form is displayed in Explorer.

    If you want to show a full path, such as C:Program FilesMyCompanyMyApp, that’s obviously the "real" on-disk name, which could confuse some people because that’s not what appears in Explorer.  So instead you might want to show C:Archivos de programaMyCompanyMyApp, which is a path that doesn’t necessarily exist on disk, and the user can’t copy/paste into some other program and expect it to find the folder.

    Whoops, you’ve just pulled all your hair out.

  15. Norman Diamond says:

    the name might even be translated into the

    user’s preferred language!

    That is true but an understatement in several ways.  The name might be translated into the default language of the Windows version and might not be displayable in the font that some programmer "knew" was best.  I still use Sunbelt/Kerio personal firewall because some its features are still valuable, but it’s really a pain when a message box shoves a network adapter friendly description through an 8-bit font and I can’t even guess which network adapter it’s talking about.

    Nitpicker’s corner

    No problem with today’s nitpick, but I can’t help remembering a recent one.  Someone characterized a restriction that some statement was only valid for US-English systems as being a nitpick.  I think that today’s article accurately points out that the previous nitpick wasn’t a nitpick.

    Tuesday, March 13, 2007 6:16 PM by Ken Hagan

    I also assume that the program wasn’t

    installed from the drive it is now looking

    for, since saving the drive in the registry

    during installation would be too easy. With

    those assumptions, I have to wonder why the

    program was looking for its (sic) drive

    It’s easy to change the drive letter of a CD-ROM drive.  Office 97’s installer did save the CD-ROM’s drive letter in the registry during installation.  If you added a hard drive and automatically got the CD-ROM’s drive letter shifted up, then Office 97’s uninstaller would refuse to uninstall it.  Why Office 97’s uninstaller was looking for its drive, neither you nor I know, we can only see that it did.

    If the program were a game, being executed instead of uninstalled, then it might be trying to check if the user actually still inserted the real CD a workable version of D-Tools, instead of having a pirated copy on the hard disk.

    Tuesday, March 13, 2007 7:46 PM by Miral

    If you want to show a full path

    Then you have to display both the display name and the real name.  This approach values hair over display space.

  16. Jonathan says:

    There are legitimate reasons to look for CDROMs. For example, suppose you are a  program distributed on CD, you get installed with some missing features, and now now you want to add these features. You tell the user "please insert the CD  labeled ‘bla’ into the CDROM". The user does that, and now you need to find it.

    Of course, you should save the origial path from which you were installed and try that first – to cover the cases of installing from HD / share / NTFS mount point / the same CDROM drive letter / etc. And also provide the user a "browse" button, should the CD been later copied into HD/share/etc.

  17. James says:

    Jonathan: Searching for the CD is certainly legitimate (although searching for the file(s) you actually need generally makes more sense: my copies of Windows, VMWare and Office were all installed from network shares not CDs) – the problem is when you try looking for "a drive Windows calls MYPROG", on the assumption that will always be the name Windows gives it.

    Reasonable idea (search for the CD), bad implementation (searching by display name, wrongly assuming that will always match the volume name). Like writing to Raymond as ‘The guy with the blog, Shell team, Microsoft’ – maybe it’ll get there this time, but what happens if the other team members’ blogs become as popular, or the teams get rearranged?

  18. Philip Taylor says:

    I encountered a vaguely related problem with the game MDK (for Win95) quite a while ago. It actually does what Raymond suggests here, though it first calls GetLogicalDrives and only tests GetDriveTypeA for those which exist. The annoying part was that it stopped searching once it found a CD drive, and then went on to check it was the right disc – but I had the game disc in a second CD drive, and it never got far enough to see that one. At least "char LookForCD(LPCTSTR pszVolumeLabel)" is a sensible function interface, unlike "char LookForCDDrive()", since it’s not assuming there’s only one drive.

    (The use of "char" reminds me that I’ve always wanted Unicode drive letters. I can’t think of any valid reasons at all, but at least it would be fun.)

  19. Dewi Morgan says:

    I have to say I’ve really come to love the nitpicker’s corner at the end of the articles :)

  20. GregM says:

    Ken, (sic) is placed after something you’ve quoted that’s a mistake by the original author to indicate that you know it’s wrong, but you left it as it was originally written.  What exactly are you indicating is wrong by using it there?

  21. Ken Hagan says:

    "Ken, (sic) is placed after something you’ve quoted that’s a mistake by the original author to indicate that you know it’s wrong, but you left it as it was originally written.  What exactly are you indicating is wrong by using it there?"

    A conceptual error, rather than a literary one. The drive does not belong to the program. It belongs to the user. It is wrong for the program to expect that the drive is still there, or contains the same volume. If it wants such things, it should ask the user to arrange it. As it happens, this approach is both simpler and more robust, which is why I claim that the program didn’t need to solve the problem it had set itself.

  22. Norman Diamond says:

    A conceptual error, rather than a literary

    one. The drive does not belong to the program.

    I asked my cat’s opinion of its (sic) owner.  That was a conceptual error, rather than a literary one.  The owner does not belong to the cat.  The cat disagreed.

    My (sic) father would be mad as hell if he saw this sentence.  (Considering how unsociable slavery is in most countries.)

  23. Ken Hagan says:

    Odd. I think it is clear I’m not using "owner" in a strict sense of "legal property", especially if you bother to read beyond the first 3 sentences, so I have no problem with Norman’s reply.

    Returning to Greg’s point, yes I suppose I am putting words into the original programmer’s mouth, but if they were asked to defend their program do you really believe they wouldn’t talk about "its" drive and don’t you think that cuts to the root of the problem?

Comments are closed.

Skip to main content