Implement DllRegisterServer and DllUnregisterServer for shdocvw.dll,
[wine] / dlls / shdocvw / regsvr.c
1 /*
2  *      self-registerable dll functions for shdocvw.dll
3  *
4  * Copyright (C) 2003 John K. Hohm
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <string.h>
22
23 #include "windef.h"
24 #include "winbase.h"
25 #include "winuser.h"
26 #include "winreg.h"
27 #include "winerror.h"
28
29 #include "shdocvw.h"
30
31 #include "wine/debug.h"
32
33 WINE_DEFAULT_DEBUG_CHANNEL(shdocvw);
34
35 /*
36  * Near the bottom of this file are the exported DllRegisterServer and
37  * DllUnregisterServer, which make all this worthwhile.
38  */
39
40 /***********************************************************************
41  *              interface for self-registering
42  */
43 struct regsvr_interface
44 {
45     IID const *iid;             /* NULL for end of list */
46     LPCSTR name;                /* can be NULL to omit */
47     IID const *base_iid;        /* can be NULL to omit */
48     int num_methods;            /* can be <0 to omit */
49     CLSID const *ps_clsid;      /* can be NULL to omit */
50     CLSID const *ps_clsid32;    /* can be NULL to omit */
51 };
52
53 static HRESULT register_interfaces(struct regsvr_interface const *list);
54 static HRESULT unregister_interfaces(struct regsvr_interface const *list);
55
56 struct regsvr_coclass
57 {
58     CLSID const *clsid;         /* NULL for end of list */
59     LPCSTR name;                /* can be NULL to omit */
60     LPCSTR ips;                 /* can be NULL to omit */
61     LPCSTR ips32;               /* can be NULL to omit */
62     LPCSTR ips32_tmodel;        /* can be NULL to omit */
63     LPCSTR progid;              /* can be NULL to omit */
64     LPCSTR viprogid;            /* can be NULL to omit */
65     LPCSTR progid_extra;        /* can be NULL to omit */
66 };
67
68 static HRESULT register_coclasses(struct regsvr_coclass const *list);
69 static HRESULT unregister_coclasses(struct regsvr_coclass const *list);
70
71 /***********************************************************************
72  *              static string constants
73  */
74 static WCHAR const interface_keyname[10] = {
75     'I', 'n', 't', 'e', 'r', 'f', 'a', 'c', 'e', 0 };
76 static WCHAR const base_ifa_keyname[14] = {
77     'B', 'a', 's', 'e', 'I', 'n', 't', 'e', 'r', 'f', 'a', 'c',
78     'e', 0 };
79 static WCHAR const num_methods_keyname[11] = {
80     'N', 'u', 'm', 'M', 'e', 't', 'h', 'o', 'd', 's', 0 };
81 static WCHAR const ps_clsid_keyname[15] = {
82     'P', 'r', 'o', 'x', 'y', 'S', 't', 'u', 'b', 'C', 'l', 's',
83     'i', 'd', 0 };
84 static WCHAR const ps_clsid32_keyname[17] = {
85     'P', 'r', 'o', 'x', 'y', 'S', 't', 'u', 'b', 'C', 'l', 's',
86     'i', 'd', '3', '2', 0 };
87 static WCHAR const clsid_keyname[6] = {
88     'C', 'L', 'S', 'I', 'D', 0 };
89 static WCHAR const curver_keyname[7] = {
90     'C', 'u', 'r', 'V', 'e', 'r', 0 };
91 static WCHAR const ips_keyname[13] = {
92     'I', 'n', 'P', 'r', 'o', 'c', 'S', 'e', 'r', 'v', 'e', 'r',
93     0 };
94 static WCHAR const ips32_keyname[15] = {
95     'I', 'n', 'P', 'r', 'o', 'c', 'S', 'e', 'r', 'v', 'e', 'r',
96     '3', '2', 0 };
97 static WCHAR const progid_keyname[7] = {
98     'P', 'r', 'o', 'g', 'I', 'D', 0 };
99 static WCHAR const viprogid_keyname[25] = {
100     'V', 'e', 'r', 's', 'i', 'o', 'n', 'I', 'n', 'd', 'e', 'p',
101     'e', 'n', 'd', 'e', 'n', 't', 'P', 'r', 'o', 'g', 'I', 'D',
102     0 };
103 static char const tmodel_valuename[] = "ThreadingModel";
104
105 /***********************************************************************
106  *              static helper functions
107  */
108 static LONG register_key_guid(HKEY base, WCHAR const *name, GUID const *guid);
109 static LONG register_key_defvalueW(HKEY base, WCHAR const *name,
110                                    WCHAR const *value);
111 static LONG register_key_defvalueA(HKEY base, WCHAR const *name,
112                                    char const *value);
113 static LONG register_progid(WCHAR const *clsid,
114                             char const *progid, char const *curver_progid,
115                             char const *name, char const *extra);
116 static LONG recursive_delete_key(HKEY key);
117 static LONG recursive_delete_keyA(HKEY base, char const *name);
118 static LONG recursive_delete_keyW(HKEY base, WCHAR const *name);
119
120 /***********************************************************************
121  *              register_interfaces
122  */
123 static HRESULT register_interfaces(struct regsvr_interface const *list)
124 {
125     LONG res = ERROR_SUCCESS;
126     HKEY interface_key;
127
128     res = RegCreateKeyExW(HKEY_CLASSES_ROOT, interface_keyname, 0, NULL, 0,
129                           KEY_READ | KEY_WRITE, NULL, &interface_key, NULL);
130     if (res != ERROR_SUCCESS) goto error_return;
131
132     for (; res == ERROR_SUCCESS && list->iid; ++list) {
133         WCHAR buf[39];
134         HKEY iid_key;
135
136         StringFromGUID2(list->iid, buf, 39);
137         res = RegCreateKeyExW(interface_key, buf, 0, NULL, 0,
138                               KEY_READ | KEY_WRITE, NULL, &iid_key, NULL);
139         if (res != ERROR_SUCCESS) goto error_close_interface_key;
140
141         if (list->name) {
142             res = RegSetValueExA(iid_key, NULL, 0, REG_SZ,
143                                  (CONST BYTE*)(list->name),
144                                  strlen(list->name) + 1);
145             if (res != ERROR_SUCCESS) goto error_close_iid_key;
146         }
147
148         if (list->base_iid) {
149             register_key_guid(iid_key, base_ifa_keyname, list->base_iid);
150             if (res != ERROR_SUCCESS) goto error_close_iid_key;
151         }
152
153         if (0 <= list->num_methods) {
154             static WCHAR const fmt[3] = { '%', 'd', 0 };
155             HKEY key;
156
157             res = RegCreateKeyExW(iid_key, num_methods_keyname, 0, NULL, 0,
158                                   KEY_READ | KEY_WRITE, NULL, &key, NULL);
159             if (res != ERROR_SUCCESS) goto error_close_iid_key;
160
161             wsprintfW(buf, fmt, list->num_methods);
162             res = RegSetValueExW(key, NULL, 0, REG_SZ,
163                                  (CONST BYTE*)buf,
164                                  (lstrlenW(buf) + 1) * sizeof(WCHAR));
165             RegCloseKey(key);
166
167             if (res != ERROR_SUCCESS) goto error_close_iid_key;
168         }
169
170         if (list->ps_clsid) {
171             register_key_guid(iid_key, ps_clsid_keyname, list->ps_clsid);
172             if (res != ERROR_SUCCESS) goto error_close_iid_key;
173         }
174
175         if (list->ps_clsid32) {
176             register_key_guid(iid_key, ps_clsid32_keyname, list->ps_clsid32);
177             if (res != ERROR_SUCCESS) goto error_close_iid_key;
178         }
179
180     error_close_iid_key:
181         RegCloseKey(iid_key);
182     }
183
184 error_close_interface_key:
185     RegCloseKey(interface_key);
186 error_return:
187     return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK;
188 }
189
190 /***********************************************************************
191  *              unregister_interfaces
192  */
193 static HRESULT unregister_interfaces(struct regsvr_interface const *list)
194 {
195     LONG res = ERROR_SUCCESS;
196     HKEY interface_key;
197
198     res = RegOpenKeyExW(HKEY_CLASSES_ROOT, interface_keyname, 0,
199                         KEY_READ | KEY_WRITE, &interface_key);
200     if (res == ERROR_FILE_NOT_FOUND) return S_OK;
201     if (res != ERROR_SUCCESS) goto error_return;
202
203     for (; res == ERROR_SUCCESS && list->iid; ++list) {
204         WCHAR buf[39];
205
206         StringFromGUID2(list->iid, buf, 39);
207         res = recursive_delete_keyW(interface_key, buf);
208     }
209
210     RegCloseKey(interface_key);
211 error_return:
212     return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK;
213 }
214
215 /***********************************************************************
216  *              register_coclasses
217  */
218 static HRESULT register_coclasses(struct regsvr_coclass const *list)
219 {
220     LONG res = ERROR_SUCCESS;
221     HKEY coclass_key;
222
223     res = RegCreateKeyExW(HKEY_CLASSES_ROOT, clsid_keyname, 0, NULL, 0,
224                           KEY_READ | KEY_WRITE, NULL, &coclass_key, NULL);
225     if (res != ERROR_SUCCESS) goto error_return;
226
227     for (; res == ERROR_SUCCESS && list->clsid; ++list) {
228         WCHAR buf[39];
229         HKEY clsid_key;
230
231         StringFromGUID2(list->clsid, buf, 39);
232         res = RegCreateKeyExW(coclass_key, buf, 0, NULL, 0,
233                               KEY_READ | KEY_WRITE, NULL, &clsid_key, NULL);
234         if (res != ERROR_SUCCESS) goto error_close_coclass_key;
235
236         if (list->name) {
237             res = RegSetValueExA(clsid_key, NULL, 0, REG_SZ,
238                                  (CONST BYTE*)(list->name),
239                                  strlen(list->name) + 1);
240             if (res != ERROR_SUCCESS) goto error_close_clsid_key;
241         }
242
243         if (list->ips) {
244             res = register_key_defvalueA(clsid_key, ips_keyname, list->ips);
245             if (res != ERROR_SUCCESS) goto error_close_clsid_key;
246         }
247
248         if (list->ips32) {
249             HKEY ips32_key;
250
251             res = RegCreateKeyExW(clsid_key, ips32_keyname, 0, NULL, 0,
252                                   KEY_READ | KEY_WRITE, NULL,
253                                   &ips32_key, NULL);
254             if (res != ERROR_SUCCESS) goto error_close_clsid_key;
255
256             res = RegSetValueExA(ips32_key, NULL, 0, REG_SZ,
257                                  (CONST BYTE*)list->ips32,
258                                  lstrlenA(list->ips32) + 1);
259             if (res == ERROR_SUCCESS && list->ips32_tmodel)
260                 res = RegSetValueExA(ips32_key, tmodel_valuename, 0, REG_SZ,
261                                      (CONST BYTE*)list->ips32_tmodel,
262                                      strlen(list->ips32_tmodel) + 1);
263             RegCloseKey(ips32_key);
264             if (res != ERROR_SUCCESS) goto error_close_clsid_key;
265         }
266
267         if (list->progid) {
268             res = register_key_defvalueA(clsid_key, progid_keyname,
269                                          list->progid);
270             if (res != ERROR_SUCCESS) goto error_close_clsid_key;
271
272             res = register_progid(buf, list->progid, NULL,
273                                   list->name, list->progid_extra);
274             if (res != ERROR_SUCCESS) goto error_close_clsid_key;
275         }
276
277         if (list->viprogid) {
278             res = register_key_defvalueA(clsid_key, viprogid_keyname,
279                                          list->viprogid);
280             if (res != ERROR_SUCCESS) goto error_close_clsid_key;
281
282             res = register_progid(buf, list->viprogid, list->progid,
283                                   list->name, list->progid_extra);
284             if (res != ERROR_SUCCESS) goto error_close_clsid_key;
285         }
286
287     error_close_clsid_key:
288         RegCloseKey(clsid_key);
289     }
290
291 error_close_coclass_key:
292     RegCloseKey(coclass_key);
293 error_return:
294     return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK;
295 }
296
297 /***********************************************************************
298  *              unregister_coclasses
299  */
300 static HRESULT unregister_coclasses(struct regsvr_coclass const *list)
301 {
302     LONG res = ERROR_SUCCESS;
303     HKEY coclass_key;
304
305     res = RegOpenKeyExW(HKEY_CLASSES_ROOT, clsid_keyname, 0,
306                         KEY_READ | KEY_WRITE, &coclass_key);
307     if (res == ERROR_FILE_NOT_FOUND) return S_OK;
308     if (res != ERROR_SUCCESS) goto error_return;
309
310     for (; res == ERROR_SUCCESS && list->clsid; ++list) {
311         WCHAR buf[39];
312
313         StringFromGUID2(list->clsid, buf, 39);
314         res = recursive_delete_keyW(coclass_key, buf);
315         if (res != ERROR_SUCCESS) goto error_close_coclass_key;
316
317         if (list->progid) {
318             res = recursive_delete_keyA(HKEY_CLASSES_ROOT, list->progid);
319             if (res != ERROR_SUCCESS) goto error_close_coclass_key;
320         }
321
322         if (list->viprogid) {
323             res = recursive_delete_keyA(HKEY_CLASSES_ROOT, list->viprogid);
324             if (res != ERROR_SUCCESS) goto error_close_coclass_key;
325         }
326     }
327
328 error_close_coclass_key:
329     RegCloseKey(coclass_key);
330 error_return:
331     return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK;
332 }
333
334 /***********************************************************************
335  *              regsvr_key_guid
336  */
337 static LONG register_key_guid(HKEY base, WCHAR const *name, GUID const *guid)
338 {
339     WCHAR buf[39];
340
341     StringFromGUID2(guid, buf, 39);
342     return register_key_defvalueW(base, name, buf);
343 }
344
345 /***********************************************************************
346  *              regsvr_key_defvalueW
347  */
348 static LONG register_key_defvalueW(
349     HKEY base,
350     WCHAR const *name,
351     WCHAR const *value)
352 {
353     LONG res;
354     HKEY key;
355
356     res = RegCreateKeyExW(base, name, 0, NULL, 0,
357                           KEY_READ | KEY_WRITE, NULL, &key, NULL);
358     if (res != ERROR_SUCCESS) return res;
359     res = RegSetValueExW(key, NULL, 0, REG_SZ, (CONST BYTE*)value,
360                          (lstrlenW(value) + 1) * sizeof(WCHAR));
361     RegCloseKey(key);
362     return res;
363 }
364
365 /***********************************************************************
366  *              regsvr_key_defvalueA
367  */
368 static LONG register_key_defvalueA(
369     HKEY base,
370     WCHAR const *name,
371     char const *value)
372 {
373     LONG res;
374     HKEY key;
375
376     res = RegCreateKeyExW(base, name, 0, NULL, 0,
377                           KEY_READ | KEY_WRITE, NULL, &key, NULL);
378     if (res != ERROR_SUCCESS) return res;
379     res = RegSetValueExA(key, NULL, 0, REG_SZ, (CONST BYTE*)value,
380                          lstrlenA(value) + 1);
381     RegCloseKey(key);
382     return res;
383 }
384
385 /***********************************************************************
386  *              regsvr_progid
387  */
388 static LONG register_progid(
389     WCHAR const *clsid,
390     char const *progid,
391     char const *curver_progid,
392     char const *name,
393     char const *extra)
394 {
395     LONG res;
396     HKEY progid_key;
397
398     res = RegCreateKeyExA(HKEY_CLASSES_ROOT, progid, 0,
399                           NULL, 0, KEY_READ | KEY_WRITE, NULL,
400                           &progid_key, NULL);
401     if (res != ERROR_SUCCESS) return res;
402
403     if (name) {
404         res = RegSetValueExA(progid_key, NULL, 0, REG_SZ,
405                              (CONST BYTE*)name, strlen(name) + 1);
406         if (res != ERROR_SUCCESS) goto error_close_progid_key;
407     }
408
409     if (clsid) {
410         res = register_key_defvalueW(progid_key, clsid_keyname, clsid);
411         if (res != ERROR_SUCCESS) goto error_close_progid_key;
412     }
413
414     if (curver_progid) {
415         res = register_key_defvalueA(progid_key, curver_keyname,
416                                      curver_progid);
417         if (res != ERROR_SUCCESS) goto error_close_progid_key;
418     }
419
420     if (extra) {
421         HKEY extra_key;
422
423         res = RegCreateKeyExA(progid_key, extra, 0,
424                               NULL, 0, KEY_READ | KEY_WRITE, NULL,
425                               &extra_key, NULL);
426         if (res == ERROR_SUCCESS)
427             RegCloseKey(extra_key);
428     }
429
430 error_close_progid_key:
431     RegCloseKey(progid_key);
432     return res;
433 }
434
435 /***********************************************************************
436  *              recursive_delete_key
437  */
438 static LONG recursive_delete_key(HKEY key)
439 {
440     LONG res;
441     WCHAR subkey_name[MAX_PATH];
442     DWORD cName;
443     HKEY subkey;
444
445     for (;;) {
446         cName = sizeof(subkey_name) / sizeof(WCHAR);
447         res = RegEnumKeyExW(key, 0, subkey_name, &cName,
448                             NULL, NULL, NULL, NULL);
449         if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA) {
450             res = ERROR_SUCCESS; /* presumably we're done enumerating */
451             break;
452         }
453         res = RegOpenKeyExW(key, subkey_name, 0,
454                             KEY_READ | KEY_WRITE, &subkey);
455         if (res == ERROR_FILE_NOT_FOUND) continue;
456         if (res != ERROR_SUCCESS) break;
457
458         res = recursive_delete_key(subkey);
459         RegCloseKey(subkey);
460         if (res != ERROR_SUCCESS) break;
461     }
462
463     if (res == ERROR_SUCCESS) res = RegDeleteKeyW(key, 0);
464     return res;
465 }
466
467 /***********************************************************************
468  *              recursive_delete_keyA
469  */
470 static LONG recursive_delete_keyA(HKEY base, char const *name)
471 {
472     LONG res;
473     HKEY key;
474
475     res = RegOpenKeyExA(base, name, 0, KEY_READ | KEY_WRITE, &key);
476     if (res == ERROR_FILE_NOT_FOUND) return ERROR_SUCCESS;
477     if (res != ERROR_SUCCESS) return res;
478     res = recursive_delete_key(key);
479     RegCloseKey(key);
480     return res;
481 }
482
483 /***********************************************************************
484  *              recursive_delete_keyW
485  */
486 static LONG recursive_delete_keyW(HKEY base, WCHAR const *name)
487 {
488     LONG res;
489     HKEY key;
490
491     res = RegOpenKeyExW(base, name, 0, KEY_READ | KEY_WRITE, &key);
492     if (res == ERROR_FILE_NOT_FOUND) return ERROR_SUCCESS;
493     if (res != ERROR_SUCCESS) return res;
494     res = recursive_delete_key(key);
495     RegCloseKey(key);
496     return res;
497 }
498
499 /***********************************************************************
500  *              coclass list
501  */
502 static struct regsvr_coclass const coclass_list[] = {
503     {   &CLSID_WebBrowser,
504         "Microsoft Web Browser",
505         NULL,
506         "shdocvw.dll",
507         "Apartment",
508         "Shell.Explorer.2",
509         "Shell.Explorer"
510     },
511     { NULL }                    /* list terminator */
512 };
513
514 /***********************************************************************
515  *              interface list
516  */
517
518 static struct regsvr_interface const interface_list[] = {
519     { NULL }                    /* list terminator */
520 };
521
522 /***********************************************************************
523  *              DllRegisterServer (SHDOCVW.@)
524  */
525 HRESULT WINAPI SHDOCVW_DllRegisterServer(void)
526 {
527     HRESULT hr;
528
529     TRACE("\n");
530
531     hr = register_coclasses(coclass_list);
532     if (SUCCEEDED(hr))
533         hr = register_interfaces(interface_list);
534     return hr;
535 }
536
537 /***********************************************************************
538  *              DllUnregisterServer (SHDOCVW.@)
539  */
540 HRESULT WINAPI SHDOCVW_DllUnregisterServer(void)
541 {
542     HRESULT hr;
543
544     TRACE("\n");
545
546     hr = unregister_coclasses(coclass_list);
547     if (SUCCEEDED(hr))
548         hr = unregister_interfaces(interface_list);
549     return hr;
550 }