Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRay Molenkamp <github@lazydodo.com>2019-08-30 17:40:08 +0300
committerRay Molenkamp <github@lazydodo.com>2019-08-30 17:40:08 +0300
commit66ec72045f8c25ec9e9710f4d70aed9fb1514df6 (patch)
tree676d5f94863224c8a0de0dec3337a73fb7707dc6 /source/blender/blendthumb
parent26c110f2d243eddcb5bf80c1214d00d810181f1a (diff)
Windows: Move building of blendthumb into the blender codebase.
Previously this was done in the deps builder due to the fact we needed both 32 and 64 bit versions of this dll and CMAKE does not support that in a single build folder. Now that 32 bit support has been dropped, this can be safely moved into the codebase. Reviewers: brecht Differential Revision: https://developer.blender.org/D5633
Diffstat (limited to 'source/blender/blendthumb')
-rw-r--r--source/blender/blendthumb/CMakeLists.txt32
-rw-r--r--source/blender/blendthumb/src/BlendThumb.def5
-rw-r--r--source/blender/blendthumb/src/BlendThumb.rc26
-rw-r--r--source/blender/blendthumb/src/BlenderThumb.cpp320
-rw-r--r--source/blender/blendthumb/src/Dll.cpp273
5 files changed, 656 insertions, 0 deletions
diff --git a/source/blender/blendthumb/CMakeLists.txt b/source/blender/blendthumb/CMakeLists.txt
new file mode 100644
index 00000000000..3f9e0b1bff5
--- /dev/null
+++ b/source/blender/blendthumb/CMakeLists.txt
@@ -0,0 +1,32 @@
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# 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.
+#
+# The Original Code is Copyright (C) 2006, Blender Foundation
+# All rights reserved.
+# ***** END GPL LICENSE BLOCK *****
+
+#-----------------------------------------------------------------------------
+include_directories(${ZLIB_INCLUDE_DIRS})
+
+set(SRC
+ src/BlenderThumb.cpp
+ src/BlendThumb.def
+ src/BlendThumb.rc
+ src/Dll.cpp
+)
+
+add_library(BlendThumb SHARED ${SRC})
+target_link_libraries(BlendThumb ${ZLIB_LIBRARIES})
diff --git a/source/blender/blendthumb/src/BlendThumb.def b/source/blender/blendthumb/src/BlendThumb.def
new file mode 100644
index 00000000000..71f9236735f
--- /dev/null
+++ b/source/blender/blendthumb/src/BlendThumb.def
@@ -0,0 +1,5 @@
+EXPORTS
+ DllGetClassObject PRIVATE
+ DllCanUnloadNow PRIVATE
+ DllRegisterServer PRIVATE
+ DllUnregisterServer PRIVATE \ No newline at end of file
diff --git a/source/blender/blendthumb/src/BlendThumb.rc b/source/blender/blendthumb/src/BlendThumb.rc
new file mode 100644
index 00000000000..5dfd416b0c5
--- /dev/null
+++ b/source/blender/blendthumb/src/BlendThumb.rc
@@ -0,0 +1,26 @@
+#define IDR_VERSION1 1
+
+IDR_VERSION1 VERSIONINFO
+FILEVERSION 1,4,0,0
+PRODUCTVERSION 2,78,0,0
+FILEOS 0x00000004
+FILETYPE 0x00000002
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "FFFF04B0"
+ BEGIN
+ VALUE "FileVersion", "1.4\0"
+ VALUE "ProductVersion", "2.78\0"
+ VALUE "FileDescription", "Blender Thumbnail Handler\0"
+ VALUE "OriginalFilename", "BlendThumb.dll\0"
+ VALUE "ProductName", "Blender\0"
+ VALUE "LegalCopyright", "GPL2, 2016\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0409, 0x04B0
+ END
+END
+
diff --git a/source/blender/blendthumb/src/BlenderThumb.cpp b/source/blender/blendthumb/src/BlenderThumb.cpp
new file mode 100644
index 00000000000..553428d5b5d
--- /dev/null
+++ b/source/blender/blendthumb/src/BlenderThumb.cpp
@@ -0,0 +1,320 @@
+/*
+ * 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.
+ */
+
+#include <shlwapi.h>
+#include <thumbcache.h> // For IThumbnailProvider.
+#include <new>
+
+#pragma comment(lib, "shlwapi.lib")
+
+// this thumbnail provider implements IInitializeWithStream to enable being hosted
+// in an isolated process for robustness
+
+class CBlendThumb : public IInitializeWithStream, public IThumbnailProvider
+{
+public:
+ CBlendThumb() : _cRef(1), _pStream(NULL) {}
+
+ virtual ~CBlendThumb()
+ {
+ if (_pStream)
+ {
+ _pStream->Release();
+ }
+ }
+
+ // IUnknown
+ IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv)
+ {
+ static const QITAB qit[] =
+ {
+ QITABENT(CBlendThumb, IInitializeWithStream),
+ QITABENT(CBlendThumb, IThumbnailProvider),
+ { 0 },
+ };
+ return QISearch(this, qit, riid, ppv);
+ }
+
+ IFACEMETHODIMP_(ULONG) AddRef()
+ {
+ return InterlockedIncrement(&_cRef);
+ }
+
+ IFACEMETHODIMP_(ULONG) Release()
+ {
+ ULONG cRef = InterlockedDecrement(&_cRef);
+ if (!cRef)
+ {
+ delete this;
+ }
+ return cRef;
+ }
+
+ // IInitializeWithStream
+ IFACEMETHODIMP Initialize(IStream *pStream, DWORD grfMode);
+
+ // IThumbnailProvider
+ IFACEMETHODIMP GetThumbnail(UINT cx, HBITMAP *phbmp, WTS_ALPHATYPE *pdwAlpha);
+
+private:
+ long _cRef;
+ IStream *_pStream; // provided during initialization.
+};
+
+HRESULT CBlendThumb_CreateInstance(REFIID riid, void **ppv)
+{
+ CBlendThumb *pNew = new (std::nothrow) CBlendThumb();
+ HRESULT hr = pNew ? S_OK : E_OUTOFMEMORY;
+ if (SUCCEEDED(hr))
+ {
+ hr = pNew->QueryInterface(riid, ppv);
+ pNew->Release();
+ }
+ return hr;
+}
+
+// IInitializeWithStream
+IFACEMETHODIMP CBlendThumb::Initialize(IStream *pStream, DWORD)
+{
+ HRESULT hr = E_UNEXPECTED; // can only be inited once
+ if (_pStream == NULL)
+ {
+ // take a reference to the stream if we have not been inited yet
+ hr = pStream->QueryInterface(&_pStream);
+ }
+ return hr;
+}
+
+#include <math.h>
+#include <zlib.h>
+#include "Wincodec.h"
+const unsigned char gzip_magic[3] = { 0x1f, 0x8b, 0x08 };
+
+// IThumbnailProvider
+IFACEMETHODIMP CBlendThumb::GetThumbnail(UINT cx, HBITMAP *phbmp, WTS_ALPHATYPE *pdwAlpha)
+{
+ ULONG BytesRead;
+ HRESULT hr = S_FALSE;
+ LARGE_INTEGER SeekPos;
+
+ // Compressed?
+ unsigned char in_magic[3];
+ _pStream->Read(&in_magic,3,&BytesRead);
+ bool gzipped = true;
+ for ( int i=0; i < 3; i++ )
+ if ( in_magic[i] != gzip_magic[i] )
+ {
+ gzipped = false;
+ break;
+ }
+
+ if (gzipped)
+ {
+ // Zlib inflate
+ z_stream stream;
+ stream.zalloc = Z_NULL;
+ stream.zfree = Z_NULL;
+ stream.opaque = Z_NULL;
+
+ // Get compressed file length
+ SeekPos.QuadPart = 0;
+ _pStream->Seek(SeekPos,STREAM_SEEK_END,NULL);
+
+ // Get compressed and uncompressed size
+ uLong source_size;
+ uLongf dest_size;
+ //SeekPos.QuadPart = -4; // last 4 bytes define size of uncompressed file
+ //ULARGE_INTEGER Tell;
+ //_pStream->Seek(SeekPos,STREAM_SEEK_END,&Tell);
+ //source_size = (uLong)Tell.QuadPart + 4; // src
+ //_pStream->Read(&dest_size,4,&BytesRead); // dest
+ dest_size = 1024*70; // thumbnail is currently always inside the first 65KB...if it moves or enlargens this line will have to change or go!
+ source_size = (uLong)max(SeekPos.QuadPart,dest_size); // for safety, assume no compression
+
+ // Input
+ Bytef* src = new Bytef[source_size];
+ stream.next_in = (Bytef*)src;
+ stream.avail_in = (uInt)source_size;
+
+ // Output
+ Bytef* dest = new Bytef[dest_size];
+ stream.next_out = (Bytef*)dest;
+ stream.avail_out = dest_size;
+
+ // IStream to src
+ SeekPos.QuadPart = 0;
+ _pStream->Seek(SeekPos,STREAM_SEEK_SET,NULL);
+ _pStream->Read(src,source_size,&BytesRead);
+
+ // Do the inflation
+ int err;
+ err = inflateInit2(&stream,16); // 16 means "gzip"...nice!
+ err = inflate(&stream, Z_FINISH);
+ err = inflateEnd(&stream);
+
+ // Replace the IStream, which is read-only
+ _pStream->Release();
+ _pStream = SHCreateMemStream(dest,dest_size);
+
+ delete[] src;
+ delete[] dest;
+ }
+
+ // Blender version, early out if sub 2.5
+ SeekPos.QuadPart = 9;
+ _pStream->Seek(SeekPos,STREAM_SEEK_SET,NULL);
+ char version[4];
+ version[3] = '\0';
+ _pStream->Read(&version,3,&BytesRead);
+ if ( BytesRead != 3)
+ return E_UNEXPECTED;
+ int iVersion = atoi(version);
+ if ( iVersion < 250 )
+ return S_FALSE;
+
+ // 32 or 64 bit blend?
+ SeekPos.QuadPart = 7;
+ _pStream->Seek(SeekPos,STREAM_SEEK_SET,NULL);
+
+ char _PointerSize;
+ _pStream->Read(&_PointerSize,1,&BytesRead);
+
+ int PointerSize = _PointerSize == '_' ? 4 : 8;
+ int HeaderSize = 16 + PointerSize;
+
+ // Find and read thumbnail ("TEST") block
+ SeekPos.QuadPart = 12;
+ _pStream->Seek(SeekPos,STREAM_SEEK_SET,NULL);
+ int BlockOffset = 12;
+ while ( _pStream )
+ {
+ // Scan current block
+ char BlockName[5];
+ BlockName[4] = '\0';
+ int BlockSize = 0;
+
+ if (_pStream->Read(BlockName,4,&BytesRead) == S_OK && _pStream->Read((void*)&BlockSize,4,&BytesRead) == S_OK)
+ {
+ if ( strcmp (BlockName,"TEST") != 0 )
+ {
+ SeekPos.QuadPart = BlockOffset += HeaderSize + BlockSize;
+ _pStream->Seek(SeekPos,STREAM_SEEK_SET,NULL);
+ continue;
+ }
+ }
+ else break; // eof
+
+ // Found the block
+ SeekPos.QuadPart = BlockOffset + HeaderSize;
+ _pStream->Seek(SeekPos,STREAM_SEEK_SET,NULL);
+
+ int width, height;
+ _pStream->Read((char*)&width,4,&BytesRead);
+ _pStream->Read((char*)&height,4,&BytesRead);
+ BlockSize -= 8;
+
+ // Isolate RGBA data
+ char* pRGBA = new char[BlockSize];
+ _pStream->Read(pRGBA,BlockSize,&BytesRead);
+
+ if (BytesRead != (ULONG)BlockSize)
+ return E_UNEXPECTED;
+
+ // Convert to BGRA for Windows
+ for (int i=0; i < BlockSize; i+=4 )
+ {
+ #define RED_BYTE pRGBA[i]
+ #define BLUE_BYTE pRGBA[i+2]
+
+ char red = RED_BYTE;
+ RED_BYTE = BLUE_BYTE;
+ BLUE_BYTE = red;
+ }
+
+ // Flip vertically (Blender stores it upside-down)
+ unsigned int LineSize = width*4;
+ char* FlippedImage = new char[BlockSize];
+ for (int i=0; i<height; i++)
+ {
+ if ( 0 != memcpy_s(&FlippedImage[ (height - i - 1)*LineSize ],LineSize,&pRGBA[ i*LineSize ],LineSize))
+ return E_UNEXPECTED;
+ }
+ delete[] pRGBA;
+ pRGBA = FlippedImage;
+
+ // Create image
+ *phbmp = CreateBitmap(width,height,1,32,pRGBA);
+ if (!*phbmp)
+ return E_FAIL;
+ *pdwAlpha = WTSAT_ARGB; // it's actually BGRA, not sure why this works
+
+ // Scale down if required
+ if ( (unsigned)width > cx || (unsigned)height > cx )
+ {
+ float scale = 1.0f / (max(width,height) / (float)cx);
+ LONG NewWidth = (LONG)(width *scale);
+ LONG NewHeight = (LONG)(height *scale);
+
+#ifdef _DEBUG
+#if 1
+ MessageBox(0,L"Attach now",L"Debugging",MB_OK);
+#endif
+#endif
+ IWICImagingFactory *pImgFac;
+ hr = CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pImgFac));
+
+ IWICBitmap* WICBmp;
+ hr = pImgFac->CreateBitmapFromHBITMAP(*phbmp,0,WICBitmapUseAlpha,&WICBmp);
+
+ BITMAPINFO bmi = {};
+ bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
+ bmi.bmiHeader.biWidth = NewWidth;
+ bmi.bmiHeader.biHeight = -NewHeight;
+ bmi.bmiHeader.biPlanes = 1;
+ bmi.bmiHeader.biBitCount = 32;
+ bmi.bmiHeader.biCompression = BI_RGB;
+
+ BYTE *pBits;
+ HBITMAP ResizedHBmp = CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, (void**)&pBits, NULL, 0);
+ hr = ResizedHBmp ? S_OK : E_OUTOFMEMORY;
+ if (SUCCEEDED(hr))
+ {
+ IWICBitmapScaler* pIScaler;
+ hr = pImgFac->CreateBitmapScaler(&pIScaler);
+ hr = pIScaler->Initialize(WICBmp,NewWidth,NewHeight,WICBitmapInterpolationModeFant);
+
+ WICRect rect = {0, 0, NewWidth, NewHeight};
+ hr = pIScaler->CopyPixels(&rect, NewWidth * 4, NewWidth * NewHeight * 4, pBits);
+
+ if (SUCCEEDED(hr))
+ {
+ DeleteObject(*phbmp);
+ *phbmp = ResizedHBmp;
+ }
+ else
+ DeleteObject(ResizedHBmp);
+
+ pIScaler->Release();
+ }
+ WICBmp->Release();
+ pImgFac->Release();
+ }
+ else
+ hr = S_OK;
+ break;
+ }
+ return hr;
+}
diff --git a/source/blender/blendthumb/src/Dll.cpp b/source/blender/blendthumb/src/Dll.cpp
new file mode 100644
index 00000000000..7b0521cd5a8
--- /dev/null
+++ b/source/blender/blendthumb/src/Dll.cpp
@@ -0,0 +1,273 @@
+/*
+ * 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.
+ */
+
+#include <objbase.h>
+#include <shlwapi.h>
+#include <thumbcache.h> // For IThumbnailProvider.
+#include <shlobj.h> // For SHChangeNotify
+#include <new>
+
+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 = NULL;
+
+// 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 = NULL;
+ 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();
+ }
+ break; // match found
+ }
+ }
+ 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;
+ PCWSTR pszData;
+};
+
+// 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, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hKey, NULL));
+ 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 = (DWORD)pRegistryEntry->pszData;
+ 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, NULL, REG_SZ, SZ_BLENDTHUMBHANDLER},
+ {HKEY_CURRENT_USER, L"Software\\Classes\\CLSID\\" SZ_CLSID_BLENDTHUMBHANDLER L"\\InProcServer32", NULL, 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}, // doesn't appear to do anything...
+ {HKEY_CURRENT_USER, L"Software\\Classes\\.blend\\ShellEx\\{e357fccd-a995-4576-b01f-234630154e96}", NULL, 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, NULL, NULL);
+ }
+ 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;
+}