Why is there a CSIDL_DESKTOP value if you need the desktop in order to get it anyway?


John asks why there is a special constant CSIDL_DESKTOP defined for the desktop. After all, in order to use CSIDL_DESKTOP, you need to call SHGet­Desktop­Folder and then bind to it. What's the point of having an ITEM­ID­LIST that represents the desktop if, in order to use it, you first need to get the desktop?

It's like asking why the file system uses . (dot) to refer to the current directory. You're already in the current directory. In order to resolve . (dot), you already need to have the current directory, so why bother with the dot at all?

Because it is often convenient to give a name to your starting point.

Suppose somebody wants to save a file to the desktop. How would you represent this as an ITEM­ID­LIST? If the only thing you can do is fill in the blank in the sentence, "Start with the desktop folder, then go to ________, then save the file there," then you need a way to say "where you are now."

And that's what CSIDL_DESKTOP gives you. An ITEM­ID­LIST that says "Where you are now."

Besides, if CSIDL_DESKTOP weren't defined, somebody would have invented it. Say your program has a list of directories it wants to operate on, say, the Documents folder, the Music folder, and the Shared Documents folder. Great, so let me write a function:

void DoItIn(HWND hwnd, int csidl)
{
 PIDLIST_ABSOLUTE pidl;
 if (SUCCEEDED(SHGetSpecialFolderLocation(
                     hwnd, csidl, &pidl))) {
  IShellFolder *psf;
  if (SUCCEEDED(SHBindToObject(NULL, pidl, NULL,
                    IID_PPV_ARGS(&psf)))) {
   ...
   psf->Release();
  }
  CoTaskMemFree(pidl);
 }
}

void DoItInStandardPlaces(HWND hwnd)
{
 const static int csidls[] = {
  CSIDL_MYDOCUMENTS,
  CSIDL_MYMUSIC,
  CSIDL_COMMON_DOCUMENTS,
 };
 for (int i = 0; i < ARRAYSIZE(csidls); i++) {
  DoItIn(hwnd, csidls[i]);
 }
}

Now you want to add the desktop folder. Oh wait, there is no CSIDL value for the desktop, so I'll have to make one up.

// Our custom CSIDLs use the high word. None of the CSIDLs we use
// set any bits in the high word, so we can use the high word to
// detect whether we have a standard CSIDL or a custom CSIDL.
#define CUSTOMCSIDL_DESKTOP 0x00010000

#define ISCUSTOMCSIDL(csidl) HIWORD(csidl)
#define STANDARDCSIDLOF(csidl) LOWORD(csidl)

HRESULT MyGetSpecialFolderLocation(
    HWND hwnd, int csidl, PIDLIST_ABSOLUTE *ppidl)
{
 HRESULT hr;
 if (ISCUSTOMCSIDL(csidl)) {
  *ppidl = (PIDLIST_ABSOLUTE)CoTaskMemAlloc(sizeof(WORD));
  if (*ppidl) {
   ppidl->mkid.cb = 0;
   hr = S_OK;
  } else {
   hr = E_OUTOFMEMORY;
  }
 } else {
  hr = SHGetSpecialFolderLocation(hwnd, STANDARDCSIDLOF(csidl), ppidl);
 }
 return hr;
}

Okay, cool, now I can add

 const static int csidls[] = {
  CSIDL_MYDOCUMENTS,
  CSIDL_MYMUSIC,
  CSIDL_COMMON_DOCUMENTS,
  CUSTOMCSIDL_DESKTOP,
 };

Oh wait, I also have to have a custom version of SHBind­To­Object that knows how to bind to this special new type of pidl that means "where you are now."

HRESULT MyBindToObject(IShellFolder *psf, PCUIDLIST_RELATIVE pidl,
  IBindCtx *pbc, REFIID riid, void **ppv)
{
 HRESULT hr;
 if (pidl->mkid.cb == 0) {
  *ppv = NULL;
  if (psf == NULL) {
   hr = SHGetDesktopFolder(&psf);
   if (SUCCEEDED(hr)) {
    hr = psf->QueryInterface(riid, ppv);
    psf->Release();
   }
  } else {
   hr = psf->QueryInterface(riid, ppv);
  }
 } else {
  hr = SHBindToObject(psf, pidl, pbc, riid, ppv);
 }
 return hr;
}

Congratulations, you just reinvented CSIDL_DESKTOP.

It can be very convenient to have a name for the null action.

Comments (4)
  1. Vista cue says:

    "Criticism

    This pattern should be used carefully as it can make errors/bugs appear as normal program execution."

  2. Joshua says:

    [Good catch. I should've used the high byte, not the high word. -Raymond]

    I made a similar mistake once where I added to my system menu and used a menu id that the next version of Windows used.

    Score -1 for me for misreading the documentation on that one.

  3. asdf says:

    We know CSIDL_FLAG_MASK is 0xff00 and that leaves us with 0xffff00ff as a mask for the id itself. How are people outside MS supposed to know that the id will never be > 0xff? If csidls[] could be set by the user, ISCUSTOMCSIDL could filter out valid MS ids! (This question is probably a bit dated since we have KNOWNFOLDERID now and Vista,7 did not add new CSIDL's)

    [Good catch. I should've used the high byte, not the high word. -Raymond]
  4. Aaron says:

    It can be very convenient to have a name for the null action.

    Interesting, I wasn't aware there was an OO pattern name for "a identity (mathematics), but with less rigor". Thanks!

Comments are closed.

Skip to main content