Как получить IPreviewHandler для расширения файла?

Как я могу получить оболочку IPreviewHandler для конкретного расширения файла?

Фон

Windows позволяет разработчикам создавать обработчик предварительного просмотра для своих пользовательских типов файлов:

Обработчики предварительного просмотра вызываются, когда элемент выбран, чтобы показать облегченный предварительный просмотр содержимого файла только для чтения в области чтения представления. Это делается без запуска приложения, связанного с файлом.

Обработчик предварительного просмотра - это размещенное приложение. В число хостов входит Windows Explorer в Windows Vista или Microsoft Outlook 2007.

Я хочу использовать существующие IPreviewHandler инфраструктура, чтобы получить эскиз для файла.

В потоке

Проблема в том, что мои файлы не находятся в пространстве имен оболочки (т.е. они не находятся на жестком диске). Они сидят в памяти, доступны через IStream, Это означает, что я не могу использовать наследие IExtractImage интерфейс; так как он не поддерживает загрузку файла из потока.

К счастью, именно поэтому современный IPreviewHandler поддерживает (рекомендует и предпочитает) загрузку данных из Stream и рекомендует не загружать превью из файла:

Этот метод предпочтителен для инициализации из-за его способности использовать потоки, которые не доступны через путь Win32, например содержимое сжатого файла с расширением имени файла.zip.

Так как я могу получить это?

Нет документации о том, как правильно овладеть IPreviewHandler связано с конкретным расширением. Но если я возьму указания о том, как зарегистрировать IPreviewHandler и прочитайте договор с другой стороны:

HKEY_CLASSES_ROOT
  .xyz
     (Default) = xyzfile

HKEY_CLASSES_ROOT
   xyzfile
      shellex
         {8895b1c6-b41f-4c1c-a562-0d564250836f} //IPreviewHandler subkey
             (Default) = [clsid of the IPreviewHandler]

Я должен быть в состоянии следовать по тому же маршруту, учитывая, что я знаю расширение. Давайте следовать этому на примере реального мира, .jpg файл:

введите описание изображения здесь

введите описание изображения здесь

Обратите внимание, что файл имеет предварительный просмотр. Обратите внимание, что я включил второй скриншот только для того, чтобы подчеркнуть, что предварительный просмотр не происходит из файла, который находится на жестком диске.

Позволяет получить орфографию!

Во-первых, это тот факт, что это .jpg файл:

HKEY_CLASSES_ROOT
   .jpg
      (Default) = ACDC_JPG

HKEY_CLASSES_ROOT
   ACDC_JPG
      ShellEx
         {BB2E617C-0920-11d1-9A0B-00C04FC2D6C1}
         ContextMenuHandlers

Подожди, нет {8895b1c6-b41f-4c1c-a562-0d564250836f} подключ для предварительного просмотра. Это должно означать, что мы не можем получить эскиз для .jpg файлы.

редуктор абсурд

Настоящий вопрос

Внимательный читатель поймет, что фактический вопрос, который я задаю:

Как получить предварительный просмотр изображения, содержащегося только в потоке?

И хотя это полезный вопрос, и реальная проблема у меня есть, имея ответ о том, как использовать IPreviewHandler тоже полезный вопрос.

Так что не стесняйтесь отвечать; или оба!

Бонус Чтение

1 ответ

Решение

У @hvd был правильный ответ.

Типы файлов имеют ключ ShellEx, с {guid} подразделы. каждый {guid} ключ представляет конкретный InterfaceID.

Существует ряд стандартных интерфейсов оболочки, которые могут быть связаны с типом файла:

  • {BB2E617C-0920-11d1-9A0B-00C04FC2D6C1} IExtractImage
  • {953BB1EE-93B4-11d1-98A3-00C04FB687DA} IExtractImage2
  • {e357fccd-a995-4576-b01f-234630154e96} IThumbnailProvider
  • {8895b1c6-b41f-4c1c-a562-0d564250836f} IPreviewHandler

Неподдерживаемое написание недокументированных ключей реестра

Если я хочу найти, например, clsid IPreviewHandler, связанный с .jpg файл, я бы посмотрел в:

HKEY_CLASSES_ROOT/.jpg/ShellEx/{8895b1c6-b41f-4c1c-a562-0d564250836f}
   (default) = [clsid]

Но это не единственное место, где я мог посмотреть. Я также могу посмотреть в:

HKEY_CLASSES_ROOT/.jpg
   (default) = jpgfile
HKEY_CLASSES_ROOT/jpgfile/ShellEx/{8895b1c6-b41f-4c1c-a562-0d564250836f}
   (default) = [clsid]

Но это не единственное место, где я мог посмотреть. Я также могу посмотреть в:

HKEY_CLASSES_ROOT/SystemFileAssociations/.jpg/ShellEx/{8895b1c6-b41f-4c1c-a562-0d564250836f}
   (default) = [clsid] 

Но это не единственное место, где я мог посмотреть. Я также могу посмотреть в:

HKEY_CLASSES_ROOT/SystemFileAssociations/jpegfile/ShellEx/{8895b1c6-b41f-4c1c-a562-0d564250836f}
   (default) = [clsid]

Но это не единственное место, где я мог посмотреть. Если я думаю, что файл является изображением, я также могу посмотреть:

HKEY_CLASSES_ROOT/SystemFileAssociations/image/ShellEx/{8895b1c6-b41f-4c1c-a562-0d564250836f}
   (default) = [clsid]

Как я нашел эти места? Я только следовал за зарегистрированными и поддерживаемыми местоположениями? Нет, я следил за Проводником, используя Process Monitor, так как он охотился за IThumbnailProvider.

Не используйте недокументированные заклинания

Так что теперь я хочу использовать стандартный интерфейс оболочки для типа файла самостоятельно. Это означает, что я должен сканировать места. Но зачем сканировать эти местоположения без документов, без поддержки. Зачем навлекать на себя гнев парня с высоты? Используйте AssocQueryString:

Guid GetShellClsidForFileType(String fileExtension, Guid interfaceID)
{
    //E.g.:
    //   String fileExtension = ".jpg"
    //   Guid   interfaceID   = "{8895b1c6-b41f-4c1c-a562-0d564250836f}"; //IExtractImage

    //The interface we're after - in string form
    String szInterfaceID := GuidToString(interfaceID);

    //Buffer to receive the clsid string
    DWORD bufferSize := 1024; //more than enough to hold a 38-character clsid
    String buffer;
    SetLength(buffer, bufferSize);

    HRESULT hr := AssocQueryString(
          ASSOCF_INIT_DEFAULTTOSTAR, 
          ASSOCSTR_SHELLEXTENSION, //for finding shell extensions
          fileExtension, //e.g. ".txt"
          szInterfaceID, //e.g. "{8895b1c6-b41f-4c1c-a562-0d564250836f}"
          buffer,        //will receive the clsid string
          @bufferSize);
   if (hr <> S_OK) 
      return Guid.Empty;

   Guid clsid;
   HRESULT hr = CLSIDFromString(buffer, out clsid);
   if (hr <> NOERROR) 
      return Guid.Empty;

   return clsid;
}

И так, чтобы получить clsid из IPreviewHandler за .xps файлы:

Guid clsid = GetShellClsidForFileType(".xps", IPreviewHandler);

Как получить IPreviewHandler для расширения файла?

Со всем вышеизложенным, теперь мы можем ответить на вопрос:

IPreviewHandler GetPreviewHandlerForFileType(String extension)
{
    //Extension: the file type to return IPreviewHandler for (e.g. ".xps")
    Guid previewHandlerClassID = GetShellClsidForFileType(extension, IPreviewHandler);

    //Create the COM object
    IUnknown unk = CreateComObject(previewHandlerClassID);

    //Return the actual IPreviewHanler interface (not IUnknown)
    return (IPreviewhandler)unk;
}
Другие вопросы по тегам