How do I show the contents of a directory while respecting the user’s preferences for hidden and super-hidden files as well as the user’s language preferences?


A customer was writing a program in (and this is what they said) "32 bit C++ .Net 4.0" which displayed the contents of a directory, and they wanted to filter out items such as hidden files and protected operating system files (also known as super-hidden files) based on the user's current Explorer preferences. Furthermore, they wanted to show localized folder names, such as Usarios instead of Users, again, the same way Explorer does. They are currently using Directory.Get­Directories().

The way to do this is to use IShell­Folder::Enum­Object, the same way Explorer does. Don't pass SHCONTF_INCLUDE­HIDDEN or SHCONTF_INCLUDE­SUPER­HIDDEN, and you will get the default enumeration that filters out hidden items based on the user's preferences. (You pass the flag to force the items to be included, overriding the user's preferences.) and the names of the items that come out of the enumeration will be the localized names. You can ask for the parsing name to get the physical file name.

#define UNICODE
#define _UNICODE
#define STRICT
#define STRICT_TYPED_ITEMIDS
#include <windows.h>
#include <shlobj.h>
#include <atlbase.h>
#include <atlalloc.h>

int __cdecl wmain(int argc, wchar_t **argv)
{
 CCoInitialize init;

 if (argc < 2) return 0;
 CComHeapPtr<ITEMIDLIST_ABSOLUTE> sppidl;
 CComPtr<IShellFolder> spsf;
 CComPtr<IEnumIDList> speidl;
 if (FAILED(SHParseDisplayName(argv[1], nullptr,
                               &sppidl, 0, nullptr)) ||
     FAILED(SHBindToObject(nullptr, sppidl,
                           nullptr, IID_PPV_ARGS(&spsf))) ||
     FAILED(spsf->EnumObjects(nullptr,
               SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &speidl)) ||
     speidl == nullptr) return 0;

 for (CComHeapPtr<ITEMID_CHILD> sppidlItem;
      speidl->Next(1, &sppidlItem, nullptr) == S_OK;
      sppidlItem.Free()) {
  PrintDisplayName(spsf, sppidlItem, SHGDN_NORMAL, L"Display Name");
  PrintDisplayName(spsf, sppidlItem, SHGDN_FORPARSING, L"For Parsing");
  wprintf(L"\n");
 }
}

The program takes a fully-qualified path on the command line and displays its contents (both in localized display name and in raw file system paths) while respecting the user's preferences for hidden and super-hidden files.

It appears that the customer is writing their program in C#, despite their claim that they were using C++ (or maybe they meant MC++ or C++/CLI). In that case, they can use the Windows 7 API CodePack for Microsoft® .NET Framework (gotta love that catchy name).

Comments (16)
  1. skSdnW says:

    SHCONTF_INCLUDESUPERHIDDEN is Win7+ and before that there is no clean way to do this. The Open/Save dialog can use CDB2GVF_SHOWALLFILES but everyone else is stuck re-implementing a custom IShellFolder implementation. Too bad the IShellFolder in shell32 is/was so tied to just the needs of Explorer.

    Implementing your own shell browser application is impossible if you just follow the documentation. Pre-Vista IShellView does not call IShellBrowser::BrowseObject but uses undocumented DDE strings + SHAllocShared and Win7+ seems to have changed the interaction between IShellView and IShellBrowser breaking previously working applications with the move to the DirectUI "Listview". IExplorerBrowser helps a bit but has its own set of issues.

    When we get that time machine Explorer should move things like SHGetSetSettings and friends to a explorer.dll creating a firewall between shlwapi/shell32 and the implementation of Explorer…

  2. George says:

    I recently converted a piece of code from using IShellFolder/IEnumIDList to IShellItem/IEnumShellItems and was surprised that the shell item enumerator doesn't respect the user settings by default. Is there a way to provide settings to IEnumShellItems similarly to IEnumIDList?

    [You use STR_ENUM_ITEMS_FLAGS in the bind context. I'll make a note to demonstrate this in a future article. -Raymond]
  3. the way to get all files (for filesystem folders) is to use FindFirstFile. Of course that's not the shell way but it is the only way for older windows before SHCONTF_INCLUDESUPERHIDDEN was added

    I have raised the lack of "super hidden access" for IEnumShellItems, perhaps Raymond can report it to the responsible team?

    social.msdn.microsoft.com/…/windows-81-ienumshellitems-protected-os-files-bug

  4. George says:

    Thanks, Raymond.

    According to msdn.microsoft.com/…/bb762592(v=vs.71).aspx, STR_ENUM_ITEMS_FLAGS is for Windows 8+. Is there a solution that works for Windows 7?

    [For earlier versions of Windows, you'll have to drop to IShellFolder::EnumObjects. -Raymond]
  5. Romulo says:

    "Usario"? Maybe Raymond means "Usuario" [Spanish] or "Usuário" [Portuguese]?

  6. Joker_vD says:

    Oh god, and *today* I was reminded about localized folder names. I've switched to English Windows specifically to NOT see that amazing design of zero virtues.

    Well. I guess with the shell/COM theme going in the latest posts that was inevitable.

  7. Matt says:

    @Romulo: Or maybe he meant "Usarios" which is Galacian for "Users".

  8. Cowardly Anon Moose says:

    @Joker_vD: Even English Windows 7 does that. I have a folder called "Documents" which displays as "My Documents".

  9. Joker_vD says:

    @Cowardly Anon Moose: Yes, it does, but at least it's shown in the middle of folders-starting-with-a-Latin-letter, instead of in the middle folders-starting-with-nonsensical-letters (which, naturally, go in sort after folders with names made of proper letters).

    Still, the most annoying thing is that there is "My Documents" folder, and right next to it, "My Documents" shortcut to folder… I think? Because everytime I click it I am informed that I have no right to access it, and it's not actually in the fiesystem.

  10. Thanks for that Raymond. Great info as always.

    This may be an opportune time (if not place, natch) to mention that the "Windows 7 API CodePack for Microsoft® .NET Framework" seems to be abandonware, at the moment. It's been moved to archive.msdn.microsoft.com, and hasn't been updated since August 2010. Sigh.

    I mentioned it the other day on the .Net Framework Blog when they were asking for requests, and I just put it on UserVoice here:

    visualstudio.uservoice.com/…/5648009-start-developing-the-windows-api-code-pack-again

  11. Fredrik Stax&#228;ng says:

    Part of the output on my system is

    Display Name = Anvõndare

    For Parsing = C:Users

    which is wrong. The õ should be ä.

  12. Chris says:

    Fredrik: I suspect this is due to an ACP/OCP difference. I presume that you are writing the output to the command prompt. You should get the correct string if you write a GUI app and output to the screen or, more simply, you could redirect the command line output to a text file and open it in Notepad.

  13. @Joker_vD: It is in the filesystem, you just don't have permission to traverse through it out-of-the-box.  The hidden shortcuts in the user folders in Windows Vista and above are simply directory junctions to the new Vista User model, made available so that programs designed for XP that use hardcoded paths will still be able to access the appropriate folder.  You can see this for yourself by using CMD or Powershell, or by granting yourself Full Access to the shortcut.

  14. Azarien says:

    @Fredrik: setlocale(LC_CTYPE, ".ACP"); will fix the codepage problem.

  15. Joker_vD says:

    "You can see this for yourself by using CMD or Powershell, or by granting yourself Full Access to the shortcut."

    The Security tab in the Properties dialog for those shortcuts shows that I *do* have a Full Access… and in Advanced–Permissions the first item also says that everybody is denied from it… so I guess the rest of the permissions are just ignored? Anyway, I don't see how "programs designed for XP that use hardcoded paths will still be able to access the appropriate folder", because ERROR_ACCESS_DENIED is a thing that exists.

    [You have full control but have explicitly revoked "list contents". Hard-coded paths will still work since they do not list contents (they require only traversal). -Raymond]
  16. Joker_vD says:

    Oh. That actually makes sense. Thanks for clarifications.

Comments are closed.

Skip to main content