1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_shell.hxx"
26 #include "internal/config.hxx"
27 #include "internal/global.hxx"
28 #include "internal/shlxthdl.hxx"
29 #include "classfactory.hxx"
30 #include "internal/registry.hxx"
31 #include "internal/fileextensions.hxx"
32 #include "internal/utilities.hxx"
33
34 #include <tchar.h>
35 #include <string>
36 #include <shlobj.h>
37
38 //---------------------------
39 // Module global
40 //---------------------------
41 long g_DllRefCnt = 0;
42 HINSTANCE g_hModule = NULL;
43
44 namespace /* private */
45 {
46 const char* GUID_PLACEHOLDER = "{GUID}";
47 const char* EXTENSION_PLACEHOLDER = "{EXT}";
48 const char* FORWARDKEY_PLACEHOLDER = "{FWDKEY}";
49
50 const char* CLSID_ENTRY = "CLSID\\{GUID}\\InProcServer32";
51 const char* SHELLEX_IID_ENTRY = "{EXT}\\shellex\\{GUID}";
52 const char* SHELLEX_ENTRY = "{EXT}\\shellex";
53 const char* PROPSHEET_ENTRY = "{EXT}\\CLSID\\{GUID}\\InProcServer32";
54 const char* EXTENSION_CLSID = "{EXT}\\CLSID";
55 const char* EXTENSION_CLSID_GUID = "{EXT}\\CLSID\\{GUID}";
56 const char* FORWARD_PROPSHEET_MYPROPSHEET_ENTRY = "{FWDKEY}\\shellex\\PropertySheetHandlers\\MyPropSheet1";
57 const char* FORWARD_PROPSHEET_ENTRY = "{FWDKEY}\\shellex\\PropertySheetHandlers";
58 const char* FORWARD_SHELLEX_ENTRY = "{FWDKEY}\\shellex";
59
60 const char* SHELL_EXTENSION_APPROVED_KEY_NAME = "Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved";
61
62 //---------------------------
63 // "String Placeholder" ->
64 // "String Replacement"
65 //---------------------------
SubstitutePlaceholder(std::string & String,const std::string & Placeholder,const std::string & Replacement)66 void SubstitutePlaceholder(std::string& String, const std::string& Placeholder, const std::string& Replacement)
67 {
68 std::string::size_type idx = String.find(Placeholder);
69 std::string::size_type len = Placeholder.length();
70
71 while (std::string::npos != idx)
72 {
73 String.replace(idx, len, Replacement);
74 idx = String.find(Placeholder);
75 }
76 }
77
78 /* Make the registry entry
79 HKCR\CLSID\{GUID}
80 InProcServer32 = Path\shlxthdl.dll
81 ThreadingModel = Apartment
82 */
RegisterComComponent(const char * FilePath,const CLSID & Guid)83 HRESULT RegisterComComponent(const char* FilePath, const CLSID& Guid)
84 {
85 std::string ClsidEntry = CLSID_ENTRY;
86 SubstitutePlaceholder(ClsidEntry, GUID_PLACEHOLDER, ClsidToString(Guid));
87
88 if (!SetRegistryKey(HKEY_CLASSES_ROOT, ClsidEntry.c_str(), "", FilePath))
89 return E_FAIL;
90
91 if (!SetRegistryKey(HKEY_CLASSES_ROOT, ClsidEntry.c_str(), "ThreadingModel", "Apartment"))
92 return E_FAIL;
93
94 return S_OK;
95 }
96
UnregisterComComponent(const CLSID & Guid)97 HRESULT UnregisterComComponent(const CLSID& Guid)
98 {
99 std::string tmp = "CLSID\\";
100 tmp += ClsidToString(Guid);
101 return DeleteRegistryKey(HKEY_CLASSES_ROOT, tmp.c_str()) ? S_OK : E_FAIL;
102 }
103
RegisterColumnHandler(const char * ModuleFileName)104 HRESULT RegisterColumnHandler(const char* ModuleFileName)
105 {
106 if (FAILED(RegisterComComponent(ModuleFileName, CLSID_COLUMN_HANDLER)))
107 return E_FAIL;
108
109 std::string tmp = "Folder\\shellex\\ColumnHandlers\\";
110 tmp += ClsidToString(CLSID_COLUMN_HANDLER);
111
112 return SetRegistryKey(
113 HKEY_CLASSES_ROOT,
114 tmp.c_str(),
115 "",
116 WStringToString(COLUMN_HANDLER_DESCRIPTIVE_NAME).c_str()) ? S_OK : E_FAIL;
117 }
118
UnregisterColumnHandler()119 HRESULT UnregisterColumnHandler()
120 {
121 std::string tmp = "Folder\\shellex\\ColumnHandlers\\";
122 tmp += ClsidToString(CLSID_COLUMN_HANDLER);
123
124 if (!DeleteRegistryKey(HKEY_CLASSES_ROOT, tmp.c_str()))
125 return E_FAIL;
126
127 return UnregisterComComponent(CLSID_COLUMN_HANDLER);
128 }
129
RegisterInfotipHandler(const char * ModuleFileName)130 HRESULT RegisterInfotipHandler(const char* ModuleFileName)
131 {
132 if (FAILED(RegisterComComponent(ModuleFileName, CLSID_INFOTIP_HANDLER)))
133 return E_FAIL;
134
135 std::string iid = ClsidToString(IID_IQueryInfo);
136 std::string tmp;
137
138 for(size_t i = 0; i < OOFileExtensionTableSize; i++)
139 {
140 tmp = SHELLEX_IID_ENTRY;
141 SubstitutePlaceholder(tmp, EXTENSION_PLACEHOLDER, OOFileExtensionTable[i].ExtensionAnsi);
142 SubstitutePlaceholder(tmp, GUID_PLACEHOLDER, iid);
143
144 if (!SetRegistryKey(HKEY_CLASSES_ROOT, tmp.c_str(), "", ClsidToString(CLSID_INFOTIP_HANDLER).c_str()))
145 return E_FAIL;
146 }
147 return S_OK;
148 }
149
UnregisterInfotipHandler()150 HRESULT UnregisterInfotipHandler()
151 {
152 std::string iid = ClsidToString(IID_IQueryInfo);
153 std::string tmp;
154
155 for (size_t i = 0; i < OOFileExtensionTableSize; i++)
156 {
157 tmp = SHELLEX_IID_ENTRY;
158
159 SubstitutePlaceholder(tmp, EXTENSION_PLACEHOLDER, OOFileExtensionTable[i].ExtensionAnsi);
160 SubstitutePlaceholder(tmp, GUID_PLACEHOLDER, iid);
161
162 DeleteRegistryKey(HKEY_CLASSES_ROOT, tmp.c_str());
163
164 // if there are no further subkey below .ext\\shellex
165 // delete the whole subkey
166 tmp = SHELLEX_ENTRY;
167 SubstitutePlaceholder(tmp, EXTENSION_PLACEHOLDER, OOFileExtensionTable[i].ExtensionAnsi);
168
169 bool HasSubKeys = true;
170 if (HasSubkeysRegistryKey(HKEY_CLASSES_ROOT, tmp.c_str(), HasSubKeys) && !HasSubKeys)
171 DeleteRegistryKey(HKEY_CLASSES_ROOT, tmp.c_str());
172 }
173 return UnregisterComComponent(CLSID_INFOTIP_HANDLER);
174 }
175
RegisterPropSheetHandler(const char * ModuleFileName)176 HRESULT RegisterPropSheetHandler(const char* ModuleFileName)
177 {
178 std::string ExtEntry;
179 std::string FwdKeyEntry;
180
181 if (FAILED(RegisterComComponent(ModuleFileName, CLSID_PROPERTYSHEET_HANDLER)))
182 return E_FAIL;
183
184 for (size_t i = 0; i < OOFileExtensionTableSize; i++)
185 {
186 FwdKeyEntry = FORWARD_PROPSHEET_MYPROPSHEET_ENTRY;
187 SubstitutePlaceholder(FwdKeyEntry, FORWARDKEY_PLACEHOLDER, OOFileExtensionTable[i].RegistryForwardKey);
188
189 if (!SetRegistryKey(HKEY_CLASSES_ROOT, FwdKeyEntry.c_str(), "", ClsidToString(CLSID_PROPERTYSHEET_HANDLER).c_str()))
190 return E_FAIL;
191 }
192 return S_OK;
193 }
194
UnregisterPropSheetHandler()195 HRESULT UnregisterPropSheetHandler()
196 {
197 std::string ExtEntry;
198 std::string FwdKeyEntry;
199
200 for (size_t i = 0; i < OOFileExtensionTableSize; i++)
201 {
202 FwdKeyEntry = FORWARD_PROPSHEET_MYPROPSHEET_ENTRY;
203 SubstitutePlaceholder(FwdKeyEntry, FORWARDKEY_PLACEHOLDER, OOFileExtensionTable[i].RegistryForwardKey);
204
205 DeleteRegistryKey(HKEY_CLASSES_ROOT, FwdKeyEntry.c_str());
206
207 FwdKeyEntry = FORWARD_PROPSHEET_ENTRY;
208 SubstitutePlaceholder(FwdKeyEntry, FORWARDKEY_PLACEHOLDER, OOFileExtensionTable[i].RegistryForwardKey);
209
210 bool HasSubKeys = true;
211 if (HasSubkeysRegistryKey(HKEY_CLASSES_ROOT, FwdKeyEntry.c_str(), HasSubKeys) && !HasSubKeys)
212 DeleteRegistryKey(HKEY_CLASSES_ROOT, FwdKeyEntry.c_str());
213
214 FwdKeyEntry = FORWARD_SHELLEX_ENTRY;
215 SubstitutePlaceholder(FwdKeyEntry, FORWARDKEY_PLACEHOLDER, OOFileExtensionTable[i].RegistryForwardKey);
216
217 HasSubKeys = true;
218 if (HasSubkeysRegistryKey(HKEY_CLASSES_ROOT, FwdKeyEntry.c_str(), HasSubKeys) && !HasSubKeys)
219 DeleteRegistryKey(HKEY_CLASSES_ROOT, FwdKeyEntry.c_str());
220 }
221
222 return UnregisterComComponent(CLSID_PROPERTYSHEET_HANDLER);
223 }
224
RegisterThumbviewerHandler(const char * ModuleFileName)225 HRESULT RegisterThumbviewerHandler(const char* ModuleFileName)
226 {
227 if (FAILED(RegisterComComponent(ModuleFileName, CLSID_THUMBVIEWER_HANDLER)))
228 return E_FAIL;
229
230 std::string iid = ClsidToString(IID_IExtractImage);
231 std::string tmp;
232
233 for(size_t i = 0; i < OOFileExtensionTableSize; i++)
234 {
235 tmp = SHELLEX_IID_ENTRY;
236
237 SubstitutePlaceholder(tmp, EXTENSION_PLACEHOLDER, OOFileExtensionTable[i].ExtensionAnsi);
238 SubstitutePlaceholder(tmp, GUID_PLACEHOLDER, iid);
239
240 if (!SetRegistryKey(HKEY_CLASSES_ROOT, tmp.c_str(), "", ClsidToString(CLSID_THUMBVIEWER_HANDLER).c_str()))
241 return E_FAIL;
242 }
243 return S_OK;
244 }
245
UnregisterThumbviewerHandler()246 HRESULT UnregisterThumbviewerHandler()
247 {
248 std::string iid = ClsidToString(IID_IExtractImage);
249 std::string tmp;
250
251 for (size_t i = 0; i < OOFileExtensionTableSize; i++)
252 {
253 tmp = SHELLEX_IID_ENTRY;
254
255 SubstitutePlaceholder(tmp, EXTENSION_PLACEHOLDER, OOFileExtensionTable[i].ExtensionAnsi);
256 SubstitutePlaceholder(tmp, GUID_PLACEHOLDER, iid);
257
258 DeleteRegistryKey(HKEY_CLASSES_ROOT, tmp.c_str());
259
260 // if there are no further subkey below .ext\\shellex
261 // delete the whole subkey
262 tmp = SHELLEX_ENTRY;
263 SubstitutePlaceholder(tmp, EXTENSION_PLACEHOLDER, OOFileExtensionTable[i].ExtensionAnsi);
264
265 bool HasSubKeys = true;
266 if (HasSubkeysRegistryKey(HKEY_CLASSES_ROOT, tmp.c_str(), HasSubKeys) && !HasSubKeys)
267 DeleteRegistryKey(HKEY_CLASSES_ROOT, tmp.c_str());
268 }
269 return UnregisterComComponent(CLSID_THUMBVIEWER_HANDLER);
270 }
271
272 /** Approving/Unapproving the Shell Extension, it's important under Windows
273 NT/2000/XP, see MSDN: Creating Shell Extension Handlers */
ApproveShellExtension(CLSID clsid,const std::wstring & Description)274 HRESULT ApproveShellExtension(CLSID clsid, const std::wstring& Description)
275 {
276 bool bRet = SetRegistryKey(
277 HKEY_LOCAL_MACHINE,
278 SHELL_EXTENSION_APPROVED_KEY_NAME,
279 ClsidToString(clsid).c_str(),
280 WStringToString(Description).c_str());
281
282 return bRet ? S_OK : E_FAIL;
283 }
284
UnapproveShellExtension(CLSID Clsid)285 HRESULT UnapproveShellExtension(CLSID Clsid)
286 {
287 HKEY hkey;
288
289 LONG rc = RegOpenKeyA(
290 HKEY_LOCAL_MACHINE,
291 SHELL_EXTENSION_APPROVED_KEY_NAME,
292 &hkey);
293
294 if (ERROR_SUCCESS == rc)
295 {
296 rc = RegDeleteValueA(
297 hkey,
298 ClsidToString(Clsid).c_str());
299
300 rc = RegCloseKey(hkey);
301 }
302
303 return rc == ERROR_SUCCESS ? S_OK : E_FAIL;
304 }
305
306 } // namespace /* private */
307
308
309 //---------------------
310 // COM exports
311 //---------------------
312
DllRegisterServer()313 extern "C" STDAPI DllRegisterServer()
314 {
315 TCHAR ModuleFileName[MAX_PATH];
316
317 GetModuleFileName(
318 GetModuleHandle(MODULE_NAME),
319 ModuleFileName,
320 sizeof(ModuleFileName));
321
322 std::string module_path = WStringToString(ModuleFileName);
323 HRESULT hr = S_OK;
324
325 if (SUCCEEDED(RegisterColumnHandler(module_path.c_str())))
326 ApproveShellExtension(CLSID_COLUMN_HANDLER, COLUMN_HANDLER_DESCRIPTIVE_NAME);
327 else
328 hr = E_FAIL;
329
330 if (SUCCEEDED(RegisterInfotipHandler(module_path.c_str())))
331 ApproveShellExtension(CLSID_INFOTIP_HANDLER, INFOTIP_HANDLER_DESCRIPTIVE_NAME);
332 else
333 hr = E_FAIL;
334
335 if (SUCCEEDED(RegisterPropSheetHandler(module_path.c_str())))
336 ApproveShellExtension(CLSID_PROPERTYSHEET_HANDLER, PROPSHEET_HANDLER_DESCRIPTIVE_NAME);
337 else
338 hr = E_FAIL;
339
340 if (SUCCEEDED(RegisterThumbviewerHandler(module_path.c_str())))
341 ApproveShellExtension(CLSID_THUMBVIEWER_HANDLER, THUMBVIEWER_HANDLER_DESCRIPTIVE_NAME);
342 else
343 hr = E_FAIL;
344
345 // notify the Shell that something has changed
346 SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, 0, 0);
347
348 return hr;
349 }
350
DllUnregisterServer()351 extern "C" STDAPI DllUnregisterServer()
352 {
353 HRESULT hr = S_OK;
354
355 if (FAILED(UnregisterColumnHandler()))
356 hr = E_FAIL;
357
358 UnapproveShellExtension(CLSID_COLUMN_HANDLER);
359
360 if (FAILED(UnregisterInfotipHandler()))
361 hr = E_FAIL;
362
363 UnapproveShellExtension(CLSID_INFOTIP_HANDLER);
364
365 if (FAILED(UnregisterPropSheetHandler()))
366 hr = E_FAIL;
367
368 UnapproveShellExtension(CLSID_PROPERTYSHEET_HANDLER);
369
370 if (FAILED(UnregisterThumbviewerHandler()))
371 hr = E_FAIL;
372
373 UnapproveShellExtension(CLSID_THUMBVIEWER_HANDLER);
374
375 // notify the Shell that something has changed
376 SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, 0, 0);
377
378 return hr;
379 }
380
DllGetClassObject(REFCLSID rclsid,REFIID riid,void ** ppv)381 extern "C" STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void** ppv)
382 {
383 *ppv = 0;
384
385 if ((rclsid != CLSID_INFOTIP_HANDLER) &&
386 (rclsid != CLSID_COLUMN_HANDLER) &&
387 (rclsid != CLSID_PROPERTYSHEET_HANDLER) &&
388 (rclsid != CLSID_THUMBVIEWER_HANDLER))
389 return CLASS_E_CLASSNOTAVAILABLE;
390
391 if ((riid != IID_IUnknown) && (riid != IID_IClassFactory))
392 return E_NOINTERFACE;
393
394 if ( rclsid == CLSID_INFOTIP_HANDLER )
395 OutputDebugStringFormat( "DllGetClassObject: Create CLSID_INFOTIP_HANDLER\n" );
396 else if ( rclsid == CLSID_COLUMN_HANDLER )
397 OutputDebugStringFormat( "DllGetClassObject: Create CLSID_COLUMN_HANDLER\n" );
398 else if ( rclsid == CLSID_PROPERTYSHEET_HANDLER )
399 OutputDebugStringFormat( "DllGetClassObject: Create CLSID_PROPERTYSHEET_HANDLER\n" );
400 else if ( rclsid == CLSID_THUMBVIEWER_HANDLER )
401 OutputDebugStringFormat( "DllGetClassObject: Create CLSID_THUMBVIEWER_HANDLER\n" );
402
403 IUnknown* pUnk = new CClassFactory(rclsid);
404 if (0 == pUnk)
405 return E_OUTOFMEMORY;
406
407 *ppv = pUnk;
408 return S_OK;
409 }
410
DllCanUnloadNow(void)411 extern "C" STDAPI DllCanUnloadNow(void)
412 {
413 if (CClassFactory::IsLocked() || g_DllRefCnt > 0)
414 return S_FALSE;
415
416 return S_OK;
417 }
418
DllMain(HINSTANCE hInst,ULONG,LPVOID)419 BOOL WINAPI DllMain(HINSTANCE hInst, ULONG /*ul_reason_for_call*/, LPVOID /*lpReserved*/)
420 {
421 g_hModule = hInst;
422 return TRUE;
423 }
424
425 /* vim: set noet sw=4 ts=4: */
426