WIC一覧の取得

上記の方法で、いろいろなファイルが読み込めることがわかったが、一方で、WIC経由で開くことのできる(はずの)ファイル形式の一覧がほしいことがある。端的に言えば、「ファイルを開く」ダイアログのフィルターを作ったりするときに、*.*では格好悪すぎる。
下記のコードは、レジストリの情報を読み込んで、WIC対応のファイル拡張子一覧を取得する。

#include <windows.h>
#include <shlwapi.h>
#include <shlguid.h>
#include <tchar.h>
#include <clocale>
#include "atlbase.h"
#include "atlstr.h"

static CString RegGetString(HKEY hKey, LPCTSTR path, LPCTSTR name)
{
  CRegKey regKey;
  if(regKey.Open(hKey, path, KEY_READ) != ERROR_SUCCESS)
    return _T("");
  ULONG sz = 0;
  if(regKey.QueryStringValue(name, NULL, &sz) != ERROR_SUCCESS)
    return _T("");
  CString s;
  regKey.QueryStringValue(name, s.GetBufferSetLength(sz), &sz);
  s.ReleaseBuffer();
  return s;
}

int main()
{
  std::setlocale(LC_ALL, "");
  
  CString keyPath = _T("CLSID\\{7ED96837-96F0-4812-B211-F13C24117ED3}\\Instance");
  CRegKey regKey;
  if(regKey.Open(HKEY_CLASSES_ROOT, keyPath, KEY_READ) == 0)
  {
    for(DWORD i = 0;; i++)
    {
      CString name;
      LPTSTR pName = name.GetBufferSetLength(MAX_PATH);
      ULONG sz = name.GetLength();
      if(regKey.EnumKey(i, pName, &sz, NULL) != 0)
        break;
      name.ReleaseBuffer();
      
      CString clsid = RegGetString(regKey, name, _T("CLSID"));
      if(clsid.GetLength() == 0)
        continue;
      
      keyPath.Format(_T("CLSID\\%s"), clsid);
      CString exts = RegGetString(HKEY_CLASSES_ROOT, keyPath, _T("FileExtensions"));
      if(exts.GetLength() == 0)
        continue;
      
      CString filters, prevTypeName;
      for(int p = 0; p >= 0;)
      {
        int np = exts.Find(_T(","), p);
        CString ext;
        if(np >= p)
        {
          ext = exts.Mid(p, np - p);
          p = np + 1;
        }
        else
        {
          ext = exts.Mid(p);
          p = -1;
        }
        
        CComPtr<IQueryAssociations> qa;
        AssocCreate(
          CLSID_QueryAssociations, __uuidof(IQueryAssociations),
          (void**)&qa);
        qa->Init(ASSOCF_INIT_DEFAULTTOSTAR, CT2W(ext), NULL, NULL);
        
        DWORD dwSize = 0;
        qa->GetString(ASSOCF_NOTRUNCATE, ASSOCSTR_FRIENDLYDOCNAME,
          NULL, NULL, &dwSize);
        CStringW typeNameW;
        qa->GetString(ASSOCF_NOTRUNCATE, ASSOCSTR_FRIENDLYDOCNAME,
          NULL, typeNameW.GetBufferSetLength(dwSize), &dwSize);
        typeNameW.ReleaseBuffer();
        
        if(typeNameW.GetLength() == 0)
          continue;
        
        CString typeName = CW2T(typeNameW);
        if(prevTypeName == typeName)
        {
          filters += _T(";*") + ext;
        }
        else
        {
          if(prevTypeName.GetLength())
            _tprintf(_T("%s (%s)\n"), prevTypeName, filters);
          prevTypeName = typeName;
          filters = _T("*") + ext;
        }
      }
      if(prevTypeName.GetLength())
        _tprintf(_T("%s (%s)\n"), prevTypeName, filters);
    }
    regKey.Close();
  }
  return 0;
}

これで表示される拡張子一覧の例:

GIF イメージ (*.gif)
PNG イメージ (*.png)
LizardTech DjVu File (*.djvu;*.djv;*.sdjvu;*.sdjv)
ビットマップ イメージ (*.bmp;*.dib)
ペイントブラシの絵 (*.rle)
JPEG イメージ (*.jpeg;*.jpe;*.jpg;*.jfif)
EXIF ファイル (*.exif)
高画質の写真 (*.wdp)
TIFF イメージ (*.tiff;*.tif)
アイコン (*.ico)
ICON ファイル (*.icon)