diff options
author | Campbell Barton <ideasman42@gmail.com> | 2021-10-20 02:16:36 +0300 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2021-10-20 02:31:30 +0300 |
commit | ef9269bd62f31e39d39cc59cd34354b53eae6661 (patch) | |
tree | 5ff2fb2f3b03678b7e58a23397d4d53bb83f35f8 /source/blender/blendthumb/src/blendthumb_win32_dll.cc | |
parent | bca2701236ab60dce5e32a8731f3bba43783b5b2 (diff) |
Thumbnails: refactor extraction to use one code-path for all platforms
Thumbnail extraction now shares code between Linux/Windows,
allowing thumbnails from Zstd compressed blend files to be extracted.
The main logic is placed in blendthumb_extract.cc and is built as static
library. For windows there is DLL which is registered during blender
install and which then reads and generates thumbnails.
For other platforms there is blender-thumbnailer executable file which
takes blend file as an input and generates PNG file. As a result
Python script blender-thumbnailer.py is no longer needed.
The thumbnail extractor shares the same code-path as Blenders file
reading, so there is no need to duplicate any file reading logic.
This means reading compressed blend files is supported (broken since
the recent move Zstd compression - D5799).
This resolves T63736.
Contributors:
- @alausic original patch.
- @LazyDodo windows fixes/support.
- @campbellbarton general fixes/update.
- @lukasstockner97 Zstd support.
Reviewed By: sybren, mont29, LazyDodo, campbellbarton
Ref D6408
Diffstat (limited to 'source/blender/blendthumb/src/blendthumb_win32_dll.cc')
-rw-r--r-- | source/blender/blendthumb/src/blendthumb_win32_dll.cc | 293 |
1 files changed, 293 insertions, 0 deletions
diff --git a/source/blender/blendthumb/src/blendthumb_win32_dll.cc b/source/blender/blendthumb/src/blendthumb_win32_dll.cc new file mode 100644 index 00000000000..7f10777f884 --- /dev/null +++ b/source/blender/blendthumb/src/blendthumb_win32_dll.cc @@ -0,0 +1,293 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup blendthumb + * + * Thumbnail from Blend file extraction for MS-Windows (DLL). + */ + +#include <new> +#include <objbase.h> +#include <shlobj.h> /* For #SHChangeNotify */ +#include <shlwapi.h> +#include <thumbcache.h> /* For IThumbnailProvider */ + +extern HRESULT CBlendThumb_CreateInstance(REFIID riid, void **ppv); + +#define SZ_CLSID_BLENDTHUMBHANDLER L"{D45F043D-F17F-4e8a-8435-70971D9FA46D}" +#define SZ_BLENDTHUMBHANDLER L"Blender Thumbnail Handler" +const CLSID CLSID_BlendThumbHandler = { + 0xd45f043d, 0xf17f, 0x4e8a, {0x84, 0x35, 0x70, 0x97, 0x1d, 0x9f, 0xa4, 0x6d}}; + +typedef HRESULT (*PFNCREATEINSTANCE)(REFIID riid, void **ppvObject); +struct CLASS_OBJECT_INIT { + const CLSID *pClsid; + PFNCREATEINSTANCE pfnCreate; +}; + +/* Add classes supported by this module here. */ +const CLASS_OBJECT_INIT c_rgClassObjectInit[] = { + {&CLSID_BlendThumbHandler, CBlendThumb_CreateInstance}}; + +long g_cRefModule = 0; + +/** Handle the DLL's module */ +HINSTANCE g_hInst = nullptr; + +/** Standard DLL functions. */ +STDAPI_(BOOL) DllMain(HINSTANCE hInstance, DWORD dwReason, void *) +{ + if (dwReason == DLL_PROCESS_ATTACH) { + g_hInst = hInstance; + DisableThreadLibraryCalls(hInstance); + } + return TRUE; +} + +STDAPI DllCanUnloadNow() +{ + /* Only allow the DLL to be unloaded after all outstanding references have been released. */ + return (g_cRefModule == 0) ? S_OK : S_FALSE; +} + +void DllAddRef() +{ + InterlockedIncrement(&g_cRefModule); +} + +void DllRelease() +{ + InterlockedDecrement(&g_cRefModule); +} + +class CClassFactory : public IClassFactory { + public: + static HRESULT CreateInstance(REFCLSID clsid, + const CLASS_OBJECT_INIT *pClassObjectInits, + size_t cClassObjectInits, + REFIID riid, + void **ppv) + { + *ppv = nullptr; + HRESULT hr = CLASS_E_CLASSNOTAVAILABLE; + for (size_t i = 0; i < cClassObjectInits; i++) { + if (clsid == *pClassObjectInits[i].pClsid) { + IClassFactory *pClassFactory = new (std::nothrow) + CClassFactory(pClassObjectInits[i].pfnCreate); + hr = pClassFactory ? S_OK : E_OUTOFMEMORY; + if (SUCCEEDED(hr)) { + hr = pClassFactory->QueryInterface(riid, ppv); + pClassFactory->Release(); + } + /* Match found. */ + break; + } + } + return hr; + } + + CClassFactory(PFNCREATEINSTANCE pfnCreate) : _cRef(1), _pfnCreate(pfnCreate) + { + DllAddRef(); + } + + /** #IUnknown */ + IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv) + { + static const QITAB qit[] = {QITABENT(CClassFactory, IClassFactory), {0}}; + return QISearch(this, qit, riid, ppv); + } + + IFACEMETHODIMP_(ULONG) AddRef() + { + return InterlockedIncrement(&_cRef); + } + + IFACEMETHODIMP_(ULONG) Release() + { + long cRef = InterlockedDecrement(&_cRef); + if (cRef == 0) { + delete this; + } + return cRef; + } + + /** #IClassFactory */ + IFACEMETHODIMP CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv) + { + return punkOuter ? CLASS_E_NOAGGREGATION : _pfnCreate(riid, ppv); + } + + IFACEMETHODIMP LockServer(BOOL fLock) + { + if (fLock) { + DllAddRef(); + } + else { + DllRelease(); + } + return S_OK; + } + + private: + ~CClassFactory() + { + DllRelease(); + } + + long _cRef; + PFNCREATEINSTANCE _pfnCreate; +}; + +STDAPI DllGetClassObject(REFCLSID clsid, REFIID riid, void **ppv) +{ + return CClassFactory::CreateInstance( + clsid, c_rgClassObjectInit, ARRAYSIZE(c_rgClassObjectInit), riid, ppv); +} + +/** + * A struct to hold the information required for a registry entry. + */ +struct REGISTRY_ENTRY { + HKEY hkeyRoot; + PCWSTR pszKeyName; + PCWSTR pszValueName; + DWORD dwValueType; + /** These two fields could/should have been a union, but C++ */ + PCWSTR pszData; + /** Only lets you initialize the first field in a union. */ + DWORD dwData; +}; + +/** + * Creates a registry key (if needed) and sets the default value of the key. + */ +HRESULT CreateRegKeyAndSetValue(const REGISTRY_ENTRY *pRegistryEntry) +{ + HKEY hKey; + HRESULT hr = HRESULT_FROM_WIN32(RegCreateKeyExW(pRegistryEntry->hkeyRoot, + pRegistryEntry->pszKeyName, + 0, + nullptr, + REG_OPTION_NON_VOLATILE, + KEY_SET_VALUE, + nullptr, + &hKey, + nullptr)); + if (SUCCEEDED(hr)) { + /* All this just to support #REG_DWORD. */ + DWORD size; + DWORD data; + BYTE *lpData = (LPBYTE)pRegistryEntry->pszData; + switch (pRegistryEntry->dwValueType) { + case REG_SZ: + size = ((DWORD)wcslen(pRegistryEntry->pszData) + 1) * sizeof(WCHAR); + break; + case REG_DWORD: + size = sizeof(DWORD); + data = pRegistryEntry->dwData; + lpData = (BYTE *)&data; + break; + default: + return E_INVALIDARG; + } + + hr = HRESULT_FROM_WIN32(RegSetValueExW( + hKey, pRegistryEntry->pszValueName, 0, pRegistryEntry->dwValueType, lpData, size)); + RegCloseKey(hKey); + } + return hr; +} + +/** + * Registers this COM server. + */ +STDAPI DllRegisterServer() +{ + HRESULT hr; + + WCHAR szModuleName[MAX_PATH]; + + if (!GetModuleFileNameW(g_hInst, szModuleName, ARRAYSIZE(szModuleName))) { + hr = HRESULT_FROM_WIN32(GetLastError()); + } + else { + const REGISTRY_ENTRY rgRegistryEntries[] = { + /* `RootKey KeyName ValueName ValueType Data` */ + {HKEY_CURRENT_USER, + L"Software\\Classes\\CLSID\\" SZ_CLSID_BLENDTHUMBHANDLER, + nullptr, + REG_SZ, + SZ_BLENDTHUMBHANDLER}, + {HKEY_CURRENT_USER, + L"Software\\Classes\\CLSID\\" SZ_CLSID_BLENDTHUMBHANDLER L"\\InProcServer32", + nullptr, + REG_SZ, + szModuleName}, + {HKEY_CURRENT_USER, + L"Software\\Classes\\CLSID\\" SZ_CLSID_BLENDTHUMBHANDLER L"\\InProcServer32", + L"ThreadingModel", + REG_SZ, + L"Apartment"}, + {HKEY_CURRENT_USER, + L"Software\\Classes\\.blend\\", + L"Treatment", + REG_DWORD, + 0, + 0}, /* This doesn't appear to do anything. */ + {HKEY_CURRENT_USER, + L"Software\\Classes\\.blend\\ShellEx\\{e357fccd-a995-4576-b01f-234630154e96}", + nullptr, + REG_SZ, + SZ_CLSID_BLENDTHUMBHANDLER}, + }; + + hr = S_OK; + for (int i = 0; i < ARRAYSIZE(rgRegistryEntries) && SUCCEEDED(hr); i++) { + hr = CreateRegKeyAndSetValue(&rgRegistryEntries[i]); + } + } + if (SUCCEEDED(hr)) { + /* This tells the shell to invalidate the thumbnail cache. + * This is important because any `.blend` files viewed before registering this handler + * would otherwise show cached blank thumbnails. */ + SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nullptr, nullptr); + } + return hr; +} + +/** + * Unregisters this COM server + */ +STDAPI DllUnregisterServer() +{ + HRESULT hr = S_OK; + + const PCWSTR rgpszKeys[] = { + L"Software\\Classes\\CLSID\\" SZ_CLSID_BLENDTHUMBHANDLER, + L"Software\\Classes\\.blend\\ShellEx\\{e357fccd-a995-4576-b01f-234630154e96}"}; + + /* Delete the registry entries. */ + for (int i = 0; i < ARRAYSIZE(rgpszKeys) && SUCCEEDED(hr); i++) { + hr = HRESULT_FROM_WIN32(RegDeleteTreeW(HKEY_CURRENT_USER, rgpszKeys[i])); + if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) { + /* If the registry entry has already been deleted, say S_OK. */ + hr = S_OK; + } + } + return hr; +} |