The goal of this first expedition is to print lists of properties from items.  This will give insight into the origins and capabilities of these different properties.  Feel free to follow along and try out different things.  There's a lot to explore.

I am splitting this and future programming expeditions into segments so that I can talk about the details without posting miles of code. 

#include <windows.h>
#include <propsys.h>
#include <propvarutil.h>
#include <propkey.h>
#include <shobjidl.h>
#include <stdio.h>

#pragma comment(lib, "propsys.lib") 
#pragma comment(lib, "ole32.lib") 
#pragma comment(lib, "shlwapi.lib") 
#pragma comment(lib, "shell32.lib") 

int wmain(__in int argc, __in_ecount(argc) WCHAR **argv)
{
    HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
    if (SUCCEEDED(hr))
    {
        if (argc == 0)
        {
            wprintf(L"Usage: %s \n", argv[0]);
        }
        else
        {
            PCWSTR pszFile = argv[1];
            wprintf(L"Properties for '%s'\n", pszFile);

            WCHAR szDir[MAX_PATH];
            if (GetCurrentDirectory(ARRAYSIZE(szDir), szDir))
            {
                WCHAR szPath[MAX_PATH];
                if (PathCombineW(szPath, szDir, pszFile))
                {
                    IShellItem2 *psi;
                    hr = SHCreateItemFromParsingName(szPath, NULL, IID_PPV_ARGS(&psi));
                    if (SUCCEEDED(hr))
                    {
                        IPropertyStore *pps;
                        hr = psi->GetPropertyStore(GPS_DEFAULT, IID_PPV_ARGS(&pps));
                        if (SUCCEEDED(hr))
                        {
                            hr = _PrintPropertyStore(pps);
                            pps->Release();
                        }
                        psi->Release();
                    }
                }
            }
        }
        CoUninitialize();
    }
}

There's nothing too special about this code.  Starting with some boilerplate, we figure out the path to the file, bind to it, and grab it's property store.  Tomorrow I'll dive into the printing loop.

SHCreateItemFromParsingName is a nifty little function.  IShellItem has been around for some time, but it was never this easy to obtain. 

On Windows 2000, you had to:

  1. Call SHGetDesktopFolder to get the desktop's IShellFolder interface
  2. Call IShellFolder::ParseDisplayName to get the PIDLIST_RELATIVE for the item
  3. Call SHCreateShellItem to get the IShellItem interface

Windows XP made this easier with SHParseDisplayName which took care of steps 1 and 2.

Windows Vista provides SHCreateItemFromParsingName to take care of steps 1, 2 and 3 all at once!

-Ben Karas