Windows XP introduced the IShellItem interface which represents an item in the shell namespace. This encapsulates what traditionally is represented by a pair of things, the the IShellFolder interface and an ITEMID_CHILD. The shell item lets you carry just one object around instead of two.
Another way of representing an item in the shell namespace is in the form of a single ITEMID_ABSOLUTE, and you can also create a shell item from that.
Creating a single unit of currency to represent a shell item tries to solve the problem of having to exchange money every time you cross a boundary. The IShellItem also gives you some methods which simplifies various operations by wrapping low-level methods on IShellFolder. For example, the IShellItem::BindToHandler method figures out the right way to get the item you ask for rather than making you puzzle out the arcane rules behind IShellFolder::BindToObject, IShellFolder::BindToStorage, IShellFolder::CreateViewObject, IShellFolder::GetUIObjectOf, and more.
But what if you need something that IShellItem doesn’t provide a convenience wrapper for? Then you need to peek inside.
If you want to peek inside and get the IShellFolder and ITEMID_CHILD, you can use the IParentAndItem interface, specifically, the IParentAndItem::GetParentAndItem method. Once nice thing about the IParentAndItem::GetParentAndItem method is that you can pass nullptr for the things you aren’t interested in.
Alternatively, if you want to peek inside and get the ITEMIDLIST_ABSOLUTE, then you can use the IPersistIDList::GetIDList method to suck it out. We saw this a while back, but I’ll repeat it here just so the information is all in one place.
If you are willing to abandon Windows XP support, you can use the SHGetIDListFromObject function which knows how to do this. (It tries other things, too.)
Okay, let’s take things out for a spin. We’ll get the normal display name for a shell item in four ways:
- By asking the item directly.
- By using the
IShellFolder::GetDisplayNamemethod. - By using the
IPersistIDList::GetIDListmethod, and then theSHGetNameFromIDListfunction. - By using the
SHGetIDListFromObjectfunction, and then theSHGetNameFromIDListfunction.
If all goes well, we should get the same string printed each time.
Remember that Little Programs do little to no error checking.
#include <windows.h>
#include <shlobj.h>
#include <atlbase.h>
#include <atlalloc.h>
#include <stdio.h> // horrors! mixing C and C++!
void PrintNameDirectlyFromItem(IShellItem* item)
{
CComHeapPtr<wchar_t> name;
item->GetDisplayName(SIGDN_NORMALDISPLAY, &name);
_putws(name);
}
void PrintNameViaIShellFolder(IShellItem* item)
{
CComPtr<IShellFolder> folder;
CComHeapPtr<ITEMID_CHILD> child;
CComQIPtr<IParentAndItem>(item)->GetParentAndItem(nullptr, &folder, &child);
STRRET ret;
folder->GetDisplayNameOf(child, SHGDN_NORMAL, &ret);
CComHeapPtr<wchar_t> name;
StrRetToStrW(&ret, child, &name);
_putws(name);
}
void PrintNameViaAbsoluteIDList(IShellItem* item)
{
CComHeapPtr<ITEMIDLIST_ABSOLUTE> absolute;
CComQIPtr<IPersistIDList>(item)->GetIDList(&absolute);
CComHeapPtr<wchar_t> name;
SHGetNameFromIDList(absolute, SIGDN_NORMALDISPLAY, &name);
_putws(name);
}
void PrintNameViaAbsoluteIDList2(IShellItem* item)
{
CComHeapPtr<ITEMIDLIST_ABSOLUTE> absolute;
SHGetIDListFromObject(item, &absolute);
CComHeapPtr<wchar_t> name;
SHGetNameFromIDList(absolute, SIGDN_NORMALDISPLAY, &name);
_putws(name);
}
int main(int, char**)
{
CCoInitialize init;
CComPtr<IShellItem> item;
SHGetKnownFolderItem(FOLDERID_Downloads, KF_FLAG_DEFAULT, nullptr, IID_PPV_ARGS(&item));
PrintNameDirectlyFromItem(item);
PrintNameViaIShellFolder(item);
PrintNameViaAbsoluteIDList(item);
PrintNameViaAbsoluteIDList2(item);
return 0;
}
Bonus chatter: When you create a shell item, it takes the things you created it from, and it produces the other equivalent things on demand. For example, if you create a shell item from an absolute item ID list, and then you ask for the folder and child item ID, it will convert the absolute item ID list into a folder and child item ID list. (It also caches the result so that the next time you ask, it’ll be able to answer the question more quickly.)
0 comments