// ************************************************************************** // * This file is part of the FreeFileSync project. It is distributed under * // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** #include "thumbnail.h" #include #define WIN32_LEAN_AND_MEAN #include #define STRICT_TYPED_ITEMIDS //better type safety for IDLists #include #include #include #include #include #include using namespace zen; thumb::HICON thumb::getThumbnail(const wchar_t* filename, int requestedSize) //return 0 on failure, caller takes ownership! { const std::wstring filenameStr(filename); ComPtr desktopFolder; { HRESULT hr = ::SHGetDesktopFolder(desktopFolder.init()); if (FAILED(hr) || !desktopFolder) return nullptr; } PIDLIST_RELATIVE pidlFolder = nullptr; { const std::wstring& pathName = beforeLast(filenameStr, L'\\'); HRESULT hr = desktopFolder->ParseDisplayName(nullptr, // [in] HWND hwnd, nullptr, // [in] IBindCtx *pbc, const_cast(pathName.c_str()), // [in] LPWSTR pszDisplayName, nullptr, // [out] ULONG *pchEaten, &pidlFolder, // [out] PIDLIST_RELATIVE* ppidl, nullptr); // [in, out] ULONG *pdwAttributes if (FAILED(hr) || !pidlFolder) return nullptr; } ZEN_ON_SCOPE_EXIT(::ILFree(pidlFolder)); //older version: ::CoTaskMemFree ComPtr imageFolder; { HRESULT hr = desktopFolder->BindToObject(pidlFolder, // [in] PCUIDLIST_RELATIVE pidl, nullptr, // [in] IBindCtx *pbc, IID_PPV_ARGS(imageFolder.init())); if (FAILED(hr) || !imageFolder) return nullptr; } PIDLIST_RELATIVE pidImage = nullptr; { const std::wstring& shortName = afterLast(filenameStr, L'\\'); HRESULT hr = imageFolder->ParseDisplayName(nullptr, // [in] HWND hwnd, nullptr, // [in] IBindCtx *pbc, const_cast(shortName.c_str()), // [in] LPWSTR pszDisplayName, nullptr, // [out] ULONG *pchEaten, &pidImage, // [out] PIDLIST_RELATIVE *ppidl, nullptr); // [in, out] ULONG *pdwAttributes if (FAILED(hr) || !pidImage) return nullptr; } ZEN_ON_SCOPE_EXIT(::ILFree(pidImage)); //older version: ::CoTaskMemFree ComPtr extractImage; { PCUITEMID_CHILD_ARRAY pidlIn = reinterpret_cast(&pidImage); //this is where STRICT_TYPED_ITEMIDS gets us ;) HRESULT hr = imageFolder->GetUIObjectOf(nullptr, // [in] HWND hwndOwner, 1, // [in] UINT cidl, pidlIn, // [in] PCUITEMID_CHILD_ARRAY apidl, IID_IExtractImage, // [in] REFIID riid, nullptr, // [in, out] UINT *rgfReserved, reinterpret_cast(extractImage.init())); // [out] void **ppv if (FAILED(hr) || !extractImage) return nullptr; } { wchar_t pathBuffer[MAX_PATH]; DWORD priority = 0; const SIZE prgSize = { requestedSize, requestedSize }; //preferred size only! DWORD clrDepth = 32; DWORD flags = IEIFLAG_SCREEN | IEIFLAG_OFFLINE; HRESULT hr = extractImage->GetLocation(pathBuffer, // [out] LPWSTR pszPathBuffer, MAX_PATH, // [in] DWORD cchMax, &priority, // [out] DWORD *pdwPriority, &prgSize, // [in] const SIZE *prgSize, clrDepth, // [in] DWORD dwRecClrDepth, &flags); // [in, out] DWORD *pdwFlags if (FAILED(hr)) return nullptr; } HBITMAP bitmap = nullptr; { HRESULT hr = extractImage->Extract(&bitmap); if (FAILED(hr) || !bitmap) return nullptr; } ZEN_ON_SCOPE_EXIT(::DeleteObject(bitmap)); BITMAP bmpInfo = {}; if (::GetObject(bitmap, //__in HGDIOBJ hgdiobj, sizeof(bmpInfo), //__in int cbBuffer, &bmpInfo) == 0) //__out LPVOID lpvObject return nullptr; HDC hDC = ::GetDC(nullptr); if (!hDC) return nullptr; ZEN_ON_SCOPE_EXIT(::ReleaseDC(nullptr, hDC)); HBITMAP bitmapMask = ::CreateCompatibleBitmap(hDC, bmpInfo.bmWidth, bmpInfo.bmHeight); if (!bitmapMask) return nullptr; ZEN_ON_SCOPE_EXIT(::DeleteObject(bitmapMask)); ICONINFO iconInfo = {}; iconInfo.fIcon = true; iconInfo.hbmColor = bitmap; iconInfo.hbmMask = bitmapMask; return ::CreateIconIndirect(&iconInfo); } thumb::HICON thumb::getIconByIndex(int iconIndex, int shilIconType) //return 0 on failure, caller takes ownership! { //Note: using IExtractIcon::Extract is *no* alternative, just as ::SHGetFileInfo(), it only supports small (16x16) and large (32x32) icons ComPtr imageList; //perf: 0,12 µs only to get the image list { HRESULT hr = ::SHGetImageList(shilIconType, //__in int iImageList, IID_PPV_ARGS(imageList.init())); if (FAILED(hr) || !imageList) return nullptr; } bool hasAlpha = false; //perf: 0,14 µs { DWORD flags = 0; HRESULT hr = imageList->GetItemFlags(iconIndex, //[in] int i, &flags); //[out] DWORD *dwFlags if (SUCCEEDED(hr)) hasAlpha = flags & ILIF_ALPHA; } ::HICON hIcon = nullptr; //perf: 1,5 ms - the dominant block { HRESULT hr = imageList->GetIcon(iconIndex, // [in] int i, (hasAlpha ? ILD_IMAGE : ILD_NORMAL), // [in] UINT flags, //ILD_IMAGE -> do not draw mask - fixes glitch with ILD_NORMAL where both mask *and* alpha channel are applied, e.g. for ffs_batch and folder icon //other flags: http://msdn.microsoft.com/en-us/library/windows/desktop/bb775230(v=vs.85).aspx &hIcon); // [out] HICON *picon if (FAILED(hr) || !hIcon) return nullptr; } return hIcon; }