1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16 
17 #include <new>
18 #include <objbase.h>
19 #include <shlobj.h>  // For SHChangeNotify
20 #include <shlwapi.h>
21 #include <thumbcache.h>  // For IThumbnailProvider.
22 
23 extern HRESULT CBlendThumb_CreateInstance(REFIID riid, void **ppv);
24 
25 #define SZ_CLSID_BLENDTHUMBHANDLER L"{D45F043D-F17F-4e8a-8435-70971D9FA46D}"
26 #define SZ_BLENDTHUMBHANDLER L"Blender Thumbnail Handler"
27 const CLSID CLSID_BlendThumbHandler = {
28     0xd45f043d, 0xf17f, 0x4e8a, {0x84, 0x35, 0x70, 0x97, 0x1d, 0x9f, 0xa4, 0x6d}};
29 
30 typedef HRESULT (*PFNCREATEINSTANCE)(REFIID riid, void **ppvObject);
31 struct CLASS_OBJECT_INIT {
32   const CLSID *pClsid;
33   PFNCREATEINSTANCE pfnCreate;
34 };
35 
36 // add classes supported by this module here
37 const CLASS_OBJECT_INIT c_rgClassObjectInit[] = {
38     {&CLSID_BlendThumbHandler, CBlendThumb_CreateInstance}};
39 
40 long g_cRefModule = 0;
41 
42 // Handle the DLL's module
43 HINSTANCE g_hInst = NULL;
44 
45 // Standard DLL functions
DllMain(HINSTANCE hInstance,DWORD dwReason,void *)46 STDAPI_(BOOL) DllMain(HINSTANCE hInstance, DWORD dwReason, void *)
47 {
48   if (dwReason == DLL_PROCESS_ATTACH) {
49     g_hInst = hInstance;
50     DisableThreadLibraryCalls(hInstance);
51   }
52   return TRUE;
53 }
54 
DllCanUnloadNow()55 STDAPI DllCanUnloadNow()
56 {
57   // Only allow the DLL to be unloaded after all outstanding references have been released
58   return (g_cRefModule == 0) ? S_OK : S_FALSE;
59 }
60 
DllAddRef()61 void DllAddRef()
62 {
63   InterlockedIncrement(&g_cRefModule);
64 }
65 
DllRelease()66 void DllRelease()
67 {
68   InterlockedDecrement(&g_cRefModule);
69 }
70 
71 class CClassFactory : public IClassFactory {
72  public:
CreateInstance(REFCLSID clsid,const CLASS_OBJECT_INIT * pClassObjectInits,size_t cClassObjectInits,REFIID riid,void ** ppv)73   static HRESULT CreateInstance(REFCLSID clsid,
74                                 const CLASS_OBJECT_INIT *pClassObjectInits,
75                                 size_t cClassObjectInits,
76                                 REFIID riid,
77                                 void **ppv)
78   {
79     *ppv = NULL;
80     HRESULT hr = CLASS_E_CLASSNOTAVAILABLE;
81     for (size_t i = 0; i < cClassObjectInits; i++) {
82       if (clsid == *pClassObjectInits[i].pClsid) {
83         IClassFactory *pClassFactory = new (std::nothrow)
84             CClassFactory(pClassObjectInits[i].pfnCreate);
85         hr = pClassFactory ? S_OK : E_OUTOFMEMORY;
86         if (SUCCEEDED(hr)) {
87           hr = pClassFactory->QueryInterface(riid, ppv);
88           pClassFactory->Release();
89         }
90         break;  // match found
91       }
92     }
93     return hr;
94   }
95 
CClassFactory(PFNCREATEINSTANCE pfnCreate)96   CClassFactory(PFNCREATEINSTANCE pfnCreate) : _cRef(1), _pfnCreate(pfnCreate)
97   {
98     DllAddRef();
99   }
100 
101   // IUnknown
QueryInterface(REFIID riid,void ** ppv)102   IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv)
103   {
104     static const QITAB qit[] = {QITABENT(CClassFactory, IClassFactory), {0}};
105     return QISearch(this, qit, riid, ppv);
106   }
107 
AddRef()108   IFACEMETHODIMP_(ULONG) AddRef()
109   {
110     return InterlockedIncrement(&_cRef);
111   }
112 
Release()113   IFACEMETHODIMP_(ULONG) Release()
114   {
115     long cRef = InterlockedDecrement(&_cRef);
116     if (cRef == 0) {
117       delete this;
118     }
119     return cRef;
120   }
121 
122   // IClassFactory
CreateInstance(IUnknown * punkOuter,REFIID riid,void ** ppv)123   IFACEMETHODIMP CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
124   {
125     return punkOuter ? CLASS_E_NOAGGREGATION : _pfnCreate(riid, ppv);
126   }
127 
LockServer(BOOL fLock)128   IFACEMETHODIMP LockServer(BOOL fLock)
129   {
130     if (fLock) {
131       DllAddRef();
132     }
133     else {
134       DllRelease();
135     }
136     return S_OK;
137   }
138 
139  private:
~CClassFactory()140   ~CClassFactory()
141   {
142     DllRelease();
143   }
144 
145   long _cRef;
146   PFNCREATEINSTANCE _pfnCreate;
147 };
148 
DllGetClassObject(REFCLSID clsid,REFIID riid,void ** ppv)149 STDAPI DllGetClassObject(REFCLSID clsid, REFIID riid, void **ppv)
150 {
151   return CClassFactory::CreateInstance(
152       clsid, c_rgClassObjectInit, ARRAYSIZE(c_rgClassObjectInit), riid, ppv);
153 }
154 
155 // A struct to hold the information required for a registry entry
156 
157 struct REGISTRY_ENTRY {
158   HKEY hkeyRoot;
159   PCWSTR pszKeyName;
160   PCWSTR pszValueName;
161   DWORD dwValueType;
162   PCWSTR pszData;  // These two fields could/should have been a union, but C++
163   DWORD dwData;    // only lets you initalize the first field in a union.
164 };
165 
166 // Creates a registry key (if needed) and sets the default value of the key
167 
CreateRegKeyAndSetValue(const REGISTRY_ENTRY * pRegistryEntry)168 HRESULT CreateRegKeyAndSetValue(const REGISTRY_ENTRY *pRegistryEntry)
169 {
170   HKEY hKey;
171   HRESULT hr = HRESULT_FROM_WIN32(RegCreateKeyExW(pRegistryEntry->hkeyRoot,
172                                                   pRegistryEntry->pszKeyName,
173                                                   0,
174                                                   NULL,
175                                                   REG_OPTION_NON_VOLATILE,
176                                                   KEY_SET_VALUE,
177                                                   NULL,
178                                                   &hKey,
179                                                   NULL));
180   if (SUCCEEDED(hr)) {
181     // All this just to support REG_DWORD...
182     DWORD size;
183     DWORD data;
184     BYTE *lpData = (LPBYTE)pRegistryEntry->pszData;
185     switch (pRegistryEntry->dwValueType) {
186       case REG_SZ:
187         size = ((DWORD)wcslen(pRegistryEntry->pszData) + 1) * sizeof(WCHAR);
188         break;
189       case REG_DWORD:
190         size = sizeof(DWORD);
191         data = pRegistryEntry->dwData;
192         lpData = (BYTE *)&data;
193         break;
194       default:
195         return E_INVALIDARG;
196     }
197 
198     hr = HRESULT_FROM_WIN32(RegSetValueExW(
199         hKey, pRegistryEntry->pszValueName, 0, pRegistryEntry->dwValueType, lpData, size));
200     RegCloseKey(hKey);
201   }
202   return hr;
203 }
204 
205 //
206 // Registers this COM server
207 //
DllRegisterServer()208 STDAPI DllRegisterServer()
209 {
210   HRESULT hr;
211 
212   WCHAR szModuleName[MAX_PATH];
213 
214   if (!GetModuleFileNameW(g_hInst, szModuleName, ARRAYSIZE(szModuleName))) {
215     hr = HRESULT_FROM_WIN32(GetLastError());
216   }
217   else {
218     const REGISTRY_ENTRY rgRegistryEntries[] = {
219         // RootKey KeyName ValueName ValueType Data
220         {HKEY_CURRENT_USER,
221          L"Software\\Classes\\CLSID\\" SZ_CLSID_BLENDTHUMBHANDLER,
222          NULL,
223          REG_SZ,
224          SZ_BLENDTHUMBHANDLER},
225         {HKEY_CURRENT_USER,
226          L"Software\\Classes\\CLSID\\" SZ_CLSID_BLENDTHUMBHANDLER L"\\InProcServer32",
227          NULL,
228          REG_SZ,
229          szModuleName},
230         {HKEY_CURRENT_USER,
231          L"Software\\Classes\\CLSID\\" SZ_CLSID_BLENDTHUMBHANDLER L"\\InProcServer32",
232          L"ThreadingModel",
233          REG_SZ,
234          L"Apartment"},
235         {HKEY_CURRENT_USER,
236          L"Software\\Classes\\.blend\\",
237          L"Treatment",
238          REG_DWORD,
239          0,
240          0},  // doesn't appear to do anything...
241         {HKEY_CURRENT_USER,
242          L"Software\\Classes\\.blend\\ShellEx\\{e357fccd-a995-4576-b01f-234630154e96}",
243          NULL,
244          REG_SZ,
245          SZ_CLSID_BLENDTHUMBHANDLER},
246     };
247 
248     hr = S_OK;
249     for (int i = 0; i < ARRAYSIZE(rgRegistryEntries) && SUCCEEDED(hr); i++) {
250       hr = CreateRegKeyAndSetValue(&rgRegistryEntries[i]);
251     }
252   }
253   if (SUCCEEDED(hr)) {
254     // This tells the shell to invalidate the thumbnail cache.  This is important because any
255     // .blend files viewed before registering this handler would otherwise show cached blank
256     // thumbnails.
257     SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
258   }
259   return hr;
260 }
261 
262 //
263 // Unregisters this COM server
264 //
DllUnregisterServer()265 STDAPI DllUnregisterServer()
266 {
267   HRESULT hr = S_OK;
268 
269   const PCWSTR rgpszKeys[] = {
270       L"Software\\Classes\\CLSID\\" SZ_CLSID_BLENDTHUMBHANDLER,
271       L"Software\\Classes\\.blend\\ShellEx\\{e357fccd-a995-4576-b01f-234630154e96}"};
272 
273   // Delete the registry entries
274   for (int i = 0; i < ARRAYSIZE(rgpszKeys) && SUCCEEDED(hr); i++) {
275     hr = HRESULT_FROM_WIN32(RegDeleteTreeW(HKEY_CURRENT_USER, rgpszKeys[i]));
276     if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) {
277       // If the registry entry has already been deleted, say S_OK.
278       hr = S_OK;
279     }
280   }
281   return hr;
282 }
283