├── .gitignore ├── Setup ├── icon.ico ├── Product.wxs └── Setup.wixproj ├── Test ├── test.psd ├── test2.psd ├── test3.psd ├── test4.psd ├── test5.psd ├── test6.psd ├── test7.psb ├── Test.vcxproj.filters ├── main.cpp └── Test.vcxproj ├── PsdThumbnailProvider ├── GetThumbnail.h ├── GlobalExportFunctions.def ├── ClassFactory.h ├── PsdThumbnailProvider.h ├── dllmain.cpp ├── PsdThumbnailProvider.cpp ├── ClassFactory.cpp ├── PsdThumbnailProvider.vcxproj.filters ├── PsdThumbnailProvider.vcxproj └── GetThumbnail.cpp ├── README.md ├── LICENSE ├── PsdThumbnailProvider.sln └── pdf.svg /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | Debug 3 | Release 4 | .vs 5 | *.sdf 6 | *.vcxproj.user 7 | -------------------------------------------------------------------------------- /Setup/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Agamnentzar/psd-thumbnail-provider/HEAD/Setup/icon.ico -------------------------------------------------------------------------------- /Test/test.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Agamnentzar/psd-thumbnail-provider/HEAD/Test/test.psd -------------------------------------------------------------------------------- /Test/test2.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Agamnentzar/psd-thumbnail-provider/HEAD/Test/test2.psd -------------------------------------------------------------------------------- /Test/test3.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Agamnentzar/psd-thumbnail-provider/HEAD/Test/test3.psd -------------------------------------------------------------------------------- /Test/test4.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Agamnentzar/psd-thumbnail-provider/HEAD/Test/test4.psd -------------------------------------------------------------------------------- /Test/test5.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Agamnentzar/psd-thumbnail-provider/HEAD/Test/test5.psd -------------------------------------------------------------------------------- /Test/test6.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Agamnentzar/psd-thumbnail-provider/HEAD/Test/test6.psd -------------------------------------------------------------------------------- /Test/test7.psb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Agamnentzar/psd-thumbnail-provider/HEAD/Test/test7.psb -------------------------------------------------------------------------------- /PsdThumbnailProvider/GetThumbnail.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #pragma comment(lib, "windowscodecs.lib") 7 | 8 | HBITMAP GetPSDThumbnail(IStream* stream); 9 | -------------------------------------------------------------------------------- /PsdThumbnailProvider/GlobalExportFunctions.def: -------------------------------------------------------------------------------- 1 | LIBRARY "PsdThumbnailProvider" 2 | EXPORTS 3 | DllGetClassObject PRIVATE 4 | DllCanUnloadNow PRIVATE 5 | DllRegisterServer PRIVATE 6 | DllUnregisterServer PRIVATE 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PSD thumbnail provider 2 | 3 | PSD thumbnail provider for Windows Explorer 4 | 5 | [Download latest installer](https://github.com/Agamnentzar/psd-thumbnail-provider/releases) 6 | 7 | ## Supported systems 8 | 9 | * **Windows Vista/7/8/10 x64** 10 | 11 | ## Building 12 | 13 | ### Requirements 14 | 15 | * [Visual Studio](https://www.visualstudio.com/en-us/products/visual-studio-community-vs.aspx) 16 | * [WIX Toolset](http://wixtoolset.org/) for generating setup 17 | -------------------------------------------------------------------------------- /PsdThumbnailProvider/ClassFactory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // For IClassFactory 4 | #include 5 | 6 | class ClassFactory : public IClassFactory { 7 | public: 8 | // IUnknown 9 | IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv); 10 | IFACEMETHODIMP_(ULONG) AddRef(); 11 | IFACEMETHODIMP_(ULONG) Release(); 12 | 13 | // IClassFactory 14 | IFACEMETHODIMP CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppv); 15 | IFACEMETHODIMP LockServer(BOOL fLock); 16 | 17 | ClassFactory(); 18 | 19 | protected: 20 | ~ClassFactory(); 21 | 22 | private: 23 | long m_cRef; 24 | }; 25 | -------------------------------------------------------------------------------- /PsdThumbnailProvider/PsdThumbnailProvider.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #pragma comment(lib, "windowscodecs.lib") 8 | 9 | class PsdThumbnailProvider : 10 | public IInitializeWithStream, 11 | public IThumbnailProvider { 12 | public: 13 | // IUnknown 14 | IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv); 15 | IFACEMETHODIMP_(ULONG) AddRef(); 16 | IFACEMETHODIMP_(ULONG) Release(); 17 | 18 | // IInitializeWithStream 19 | IFACEMETHODIMP Initialize(IStream *pStream, DWORD grfMode); 20 | 21 | // IThumbnailProvider 22 | IFACEMETHODIMP GetThumbnail(UINT cx, HBITMAP *phbmp, WTS_ALPHATYPE *pdwAlpha); 23 | 24 | PsdThumbnailProvider(); 25 | 26 | protected: 27 | ~PsdThumbnailProvider(); 28 | 29 | private: 30 | long m_cRef; 31 | IStream *m_pStream; 32 | }; 33 | -------------------------------------------------------------------------------- /Test/Test.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Agamnentzar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /PsdThumbnailProvider/dllmain.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include // For SHChangeNotify 4 | #include "ClassFactory.h" // For the class factory 5 | 6 | // {5BB45D32-AF01-414F-B60A-5E999B986681} 7 | const CLSID CLSID_RecipeThumbnailProvider = 8 | { 0x5bb45d32, 0xaf01, 0x414f, { 0xb6, 0xa, 0x5e, 0x99, 0x9b, 0x98, 0x66, 0x81 } }; 9 | 10 | HINSTANCE g_hInst = NULL; 11 | long g_cDllRef = 0; 12 | 13 | BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved) { 14 | switch (dwReason) { 15 | case DLL_PROCESS_ATTACH: 16 | // Hold the instance of this DLL module, we will use it to get the 17 | // path of the DLL to register the component. 18 | g_hInst = hModule; 19 | DisableThreadLibraryCalls(hModule); 20 | break; 21 | case DLL_THREAD_ATTACH: 22 | case DLL_THREAD_DETACH: 23 | case DLL_PROCESS_DETACH: 24 | break; 25 | } 26 | return TRUE; 27 | } 28 | 29 | STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void **ppv) { 30 | HRESULT hr = CLASS_E_CLASSNOTAVAILABLE; 31 | 32 | if (IsEqualCLSID(CLSID_RecipeThumbnailProvider, rclsid)) { 33 | hr = E_OUTOFMEMORY; 34 | 35 | ClassFactory *pClassFactory = new ClassFactory(); 36 | if (pClassFactory) { 37 | hr = pClassFactory->QueryInterface(riid, ppv); 38 | pClassFactory->Release(); 39 | } 40 | } 41 | 42 | return hr; 43 | } 44 | 45 | STDAPI DllCanUnloadNow(void) { 46 | return g_cDllRef > 0 ? S_FALSE : S_OK; 47 | } 48 | 49 | STDAPI DllRegisterServer(void) { 50 | return S_OK; 51 | } 52 | 53 | STDAPI DllUnregisterServer(void) { 54 | return S_OK; 55 | } 56 | -------------------------------------------------------------------------------- /PsdThumbnailProvider/PsdThumbnailProvider.cpp: -------------------------------------------------------------------------------- 1 | #include "PsdThumbnailProvider.h" 2 | #include "GetThumbnail.h" 3 | #include 4 | 5 | #pragma comment(lib, "Shlwapi.lib") 6 | 7 | extern HINSTANCE g_hInst; 8 | extern long g_cDllRef; 9 | 10 | PsdThumbnailProvider::PsdThumbnailProvider() : m_cRef(1), m_pStream(NULL) { 11 | InterlockedIncrement(&g_cDllRef); 12 | } 13 | 14 | PsdThumbnailProvider::~PsdThumbnailProvider() { 15 | InterlockedDecrement(&g_cDllRef); 16 | } 17 | 18 | // IUnknown 19 | 20 | IFACEMETHODIMP PsdThumbnailProvider::QueryInterface(REFIID riid, void **ppv) { 21 | static const QITAB qit[] = 22 | { 23 | QITABENT(PsdThumbnailProvider, IThumbnailProvider), 24 | QITABENT(PsdThumbnailProvider, IInitializeWithStream), 25 | { 0 }, 26 | }; 27 | return QISearch(this, qit, riid, ppv); 28 | } 29 | 30 | IFACEMETHODIMP_(ULONG) PsdThumbnailProvider::AddRef() { 31 | return InterlockedIncrement(&m_cRef); 32 | } 33 | 34 | IFACEMETHODIMP_(ULONG) PsdThumbnailProvider::Release() { 35 | ULONG cRef = InterlockedDecrement(&m_cRef); 36 | 37 | if (0 == cRef) 38 | delete this; 39 | 40 | return cRef; 41 | } 42 | 43 | // IInitializeWithStream 44 | 45 | IFACEMETHODIMP PsdThumbnailProvider::Initialize(IStream *pStream, DWORD grfMode) { 46 | HRESULT hr = HRESULT_FROM_WIN32(ERROR_ALREADY_INITIALIZED); 47 | 48 | if (m_pStream == NULL) 49 | hr = pStream->QueryInterface(&m_pStream); 50 | 51 | return hr; 52 | } 53 | 54 | // IThumbnailProvider 55 | 56 | IFACEMETHODIMP PsdThumbnailProvider::GetThumbnail(UINT cx, HBITMAP *phbmp, WTS_ALPHATYPE *pdwAlpha) { 57 | *pdwAlpha = WTSAT_ARGB; 58 | *phbmp = GetPSDThumbnail(m_pStream); 59 | 60 | m_pStream->Release(); 61 | 62 | return *phbmp != NULL ? NOERROR : E_NOTIMPL; 63 | } 64 | -------------------------------------------------------------------------------- /PsdThumbnailProvider/ClassFactory.cpp: -------------------------------------------------------------------------------- 1 | #include "ClassFactory.h" 2 | #include "PsdThumbnailProvider.h" 3 | #include 4 | #include 5 | #pragma comment(lib, "shlwapi.lib") 6 | 7 | extern long g_cDllRef; 8 | 9 | ClassFactory::ClassFactory() : m_cRef(1) { 10 | InterlockedIncrement(&g_cDllRef); 11 | } 12 | 13 | ClassFactory::~ClassFactory() { 14 | InterlockedDecrement(&g_cDllRef); 15 | } 16 | 17 | // IUnknown 18 | 19 | IFACEMETHODIMP ClassFactory::QueryInterface(REFIID riid, void **ppv) { 20 | static const QITAB qit[] = 21 | { 22 | QITABENT(ClassFactory, IClassFactory), 23 | { 0 }, 24 | }; 25 | return QISearch(this, qit, riid, ppv); 26 | } 27 | 28 | IFACEMETHODIMP_(ULONG) ClassFactory::AddRef() { 29 | return InterlockedIncrement(&m_cRef); 30 | } 31 | 32 | IFACEMETHODIMP_(ULONG) ClassFactory::Release() { 33 | ULONG cRef = InterlockedDecrement(&m_cRef); 34 | if (0 == cRef) { 35 | delete this; 36 | } 37 | return cRef; 38 | } 39 | 40 | // IClassFactory 41 | 42 | IFACEMETHODIMP ClassFactory::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppv) { 43 | HRESULT hr = CLASS_E_NOAGGREGATION; 44 | 45 | // pUnkOuter is used for aggregation. We do not support it in the sample. 46 | if (pUnkOuter == NULL) { 47 | hr = E_OUTOFMEMORY; 48 | 49 | // Create the COM component. 50 | PsdThumbnailProvider *pExt = new (std::nothrow) PsdThumbnailProvider(); 51 | 52 | if (pExt) { 53 | // Query the specified interface. 54 | hr = pExt->QueryInterface(riid, ppv); 55 | pExt->Release(); 56 | } 57 | } 58 | 59 | return hr; 60 | } 61 | 62 | IFACEMETHODIMP ClassFactory::LockServer(BOOL fLock) { 63 | if (fLock) { 64 | InterlockedIncrement(&g_cDllRef); 65 | } else { 66 | InterlockedDecrement(&g_cDllRef); 67 | } 68 | return S_OK; 69 | } 70 | -------------------------------------------------------------------------------- /PsdThumbnailProvider/PsdThumbnailProvider.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | 26 | 27 | Header Files 28 | 29 | 30 | Header Files 31 | 32 | 33 | 34 | 35 | Source Files 36 | 37 | 38 | Source Files 39 | 40 | 41 | Source Files 42 | 43 | 44 | Source Files 45 | 46 | 47 | -------------------------------------------------------------------------------- /Setup/Product.wxs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Privileged 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /Setup/Setup.wixproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | x86 6 | 3.5 7 | ea1c995b-28a6-4036-911b-f83b361ac895 8 | 2.0 9 | PsdThumbnailProvider 10 | Package 11 | $(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets 12 | $(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.targets 13 | PsdThumbnailProviderSetup 14 | 15 | 16 | bin\$(Configuration)\ 17 | obj\$(Configuration)\ 18 | Debug 19 | 20 | 21 | bin\$(Configuration)\ 22 | obj\$(Configuration)\ 23 | 24 | 25 | Debug 26 | bin\$(Platform)\$(Configuration)\ 27 | obj\$(Platform)\$(Configuration)\ 28 | 29 | 30 | bin\$(Platform)\$(Configuration)\ 31 | obj\$(Platform)\$(Configuration)\ 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 48 | -------------------------------------------------------------------------------- /Test/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../PsdThumbnailProvider/GetThumbnail.cpp" 3 | 4 | static HBITMAP bitmap = NULL; 5 | 6 | LRESULT CALLBACK windowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { 7 | LRESULT result = 0; 8 | 9 | switch (message) { 10 | case WM_CREATE: { 11 | IStream* stream; 12 | SHCreateStreamOnFileEx(L"test7.psb", STGM_READ, FILE_ATTRIBUTE_NORMAL, false, NULL, &stream); 13 | bitmap = GetPSDThumbnail(stream); 14 | BITMAP bm = {}; 15 | GetObject(bitmap, sizeof(bm), &bm); 16 | RECT rc, rcClient; 17 | GetWindowRect(hWnd, &rc); 18 | GetClientRect(hWnd, &rcClient); 19 | int xExtra = rc.right - rc.left - rcClient.right; 20 | int yExtra = rc.bottom - rc.top - rcClient.bottom; 21 | SetWindowPos(hWnd, NULL, 0, 0, bm.bmWidth + xExtra, bm.bmHeight + yExtra, SWP_NOMOVE); 22 | break; 23 | } 24 | case WM_ACTIVATEAPP: 25 | break; 26 | case WM_SIZE: 27 | break; 28 | case WM_DESTROY: 29 | PostQuitMessage(0); 30 | break; 31 | case WM_CLOSE: 32 | PostQuitMessage(0); 33 | break; 34 | case WM_PAINT: { 35 | RECT rect; 36 | GetWindowRect(hWnd, &rect); 37 | rect.right -= rect.left; 38 | rect.bottom -= rect.top; 39 | rect.left = 0; 40 | rect.top = 0; 41 | PAINTSTRUCT ps; 42 | HDC hdc = BeginPaint(hWnd, &ps); 43 | HDC hdcMem = CreateCompatibleDC(hdc); 44 | HBRUSH bgBrush = CreateSolidBrush(RGB(0, 0, 0)); 45 | FillRect(hdc, &rect, bgBrush); 46 | BITMAP bm = {}; 47 | GetObject(bitmap, sizeof(BITMAP), &bm); 48 | SelectObject(hdcMem, bitmap); 49 | BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY); 50 | DeleteObject(bgBrush); 51 | DeleteDC(hdcMem); 52 | EndPaint(hWnd, &ps); 53 | break; 54 | } 55 | default: 56 | result = DefWindowProc(hWnd, message, wParam, lParam); 57 | break; 58 | } 59 | 60 | return result; 61 | } 62 | 63 | int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) { 64 | WNDCLASS windowClass = {}; 65 | windowClass.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; 66 | windowClass.lpfnWndProc = windowProc; 67 | windowClass.hInstance = hInstance; 68 | windowClass.hIcon = 0; // TODO 69 | windowClass.lpszClassName = TEXT("aggie-window-class"); 70 | 71 | RegisterClass(&windowClass); 72 | 73 | HWND mainWindow = CreateWindowEx( 74 | 0, // WS_EX_ACCEPTFILES 75 | windowClass.lpszClassName, 76 | TEXT("PSD Preview Test"), 77 | WS_OVERLAPPEDWINDOW | WS_VISIBLE, 78 | CW_USEDEFAULT, 79 | CW_USEDEFAULT, 80 | CW_USEDEFAULT, 81 | CW_USEDEFAULT, 82 | NULL, 83 | NULL, 84 | hInstance, 85 | NULL); 86 | 87 | while (true) { 88 | MSG message; 89 | 90 | while (PeekMessage(&message, 0, 0, 0, PM_REMOVE)) { 91 | if (message.message == WM_QUIT) { 92 | return 0; 93 | } 94 | 95 | TranslateMessage(&message); 96 | DispatchMessage(&message); 97 | } 98 | 99 | Sleep(1); 100 | } 101 | 102 | return 0; 103 | } 104 | -------------------------------------------------------------------------------- /PsdThumbnailProvider.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio 15 3 | VisualStudioVersion = 15.0.28307.168 4 | MinimumVisualStudioVersion = 10.0.40219.1 5 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C9250E0E-3B03-4029-ADF0-0FA8B1AEB70A}" 6 | EndProject 7 | Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "Setup", "Setup\Setup.wixproj", "{EA1C995B-28A6-4036-911B-F83B361AC895}" 8 | EndProject 9 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PsdThumbnailProvider", "PsdThumbnailProvider\PsdThumbnailProvider.vcxproj", "{145AB33E-71A1-42C3-8223-E107CFC162F4}" 10 | EndProject 11 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test", "Test\Test.vcxproj", "{998FCBFC-78A6-4DE9-9F94-6D4762B445D7}" 12 | EndProject 13 | Global 14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 15 | Debug|Any CPU = Debug|Any CPU 16 | Debug|Mixed Platforms = Debug|Mixed Platforms 17 | Debug|Win32 = Debug|Win32 18 | Debug|x64 = Debug|x64 19 | Debug|x86 = Debug|x86 20 | Release|Any CPU = Release|Any CPU 21 | Release|Mixed Platforms = Release|Mixed Platforms 22 | Release|Win32 = Release|Win32 23 | Release|x64 = Release|x64 24 | Release|x86 = Release|x86 25 | EndGlobalSection 26 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 27 | {EA1C995B-28A6-4036-911B-F83B361AC895}.Debug|Any CPU.ActiveCfg = Debug|x86 28 | {EA1C995B-28A6-4036-911B-F83B361AC895}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 29 | {EA1C995B-28A6-4036-911B-F83B361AC895}.Debug|Mixed Platforms.Build.0 = Debug|x86 30 | {EA1C995B-28A6-4036-911B-F83B361AC895}.Debug|Win32.ActiveCfg = Debug|x86 31 | {EA1C995B-28A6-4036-911B-F83B361AC895}.Debug|Win32.Build.0 = Debug|x86 32 | {EA1C995B-28A6-4036-911B-F83B361AC895}.Debug|x64.ActiveCfg = Debug|x64 33 | {EA1C995B-28A6-4036-911B-F83B361AC895}.Debug|x64.Build.0 = Debug|x64 34 | {EA1C995B-28A6-4036-911B-F83B361AC895}.Debug|x86.ActiveCfg = Debug|x86 35 | {EA1C995B-28A6-4036-911B-F83B361AC895}.Debug|x86.Build.0 = Debug|x86 36 | {EA1C995B-28A6-4036-911B-F83B361AC895}.Release|Any CPU.ActiveCfg = Release|x86 37 | {EA1C995B-28A6-4036-911B-F83B361AC895}.Release|Mixed Platforms.ActiveCfg = Release|x86 38 | {EA1C995B-28A6-4036-911B-F83B361AC895}.Release|Mixed Platforms.Build.0 = Release|x86 39 | {EA1C995B-28A6-4036-911B-F83B361AC895}.Release|Win32.ActiveCfg = Release|x86 40 | {EA1C995B-28A6-4036-911B-F83B361AC895}.Release|Win32.Build.0 = Release|x86 41 | {EA1C995B-28A6-4036-911B-F83B361AC895}.Release|x64.ActiveCfg = Release|x64 42 | {EA1C995B-28A6-4036-911B-F83B361AC895}.Release|x64.Build.0 = Release|x64 43 | {EA1C995B-28A6-4036-911B-F83B361AC895}.Release|x86.ActiveCfg = Release|x86 44 | {EA1C995B-28A6-4036-911B-F83B361AC895}.Release|x86.Build.0 = Release|x86 45 | {145AB33E-71A1-42C3-8223-E107CFC162F4}.Debug|Any CPU.ActiveCfg = Debug|Win32 46 | {145AB33E-71A1-42C3-8223-E107CFC162F4}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 47 | {145AB33E-71A1-42C3-8223-E107CFC162F4}.Debug|Mixed Platforms.Build.0 = Debug|Win32 48 | {145AB33E-71A1-42C3-8223-E107CFC162F4}.Debug|Win32.ActiveCfg = Debug|Win32 49 | {145AB33E-71A1-42C3-8223-E107CFC162F4}.Debug|Win32.Build.0 = Debug|Win32 50 | {145AB33E-71A1-42C3-8223-E107CFC162F4}.Debug|x64.ActiveCfg = Debug|x64 51 | {145AB33E-71A1-42C3-8223-E107CFC162F4}.Debug|x64.Build.0 = Debug|x64 52 | {145AB33E-71A1-42C3-8223-E107CFC162F4}.Debug|x86.ActiveCfg = Debug|Win32 53 | {145AB33E-71A1-42C3-8223-E107CFC162F4}.Debug|x86.Build.0 = Debug|Win32 54 | {145AB33E-71A1-42C3-8223-E107CFC162F4}.Release|Any CPU.ActiveCfg = Release|Win32 55 | {145AB33E-71A1-42C3-8223-E107CFC162F4}.Release|Mixed Platforms.ActiveCfg = Release|Win32 56 | {145AB33E-71A1-42C3-8223-E107CFC162F4}.Release|Mixed Platforms.Build.0 = Release|Win32 57 | {145AB33E-71A1-42C3-8223-E107CFC162F4}.Release|Win32.ActiveCfg = Release|Win32 58 | {145AB33E-71A1-42C3-8223-E107CFC162F4}.Release|Win32.Build.0 = Release|Win32 59 | {145AB33E-71A1-42C3-8223-E107CFC162F4}.Release|x64.ActiveCfg = Release|x64 60 | {145AB33E-71A1-42C3-8223-E107CFC162F4}.Release|x64.Build.0 = Release|x64 61 | {145AB33E-71A1-42C3-8223-E107CFC162F4}.Release|x86.ActiveCfg = Release|Win32 62 | {145AB33E-71A1-42C3-8223-E107CFC162F4}.Release|x86.Build.0 = Release|Win32 63 | {998FCBFC-78A6-4DE9-9F94-6D4762B445D7}.Debug|Any CPU.ActiveCfg = Debug|Win32 64 | {998FCBFC-78A6-4DE9-9F94-6D4762B445D7}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 65 | {998FCBFC-78A6-4DE9-9F94-6D4762B445D7}.Debug|Mixed Platforms.Build.0 = Debug|Win32 66 | {998FCBFC-78A6-4DE9-9F94-6D4762B445D7}.Debug|Win32.ActiveCfg = Debug|Win32 67 | {998FCBFC-78A6-4DE9-9F94-6D4762B445D7}.Debug|Win32.Build.0 = Debug|Win32 68 | {998FCBFC-78A6-4DE9-9F94-6D4762B445D7}.Debug|x64.ActiveCfg = Debug|x64 69 | {998FCBFC-78A6-4DE9-9F94-6D4762B445D7}.Debug|x64.Build.0 = Debug|x64 70 | {998FCBFC-78A6-4DE9-9F94-6D4762B445D7}.Debug|x86.ActiveCfg = Debug|Win32 71 | {998FCBFC-78A6-4DE9-9F94-6D4762B445D7}.Debug|x86.Build.0 = Debug|Win32 72 | {998FCBFC-78A6-4DE9-9F94-6D4762B445D7}.Release|Any CPU.ActiveCfg = Release|Win32 73 | {998FCBFC-78A6-4DE9-9F94-6D4762B445D7}.Release|Mixed Platforms.ActiveCfg = Release|Win32 74 | {998FCBFC-78A6-4DE9-9F94-6D4762B445D7}.Release|Mixed Platforms.Build.0 = Release|Win32 75 | {998FCBFC-78A6-4DE9-9F94-6D4762B445D7}.Release|Win32.ActiveCfg = Release|Win32 76 | {998FCBFC-78A6-4DE9-9F94-6D4762B445D7}.Release|Win32.Build.0 = Release|Win32 77 | {998FCBFC-78A6-4DE9-9F94-6D4762B445D7}.Release|x64.ActiveCfg = Debug|x64 78 | {998FCBFC-78A6-4DE9-9F94-6D4762B445D7}.Release|x64.Build.0 = Debug|x64 79 | {998FCBFC-78A6-4DE9-9F94-6D4762B445D7}.Release|x86.ActiveCfg = Release|Win32 80 | {998FCBFC-78A6-4DE9-9F94-6D4762B445D7}.Release|x86.Build.0 = Release|Win32 81 | EndGlobalSection 82 | GlobalSection(SolutionProperties) = preSolution 83 | HideSolutionNode = FALSE 84 | EndGlobalSection 85 | GlobalSection(ExtensibilityGlobals) = postSolution 86 | SolutionGuid = {61AA6387-FF05-4307-90D6-1E72C5200D6C} 87 | EndGlobalSection 88 | EndGlobal 89 | -------------------------------------------------------------------------------- /Test/Test.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 15.0 23 | {998FCBFC-78A6-4DE9-9F94-6D4762B445D7} 24 | Test 25 | 10.0.17763.0 26 | 27 | 28 | 29 | Application 30 | true 31 | v141 32 | MultiByte 33 | 34 | 35 | Application 36 | false 37 | v141 38 | true 39 | MultiByte 40 | 41 | 42 | Application 43 | true 44 | v141 45 | MultiByte 46 | 47 | 48 | Application 49 | false 50 | v141 51 | true 52 | MultiByte 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | Level3 76 | MaxSpeed 77 | true 78 | true 79 | true 80 | true 81 | 82 | 83 | true 84 | true 85 | gdiplus.lib;%(AdditionalDependencies) 86 | 87 | 88 | 89 | 90 | Level3 91 | Disabled 92 | true 93 | true 94 | 95 | 96 | 97 | 98 | Level3 99 | Disabled 100 | true 101 | true 102 | 103 | 104 | gdiplus.lib;Msimg32.lib;%(AdditionalDependencies) 105 | 106 | 107 | 108 | 109 | Level3 110 | MaxSpeed 111 | true 112 | true 113 | true 114 | true 115 | 116 | 117 | true 118 | true 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /pdf.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 21 | 23 | 46 | 53 | 54 | 56 | 57 | 59 | image/svg+xml 60 | 62 | 63 | 64 | 65 | 66 | 71 | 77 | 82 | 84 | 87 | 91 | 95 | 99 | 100 | PDF 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /PsdThumbnailProvider/PsdThumbnailProvider.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {145AB33E-71A1-42C3-8223-E107CFC162F4} 23 | Win32Proj 24 | CppShellExtThumbnailHandler 25 | PsdThumbnailProvider 26 | 10.0.17763.0 27 | 28 | 29 | 30 | DynamicLibrary 31 | true 32 | Unicode 33 | v141 34 | 35 | 36 | DynamicLibrary 37 | true 38 | Unicode 39 | v141 40 | 41 | 42 | DynamicLibrary 43 | false 44 | true 45 | Unicode 46 | v141 47 | 48 | 49 | DynamicLibrary 50 | false 51 | true 52 | Unicode 53 | v141 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | true 73 | 74 | 75 | true 76 | 77 | 78 | false 79 | 80 | 81 | false 82 | $(Platform)\$(Configuration)\ 83 | 84 | 85 | 86 | 87 | 88 | Level3 89 | Disabled 90 | WIN32;_DEBUG;_WINDOWS;_USRDLL;CPPSHELLEXTTHUMBNAILHANDLER_EXPORTS;%(PreprocessorDefinitions) 91 | 92 | 93 | Windows 94 | true 95 | GlobalExportFunctions.def 96 | 97 | 98 | 99 | 100 | 101 | 102 | Level3 103 | Disabled 104 | WIN32;_DEBUG;_WINDOWS;_USRDLL;CPPSHELLEXTTHUMBNAILHANDLER_EXPORTS;%(PreprocessorDefinitions) 105 | 106 | 107 | Windows 108 | true 109 | GlobalExportFunctions.def 110 | gdiplus.lib;Msimg32.lib;%(AdditionalDependencies) 111 | 112 | 113 | 114 | 115 | Level3 116 | 117 | 118 | MaxSpeed 119 | true 120 | true 121 | WIN32;NDEBUG;_WINDOWS;_USRDLL;CPPSHELLEXTTHUMBNAILHANDLER_EXPORTS;%(PreprocessorDefinitions) 122 | 123 | 124 | Windows 125 | true 126 | true 127 | true 128 | GlobalExportFunctions.def 129 | 130 | 131 | 132 | 133 | Level3 134 | 135 | 136 | MaxSpeed 137 | true 138 | true 139 | WIN32;NDEBUG;_WINDOWS;_USRDLL;CPPSHELLEXTTHUMBNAILHANDLER_EXPORTS;%(PreprocessorDefinitions) 140 | MultiThreaded 141 | 142 | 143 | Windows 144 | true 145 | true 146 | true 147 | GlobalExportFunctions.def 148 | gdiplus.lib;Msimg32.lib;%(AdditionalDependencies) 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | -------------------------------------------------------------------------------- /PsdThumbnailProvider/GetThumbnail.cpp: -------------------------------------------------------------------------------- 1 | #include "GetThumbnail.h" 2 | #include "gdiplus.h" 3 | #include 4 | #include 5 | #include // Gdiplus 6 | #include // Gdiplus 7 | 8 | #pragma comment(lib, "Shlwapi.lib") 9 | #pragma comment (lib, "Gdiplus.lib") // Gdiplus 10 | #pragma comment (lib, "Msimg32.lib") // AlphaBlend 11 | 12 | using namespace Gdiplus; 13 | 14 | inline void ReadData(IStream *pStream, BYTE *data, ULONG length) { 15 | ULONG read, total = 0; 16 | HRESULT hr; 17 | 18 | do { 19 | hr = pStream->Read(data + total, length - total, &read); 20 | total += read; 21 | } while (total < length && hr == S_OK); 22 | } 23 | 24 | inline UINT ReadUInt32(IStream *pStream) { 25 | BYTE b[4]; 26 | ReadData(pStream, b, 4); 27 | return b[0] << 24 | b[1] << 16 | b[2] << 8 | b[3]; 28 | } 29 | 30 | inline ULONG64 ReadUInt64(IStream *pStream) { 31 | BYTE b[8]; 32 | ReadData(pStream, b, 8); 33 | return (ULONG64)b[0] << 56 | (ULONG64)b[1] << 48 | (ULONG64)b[2] << 40 | (ULONG64)b[3] << 32 | (ULONG64)b[4] << 24 | (ULONG64)b[5] << 16 | (ULONG64)b[6] << 8 | (ULONG64)b[7]; 34 | } 35 | 36 | inline SHORT ReadInt16(IStream *pStream) { 37 | BYTE b[2]; 38 | ReadData(pStream, b, 2); 39 | return b[0] << 8 | b[1]; 40 | } 41 | 42 | inline USHORT ReadUInt16(IStream *pStream) { 43 | BYTE b[2]; 44 | ReadData(pStream, b, 2); 45 | return b[0] << 8 | b[1]; 46 | } 47 | 48 | inline BYTE ReadByte(IStream *pStream) { 49 | BYTE b; 50 | ReadData(pStream, &b, 1); 51 | return b; 52 | } 53 | 54 | inline LONGLONG ReadSectionLength(IStream *pStream, int version = 1) { 55 | if (version == 1) { 56 | return (LONGLONG)ReadUInt32(pStream); 57 | } else { 58 | return (LONGLONG)ReadUInt64(pStream); 59 | } 60 | } 61 | 62 | inline void Seek(IStream *pStream, LONGLONG offset, DWORD origin) { 63 | if (offset > 0) { 64 | LARGE_INTEGER pos; 65 | pos.QuadPart = offset; 66 | pStream->Seek(pos, origin, NULL); 67 | } 68 | } 69 | 70 | inline ULONGLONG Tell(IStream *pStream) { 71 | LARGE_INTEGER pos; 72 | ULARGE_INTEGER newPos; 73 | pos.QuadPart = 0; 74 | pStream->Seek(pos, STREAM_SEEK_CUR, &newPos); 75 | return newPos.QuadPart; 76 | } 77 | 78 | HBITMAP GetPSDThumbnail(IStream* stream) { 79 | HBITMAP result = NULL; 80 | UINT signature = ReadUInt32(stream); 81 | USHORT version = ReadUInt16(stream); 82 | 83 | if (signature != 0x38425053 || (version != 1 && version != 2)) return NULL; 84 | 85 | Seek(stream, 6, STREAM_SEEK_CUR); 86 | 87 | USHORT channels = ReadUInt16(stream); 88 | int height = ReadUInt32(stream); 89 | int width = ReadUInt32(stream); 90 | USHORT bitsPerChannel = ReadUInt16(stream); 91 | USHORT colorMode = ReadUInt16(stream); 92 | int maxSize = version == 1 ? 30000 : 300000; 93 | if (width > maxSize || height > maxSize) return NULL; 94 | 95 | // color mode data 96 | auto colorModeLength = ReadSectionLength(stream); 97 | Seek(stream, colorModeLength, STREAM_SEEK_CUR); 98 | 99 | // image resources 100 | auto resourcesLength = ReadSectionLength(stream); 101 | auto reasourceOffset = Tell(stream); 102 | ULONGLONG resourceEnd = reasourceOffset + resourcesLength; 103 | LONGLONG thumbnailOffset = 0; 104 | ULONG thumbnailLength = 0; 105 | 106 | while (Tell(stream) < resourceEnd) { 107 | Seek(stream, 4, STREAM_SEEK_CUR); // signature 108 | 109 | USHORT id = ReadUInt16(stream); 110 | 111 | BYTE nameLength = ReadByte(stream); // name 112 | Seek(stream, ((nameLength + 1) % 2) ? nameLength + 1 : nameLength, STREAM_SEEK_CUR); 113 | 114 | auto length = ReadSectionLength(stream); 115 | 116 | if (id == 1036) { 117 | thumbnailOffset = Tell(stream); 118 | thumbnailLength = (ULONG)length; 119 | break; 120 | } 121 | 122 | Seek(stream, length, STREAM_SEEK_CUR); 123 | if (Tell(stream) % 2) Seek(stream, 1, STREAM_SEEK_CUR); 124 | } 125 | 126 | // read composite image 127 | if (colorMode == 3 && ((width <= 256 && height <= 256) || !thumbnailOffset)) { 128 | Seek(stream, reasourceOffset + resourcesLength, STREAM_SEEK_SET); 129 | 130 | auto layerAndMaskInfoLength = ReadSectionLength(stream); 131 | auto layerAndMastInfoOffset = Tell(stream); 132 | 133 | auto layerInfoLength = ReadSectionLength(stream); 134 | SHORT layerCount = ReadInt16(stream); 135 | bool globalAlpha = layerCount < 0; 136 | 137 | Seek(stream, layerAndMastInfoOffset + layerAndMaskInfoLength, STREAM_SEEK_SET); 138 | 139 | USHORT compression = ReadUInt16(stream); 140 | 141 | int channelCount = 3; 142 | int offsets[16] = { 2, 1, 0 }; 143 | 144 | if (channels && channels > 3) { 145 | for (int i = 3; i < channels; i++) { 146 | offsets[i] = i; 147 | channelCount++; 148 | } 149 | } else if (globalAlpha) { 150 | offsets[3] = 3; 151 | channelCount++; 152 | } 153 | 154 | if (compression == 1 || compression == 0) { 155 | int dataLength = width * height * 4; 156 | BYTE* data = new BYTE[dataLength]; 157 | 158 | for (int i = 0; i < dataLength; i++) { 159 | data[i] = 0xff; 160 | } 161 | 162 | if (compression == 1) { 163 | USHORT* lengths = new USHORT[channelCount * height]; 164 | int step = 4; 165 | int maxLength = 0; 166 | 167 | for (int o = 0, li = 0; o < channelCount; o++) { 168 | for (int y = 0; y < height; y++, li++) { 169 | lengths[li] = ReadUInt16(stream); 170 | maxLength = maxLength < lengths[li] ? lengths[li] : maxLength; 171 | } 172 | } 173 | 174 | BYTE* buffer = new BYTE[maxLength]; 175 | 176 | for (int c = 0, li = 0; c < channelCount; c++) { 177 | int offset = offsets[c]; 178 | int extra = c > 3 || offset > 3; 179 | 180 | if (extra) { 181 | for (int y = 0; y < height; y++, li++) { 182 | Seek(stream, lengths[li], STREAM_SEEK_CUR); 183 | } 184 | } else { 185 | for (int y = 0, p = offset; y < height; y++, li++) { 186 | int length = lengths[li]; 187 | ReadData(stream, buffer, length); 188 | 189 | for (int i = 0; i < length; i++) { 190 | BYTE header = buffer[i]; 191 | 192 | if (header >= 128) { 193 | BYTE value = buffer[++i]; 194 | header = (256 - header); 195 | 196 | for (int j = 0; j <= header; j++) { 197 | data[p] = value; 198 | p += step; 199 | } 200 | } else { // header < 128 201 | for (int j = 0; j <= header; j++) { 202 | data[p] = buffer[++i]; 203 | p += step; 204 | } 205 | } 206 | } 207 | } 208 | } 209 | } 210 | delete lengths; 211 | delete buffer; 212 | } else { 213 | int pixels = width * height; 214 | BYTE* buffer = new BYTE[pixels]; 215 | if (channelCount > 4) channelCount = 4; 216 | 217 | for (int c = 0; c < channelCount; c++) { 218 | int o = offsets[c]; 219 | ReadData(stream, buffer, pixels); 220 | 221 | for (int i = 0; i < pixels; i++) { 222 | data[i * 4 + o] = buffer[i]; 223 | } 224 | } 225 | 226 | delete buffer; 227 | } 228 | 229 | bool allWhite = true; 230 | 231 | for (int i = 0; i < dataLength; i++) { 232 | if (data[i] != 0xff) { 233 | allWhite = false; 234 | break; 235 | } 236 | } 237 | 238 | if (!allWhite) { 239 | if (width > 256 || height > 256) { 240 | HBITMAP fullBitmap = CreateBitmap(width, height, 1, 32, data); 241 | int thumbWidth, thumbHeight; 242 | 243 | if (width > height) { 244 | thumbWidth = 256; 245 | thumbHeight = height * thumbWidth / width; 246 | } else { 247 | thumbHeight = 256; 248 | thumbWidth = width * thumbHeight / height; 249 | } 250 | 251 | BLENDFUNCTION fnc; 252 | fnc.BlendOp = AC_SRC_OVER; 253 | fnc.BlendFlags = 0; 254 | fnc.SourceConstantAlpha = 0xFF; 255 | fnc.AlphaFormat = AC_SRC_ALPHA; 256 | 257 | HDC dc = GetDC(NULL); 258 | HDC srcDC = CreateCompatibleDC(dc); 259 | HDC memDC = CreateCompatibleDC(dc); 260 | result = CreateCompatibleBitmap(dc, thumbWidth, thumbHeight); 261 | SelectObject(memDC, result); 262 | SelectObject(srcDC, fullBitmap); 263 | 264 | RECT rect = {}; 265 | rect.right = thumbWidth; 266 | rect.bottom = thumbHeight; 267 | FillRect(memDC, &rect, (HBRUSH)GetStockObject(NULL_BRUSH)); 268 | AlphaBlend(memDC, 0, 0, thumbWidth, thumbHeight, srcDC, 0, 0, width, height, fnc); 269 | 270 | DeleteObject(fullBitmap); 271 | DeleteDC(srcDC); 272 | DeleteDC(memDC); 273 | ReleaseDC(NULL, dc); 274 | } else { 275 | result = CreateBitmap(width, height, 1, 32, data); 276 | } 277 | } 278 | 279 | delete data; 280 | } 281 | } 282 | 283 | // read thumbnail resource 284 | if (!result && thumbnailOffset) { 285 | Seek(stream, thumbnailOffset, STREAM_SEEK_SET); 286 | ULONG_PTR token; 287 | GdiplusStartupInput input; 288 | 289 | if (Ok == GdiplusStartup(&token, &input, NULL)) { 290 | Seek(stream, 4 + 4 + 4 + 4 + 4 + 4 + 2 + 2, STREAM_SEEK_CUR); 291 | 292 | BYTE *data = new BYTE[thumbnailLength]; // TODO: should this be freed? 293 | ReadData(stream, data, thumbnailLength); 294 | IStream *memory = SHCreateMemStream(data, thumbnailLength); 295 | 296 | if (memory) { 297 | Seek(memory, 0, STREAM_SEEK_SET); 298 | 299 | Bitmap *bitmap = Bitmap::FromStream(memory); 300 | 301 | if (bitmap) { 302 | Color color(0, 255, 255, 255); 303 | bitmap->GetHBITMAP(color, &result); 304 | delete bitmap; 305 | } 306 | 307 | memory->Release(); // test 308 | } 309 | } 310 | 311 | GdiplusShutdown(token); 312 | } 313 | 314 | return result; 315 | } 316 | --------------------------------------------------------------------------------