Как получить 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;
}