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