Создание обработчиков эскизов
Начиная с Windows Vista, более активно используются эскизные изображения файлов, чем в более ранних версиях Windows. Они используются во всех представлениях, в диалоговых окнах и для любого типа файла, предоставляющего их. Отображение эскизов также было изменено. Непрерывный спектр выбираемых пользователем размеров доступен вместо дискретных размеров, таких как иконки и миниатюры.
Интерфейс IThumbnailProvider упрощает предоставление эскизов по сравнению со старой версией IExtractImage или IExtractImage2. Обратите внимание, что существующий код, использующий IExtractImage или IExtractImage2, по-прежнему действителен и поддерживается.
Пример RecipeThumbnailProvider
Пример RecipeThumbnailProvider, рассмотренный в этом разделе, включен в набор средств разработки программного обеспечения (SDK) для Windows. Расположение установки по умолчанию — C:\Program Files\Microsoft SDKs\Windows\v6.0\Samples\WinUI\Shell\AppShellIntegration\RecipeThumbnailProvider. Однако основная часть кода также включена здесь.
Пример RecipeThumbnailProvider демонстрирует реализацию обработчика эскизов для нового типа файла, зарегистрированного с расширением ".recipe". В этом примере показано использование различных API-интерфейсов обработчика эскизов для регистрации серверов извлечения эскизов объектной модели компонентов (COM) для пользовательских типов файлов. В этом разделе описывается пример кода, выделение вариантов написания кода и рекомендаций.
Обработчик эскизов должен всегда реализовывать IThumbnailProvider совместно с одним из следующих интерфейсов:
Существуют случаи, когда инициализация с потоками невозможна. В сценариях, когда обработчик эскизов не реализует IInitializeWithStream, ему следует отказаться от работы в изолированном процессе, куда системный индексатор помещает его по умолчанию при изменении потока. Чтобы отказаться от функции изоляции процесса, задайте следующее значение реестра.
HKEY_CLASSES_ROOT
CLSID
{The CLSID of your thumbnail handler}
DisableProcessIsolation = 1
Если вы реализуете IInitializeWithStream и выполняете инициализацию на основе потоков, обработчик является более безопасным и надежным. Как правило, отключение изоляции процессов предназначено только для устаревших обработчиков; избегайте отключения этой функции для любого нового кода. IInitializeWithStream должен быть первым выбором интерфейса инициализации по возможности.
Так как файл изображения в образце не внедрен в файл рецепта и не является частью его потока файлов, в примере используется IInitializeWithItem. Реализация метода IInitializeWithItem::Initialize просто передает свои параметры переменным частного класса.
IThumbnailProvider имеет только один метод—GetThumbnail, который вызывается с наибольшим требуемым размером изображения в пикселях. Хотя параметр называется cx, его значение используется как максимальный размер размера изображения x и y. Если извлеченный эскиз не равен квадрату, то длинная ось ограничена cx, а пропорции исходного изображения сохраняются.
При возвращении GetThumbnail предоставляет дескриптор извлеченного изображения. Он также предоставляет значение, указывающее цветной формат изображения и наличие допустимых альфа-данных.
Реализация GetThumbnail в примере начинается с вызова частного метода _GetBase64EncodedImageString.
IFACEMETHODIMP CRecipeThumbProvider::GetThumbnail(UINT cx,
HBITMAP *phbmp,
WTS_ALPHATYPE *pdwAlpha)
{
PWSTR pszBase64EncodedImageString;
HRESULT hr = _GetBase64EncodedImageString(cx, &pszBase64EncodedImageString);
Тип файла рецепта — это просто XML-файл, зарегистрированный в качестве уникального расширения имени файла. Он содержит элемент с именем Picture, который предоставляет относительный путь и имя файла изображения, используемого в качестве эскиза для этого конкретного файла рецепта. Элемент Picture состоит из атрибута Source, указывающего базовый 64-кодированный образ и необязательный атрибут Size.
Размер имеет два значения, Small и Large. Это позволяет предоставить несколько узлов picture с отдельными изображениями. Полученное изображение зависит от максимального размера (cx) в вызове GetThumbnail. Поскольку Windows никогда не увеличивает изображение больше его максимального размера, для разных разрешений могут быть предоставлены разные изображения. Однако для простоты пример опускает атрибут Size и предоставляет только одно изображение для всех ситуаций.
Метод _GetBase64EncodedImageString, реализация которого показана здесь, использует API объектной модели XML-документа (DOM), чтобы получить узел Picture. Из этого узла он извлекает изображение из данных атрибута источника.
HRESULT CRecipeThumbProvider::_GetBase64EncodedImageString(UINT /* cx */,
PWSTR *ppszResult)
{
*ppszResult = NULL;
IXMLDOMDocument *pXMLDoc;
HRESULT hr = _LoadXMLDocument(&pXMLDoc);
if (SUCCEEDED(hr))
{
BSTR bstrQuery = SysAllocString(L"Recipe/Attachments/Picture");
hr = bstrQuery ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hr))
{
IXMLDOMNode *pXMLNode;
hr = pXMLDoc->selectSingleNode(bstrQuery, &pXMLNode);
if (SUCCEEDED(hr))
{
IXMLDOMElement *pXMLElement;
hr = pXMLNode->QueryInterface(&pXMLElement);
if (SUCCEEDED(hr))
{
BSTR bstrAttribute = SysAllocString(L"Source");
hr = bstrAttribute ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hr))
{
VARIANT varValue;
hr = pXMLElement->getAttribute(bstrAttribute, &varValue);
if (SUCCEEDED(hr))
{
if ((varValue.vt == VT_BSTR) && varValue.bstrVal && varValue.bstrVal[0])
{
hr = SHStrDupW(varValue.bstrVal, ppszResult);
}
else
{
hr = E_FAIL;
}
VariantClear(&varValue);
}
SysFreeString(bstrAttribute);
}
pXMLElement->Release();
}
pXMLNode->Release();
}
SysFreeString(bstrQuery);
}
pXMLDoc->Release();
}
return hr;
}
GetThumbnail затем передает полученную строку _GetStreamFromString.
IFACEMETHODIMP CRecipeThumbProvider::GetThumbnail(UINT cx,
HBITMAP *phbmp,
WTS_ALPHATYPE *pdwAlpha)
{
PWSTR pszBase64EncodedImageString;
HRESULT hr = _GetBase64EncodedImageString(cx, &pszBase64EncodedImageString);
if (SUCCEEDED(hr))
{
IStream *pImageStream;
hr = _GetStreamFromString(pszBase64EncodedImageString, &pImageStream);
Метод _GetStreamFromString, реализация которого показана здесь, которая преобразует закодированное изображение в поток.
HRESULT CRecipeThumbProvider::_GetStreamFromString(PCWSTR pszImageName,
IStream **ppImageStream)
{
HRESULT hr = E_FAIL;
DWORD dwDecodedImageSize = 0;
DWORD dwSkipChars = 0;
DWORD dwActualFormat = 0;
// Base64-decode the string
BOOL fSuccess = CryptStringToBinaryW(pszImageName,
NULL,
CRYPT_STRING_BASE64,
NULL,
&dwDecodedImageSize,
&dwSkipChars,
&dwActualFormat);
if (fSuccess)
{
BYTE *pbDecodedImage = (BYTE*)LocalAlloc(LPTR, dwDecodedImageSize);
if (pbDecodedImage)
{
fSuccess = CryptStringToBinaryW(pszImageName,
lstrlenW(pszImageName),
CRYPT_STRING_BASE64,
pbDecodedImage,
&dwDecodedImageSize,
&dwSkipChars,
&dwActualFormat);
if (fSuccess)
{
*ppImageStream = SHCreateMemStream(pbDecodedImage,
dwDecodedImageSize);
if (*ppImageStream != NULL)
{
hr = S_OK;
}
}
LocalFree(pbDecodedImage);
}
}
return hr;
}
GetThumbnail затем использует API компонента образов Windows (WIC) для извлечения растрового изображения из потока и получения дескриптора для этого растрового изображения. Альфа-информация задана, WIC завершается правильно, и метод завершается успешно.
IFACEMETHODIMP CRecipeThumbProvider::GetThumbnail(UINT cx,
HBITMAP *phbmp,
WTS_ALPHATYPE *pdwAlpha)
{
PWSTR pszBase64EncodedImageString;
HRESULT hr = _GetBase64EncodedImageString(cx, &pszBase64EncodedImageString);
if (SUCCEEDED(hr))
{
IStream *pImageStream;
hr = _GetStreamFromString(pszBase64EncodedImageString, &pImageStream);
if (SUCCEEDED(hr))
{
hr = WICCreate32BitsPerPixelHBITMAP(pImageStream,
cx,
phbmp,
pdwAlpha);
pImageStream->Release();
}
CoTaskMemFree(pszBase64EncodedImageString);
}
return hr;
}