Vista/XPのプロパティシステムで使えるプロパティ

Windows VistaやXP SP2では、Windows Searchをはじめとする各種アプリケーションで、共通のプロパティシステムがある。このシステムには、IPropertySystem経由でアクセスできる。また、XMLベースのProperty Description Schemaを利用すれば、プログラマが自由に拡張することも出来る。

これらのプロパティは、通常、GUID+PIDの組み合わせで判別され、それとは別にCanonical Name(正規名)とDisplay Name(表示名)を持っている。たとえば、

GUID/PID Canonical Name Display Name
{F29F85E0-4FF9-1068-AB91-08002B27B3D9},2 System.Title タイトル

となっている。また、Canonical NameとGUID/PIDは相互に変換可能(PSGetNameFromPropertyKeyや、PSGetPropertyKeyFromName)なので、どちらか片方が分かれば、プロパティを識別することは可能だ。ただし、Canonical Nameはない場合もある。

下記のプログラムはこのプロパティ一覧を取得するプログラム。

// enumproptypes.cpp
#include <windows.h>
#include <initguid.h>
#include <propsys.h>
#include <stdio.h>
#include <string.h>
#include <locale.h>
#include <stdexcept>

#include "atlbase.h"
#include "atlcom.h"
#include "atlstr.h"

#define TOF(expr) do {HRESULT hr = expr; if(FAILED(hr)) {CStringA s; s.Format("%s(%d): HR=0x%08X: %s", __FILE__, __LINE__, hr, #expr); throw std::runtime_error((const char*)s);}} while(0)

static CStringW GetVTStr(VARTYPE vt)
{
#define VTDEF_ENTRY(a) {#a, a},
  static const struct VTDEF {const char* name; VARTYPE vt;} tbl[] = {
  VTDEF_ENTRY(VT_NULL)
  VTDEF_ENTRY(VT_LPWSTR)
  VTDEF_ENTRY(VT_BOOL)
  VTDEF_ENTRY(VT_UI1)
  VTDEF_ENTRY(VT_I2)
  VTDEF_ENTRY(VT_UI2)
  VTDEF_ENTRY(VT_I4)
  VTDEF_ENTRY(VT_UI4)
  VTDEF_ENTRY(VT_I8)
  VTDEF_ENTRY(VT_UI8)
  VTDEF_ENTRY(VT_R8)
  VTDEF_ENTRY(VT_FILETIME)
  VTDEF_ENTRY(VT_CLSID)
  VTDEF_ENTRY(VT_BLOB)
  VTDEF_ENTRY(VT_UNKNOWN)
  VTDEF_ENTRY(VT_STREAM)
  {NULL, 0}};
#undef VTDEF_ENTRY
  for(size_t i = 0; tbl[i].name != NULL; i++)
    if(tbl[i].vt == vt)
      return tbl[i].name;
  
  CStringW ret;
  ret.Format(L"%u", vt);
  return ret;
}

static size_t GetLevel(LPCWSTR propName)
{
  size_t lev = 0;
  while(propName = wcschr(propName, '.'))
  {
    propName++;
    lev++;
  }
  return lev;
}

int wmain(int argc, wchar_t* argv[])
{
  setlocale(LC_ALL, "");
  CoInitialize(NULL);

  try
  {
    CComPtr<IPropertySystem> propSys;
    TOF(propSys.CoCreateInstance(CLSID_PropertySystem));
    
    CComPtr<IPropertyDescriptionList> pdl;
    TOF(propSys->EnumeratePropertyDescriptions(
      PDEF_ALL,
      __uuidof(IPropertyDescriptionList),
      (void**)&pdl));
    
    UINT count = 0;
    TOF(pdl->GetCount(&count));
    for(UINT i = 0; i < count; i++)
    {
      CComPtr<IPropertyDescription> pd;
      if(FAILED(pdl->GetAt(i, IID_IPropertyDescription, (void**)&pd)))
        continue;
      
      VARTYPE type = VT_NULL;
      if(FAILED(pd->GetPropertyType(&type)))
        continue;
      
      LPWSTR cname = NULL;
      pd->GetCanonicalName(&cname);
      if(GetLevel(cname) == 1)
      {
        PROPERTYKEY key;
        pd->GetPropertyKey(&key);
        LPWSTR clsid;
        StringFromCLSID(key.fmtid, &clsid);
        
        LPWSTR dname = NULL;
        pd->GetDisplayName(&dname);
        wprintf(L"%s,%u\t%s\t%s\t%s\n", clsid, key.pid, cname, dname, GetVTStr(type));
        CoTaskMemFree(dname);
        CoTaskMemFree(clsid);
      }
      CoTaskMemFree(cname);
    }
  }
  catch(std::exception& e)
  {
    fprintf(stderr, "%s\n", e.what());
  }
  CoUninitialize();
  return 0;
}