advapi32: Implement and test SystemFunction004.
[wine] / dlls / urlmon / regsvr.c
1 /*
2  *      self-registerable dll functions for urlmon.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 #define COM_NO_WINDOWS_H
22 #include <stdarg.h>
23 #include <stdio.h>
24 #include <string.h>
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winuser.h"
29 #include "wingdi.h"
30 #include "winreg.h"
31 #include "winerror.h"
32 #include "advpub.h"
33
34 #include "objbase.h"
35
36 #include "urlmon.h"
37
38 #include "wine/debug.h"
39
40 #include "urlmon_main.h"
41
42 WINE_DEFAULT_DEBUG_CHANNEL(urlmon);
43
44 /*
45  * Near the bottom of this file are the exported DllRegisterServer and
46  * DllUnregisterServer, which make all this worthwhile.
47  */
48
49 /***********************************************************************
50  *              interface for self-registering
51  */
52 struct regsvr_interface
53 {
54     IID const *iid;             /* NULL for end of list */
55     LPCSTR name;                /* can be NULL to omit */
56     IID const *base_iid;        /* can be NULL to omit */
57     int num_methods;            /* can be <0 to omit */
58     CLSID const *ps_clsid;      /* can be NULL to omit */
59     CLSID const *ps_clsid32;    /* can be NULL to omit */
60 };
61
62 static HRESULT register_interfaces(struct regsvr_interface const *list);
63 static HRESULT unregister_interfaces(struct regsvr_interface const *list);
64
65 struct regsvr_coclass
66 {
67     CLSID const *clsid;         /* NULL for end of list */
68     LPCSTR name;                /* can be NULL to omit */
69     LPCSTR ips;                 /* can be NULL to omit */
70     LPCSTR ips32;               /* can be NULL to omit */
71     LPCSTR ips32_tmodel;        /* can be NULL to omit */
72     LPCSTR progid;              /* can be NULL to omit */
73     LPCSTR viprogid;            /* can be NULL to omit */
74     LPCSTR progid_extra;        /* can be NULL to omit */
75 };
76
77 static HRESULT register_coclasses(struct regsvr_coclass const *list);
78 static HRESULT unregister_coclasses(struct regsvr_coclass const *list);
79
80 /***********************************************************************
81  *              static string constants
82  */
83 static WCHAR const interface_keyname[10] = {
84     'I', 'n', 't', 'e', 'r', 'f', 'a', 'c', 'e', 0 };
85 static WCHAR const base_ifa_keyname[14] = {
86     'B', 'a', 's', 'e', 'I', 'n', 't', 'e', 'r', 'f', 'a', 'c',
87     'e', 0 };
88 static WCHAR const num_methods_keyname[11] = {
89     'N', 'u', 'm', 'M', 'e', 't', 'h', 'o', 'd', 's', 0 };
90 static WCHAR const ps_clsid_keyname[15] = {
91     'P', 'r', 'o', 'x', 'y', 'S', 't', 'u', 'b', 'C', 'l', 's',
92     'i', 'd', 0 };
93 static WCHAR const ps_clsid32_keyname[17] = {
94     'P', 'r', 'o', 'x', 'y', 'S', 't', 'u', 'b', 'C', 'l', 's',
95     'i', 'd', '3', '2', 0 };
96 static WCHAR const clsid_keyname[6] = {
97     'C', 'L', 'S', 'I', 'D', 0 };
98 static WCHAR const curver_keyname[7] = {
99     'C', 'u', 'r', 'V', 'e', 'r', 0 };
100 static WCHAR const ips_keyname[13] = {
101     'I', 'n', 'P', 'r', 'o', 'c', 'S', 'e', 'r', 'v', 'e', 'r',
102     0 };
103 static WCHAR const ips32_keyname[15] = {
104     'I', 'n', 'P', 'r', 'o', 'c', 'S', 'e', 'r', 'v', 'e', 'r',
105     '3', '2', 0 };
106 static WCHAR const progid_keyname[7] = {
107     'P', 'r', 'o', 'g', 'I', 'D', 0 };
108 static WCHAR const viprogid_keyname[25] = {
109     'V', 'e', 'r', 's', 'i', 'o', 'n', 'I', 'n', 'd', 'e', 'p',
110     'e', 'n', 'd', 'e', 'n', 't', 'P', 'r', 'o', 'g', 'I', 'D',
111     0 };
112 static char const tmodel_valuename[] = "ThreadingModel";
113
114 /***********************************************************************
115  *              static helper functions
116  */
117 static LONG register_key_guid(HKEY base, WCHAR const *name, GUID const *guid);
118 static LONG register_key_defvalueW(HKEY base, WCHAR const *name,
119                                    WCHAR const *value);
120 static LONG register_key_defvalueA(HKEY base, WCHAR const *name,
121                                    char const *value);
122 static LONG register_progid(WCHAR const *clsid,
123                             char const *progid, char const *curver_progid,
124                             char const *name, char const *extra);
125 static LONG recursive_delete_key(HKEY key);
126 static LONG recursive_delete_keyA(HKEY base, char const *name);
127 static LONG recursive_delete_keyW(HKEY base, WCHAR const *name);
128
129 /***********************************************************************
130  *              register_interfaces
131  */
132 static HRESULT register_interfaces(struct regsvr_interface const *list)
133 {
134     LONG res = ERROR_SUCCESS;
135     HKEY interface_key;
136
137     res = RegCreateKeyExW(HKEY_CLASSES_ROOT, interface_keyname, 0, NULL, 0,
138                           KEY_READ | KEY_WRITE, NULL, &interface_key, NULL);
139     if (res != ERROR_SUCCESS) goto error_return;
140
141     for (; res == ERROR_SUCCESS && list->iid; ++list) {
142         WCHAR buf[39];
143         HKEY iid_key;
144
145         StringFromGUID2(list->iid, buf, 39);
146         res = RegCreateKeyExW(interface_key, buf, 0, NULL, 0,
147                               KEY_READ | KEY_WRITE, NULL, &iid_key, NULL);
148         if (res != ERROR_SUCCESS) goto error_close_interface_key;
149
150         if (list->name) {
151             res = RegSetValueExA(iid_key, NULL, 0, REG_SZ,
152                                  (CONST BYTE*)(list->name),
153                                  strlen(list->name) + 1);
154             if (res != ERROR_SUCCESS) goto error_close_iid_key;
155         }
156
157         if (list->base_iid) {
158             res = register_key_guid(iid_key, base_ifa_keyname, list->base_iid);
159             if (res != ERROR_SUCCESS) goto error_close_iid_key;
160         }
161
162         if (0 <= list->num_methods) {
163             static WCHAR const fmt[3] = { '%', 'd', 0 };
164             HKEY key;
165
166             res = RegCreateKeyExW(iid_key, num_methods_keyname, 0, NULL, 0,
167                                   KEY_READ | KEY_WRITE, NULL, &key, NULL);
168             if (res != ERROR_SUCCESS) goto error_close_iid_key;
169
170             wsprintfW(buf, fmt, list->num_methods);
171             res = RegSetValueExW(key, NULL, 0, REG_SZ,
172                                  (CONST BYTE*)buf,
173                                  (lstrlenW(buf) + 1) * sizeof(WCHAR));
174             RegCloseKey(key);
175
176             if (res != ERROR_SUCCESS) goto error_close_iid_key;
177         }
178
179         if (list->ps_clsid) {
180             register_key_guid(iid_key, ps_clsid_keyname, list->ps_clsid);
181             if (res != ERROR_SUCCESS) goto error_close_iid_key;
182         }
183
184         if (list->ps_clsid32) {
185             register_key_guid(iid_key, ps_clsid32_keyname, list->ps_clsid32);
186             if (res != ERROR_SUCCESS) goto error_close_iid_key;
187         }
188
189     error_close_iid_key:
190         RegCloseKey(iid_key);
191     }
192
193 error_close_interface_key:
194     RegCloseKey(interface_key);
195 error_return:
196     return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK;
197 }
198
199 /***********************************************************************
200  *              unregister_interfaces
201  */
202 static HRESULT unregister_interfaces(struct regsvr_interface const *list)
203 {
204     LONG res = ERROR_SUCCESS;
205     HKEY interface_key;
206
207     res = RegOpenKeyExW(HKEY_CLASSES_ROOT, interface_keyname, 0,
208                         KEY_READ | KEY_WRITE, &interface_key);
209     if (res == ERROR_FILE_NOT_FOUND) return S_OK;
210     if (res != ERROR_SUCCESS) goto error_return;
211
212     for (; res == ERROR_SUCCESS && list->iid; ++list) {
213         WCHAR buf[39];
214
215         StringFromGUID2(list->iid, buf, 39);
216         res = recursive_delete_keyW(interface_key, buf);
217     }
218
219     RegCloseKey(interface_key);
220 error_return:
221     return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK;
222 }
223
224 /***********************************************************************
225  *              register_coclasses
226  */
227 static HRESULT register_coclasses(struct regsvr_coclass const *list)
228 {
229     LONG res = ERROR_SUCCESS;
230     HKEY coclass_key;
231
232     res = RegCreateKeyExW(HKEY_CLASSES_ROOT, clsid_keyname, 0, NULL, 0,
233                           KEY_READ | KEY_WRITE, NULL, &coclass_key, NULL);
234     if (res != ERROR_SUCCESS) goto error_return;
235
236     for (; res == ERROR_SUCCESS && list->clsid; ++list) {
237         WCHAR buf[39];
238         HKEY clsid_key;
239
240         StringFromGUID2(list->clsid, buf, 39);
241         res = RegCreateKeyExW(coclass_key, buf, 0, NULL, 0,
242                               KEY_READ | KEY_WRITE, NULL, &clsid_key, NULL);
243         if (res != ERROR_SUCCESS) goto error_close_coclass_key;
244
245         if (list->name) {
246             res = RegSetValueExA(clsid_key, NULL, 0, REG_SZ,
247                                  (CONST BYTE*)(list->name),
248                                  strlen(list->name) + 1);
249             if (res != ERROR_SUCCESS) goto error_close_clsid_key;
250         }
251
252         if (list->ips) {
253             res = register_key_defvalueA(clsid_key, ips_keyname, list->ips);
254             if (res != ERROR_SUCCESS) goto error_close_clsid_key;
255         }
256
257         if (list->ips32) {
258             HKEY ips32_key;
259
260             res = RegCreateKeyExW(clsid_key, ips32_keyname, 0, NULL, 0,
261                                   KEY_READ | KEY_WRITE, NULL,
262                                   &ips32_key, NULL);
263             if (res != ERROR_SUCCESS) goto error_close_clsid_key;
264
265             res = RegSetValueExA(ips32_key, NULL, 0, REG_SZ,
266                                  (CONST BYTE*)list->ips32,
267                                  lstrlenA(list->ips32) + 1);
268             if (res == ERROR_SUCCESS && list->ips32_tmodel)
269                 res = RegSetValueExA(ips32_key, tmodel_valuename, 0, REG_SZ,
270                                      (CONST BYTE*)list->ips32_tmodel,
271                                      strlen(list->ips32_tmodel) + 1);
272             RegCloseKey(ips32_key);
273             if (res != ERROR_SUCCESS) goto error_close_clsid_key;
274         }
275
276         if (list->progid) {
277             res = register_key_defvalueA(clsid_key, progid_keyname,
278                                          list->progid);
279             if (res != ERROR_SUCCESS) goto error_close_clsid_key;
280
281             res = register_progid(buf, list->progid, NULL,
282                                   list->name, list->progid_extra);
283             if (res != ERROR_SUCCESS) goto error_close_clsid_key;
284         }
285
286         if (list->viprogid) {
287             res = register_key_defvalueA(clsid_key, viprogid_keyname,
288                                          list->viprogid);
289             if (res != ERROR_SUCCESS) goto error_close_clsid_key;
290
291             res = register_progid(buf, list->viprogid, list->progid,
292                                   list->name, list->progid_extra);
293             if (res != ERROR_SUCCESS) goto error_close_clsid_key;
294         }
295
296     error_close_clsid_key:
297         RegCloseKey(clsid_key);
298     }
299
300 error_close_coclass_key:
301     RegCloseKey(coclass_key);
302 error_return:
303     return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK;
304 }
305
306 /***********************************************************************
307  *              unregister_coclasses
308  */
309 static HRESULT unregister_coclasses(struct regsvr_coclass const *list)
310 {
311     LONG res = ERROR_SUCCESS;
312     HKEY coclass_key;
313
314     res = RegOpenKeyExW(HKEY_CLASSES_ROOT, clsid_keyname, 0,
315                         KEY_READ | KEY_WRITE, &coclass_key);
316     if (res == ERROR_FILE_NOT_FOUND) return S_OK;
317     if (res != ERROR_SUCCESS) goto error_return;
318
319     for (; res == ERROR_SUCCESS && list->clsid; ++list) {
320         WCHAR buf[39];
321
322         StringFromGUID2(list->clsid, buf, 39);
323         res = recursive_delete_keyW(coclass_key, buf);
324         if (res != ERROR_SUCCESS) goto error_close_coclass_key;
325
326         if (list->progid) {
327             res = recursive_delete_keyA(HKEY_CLASSES_ROOT, list->progid);
328             if (res != ERROR_SUCCESS) goto error_close_coclass_key;
329         }
330
331         if (list->viprogid) {
332             res = recursive_delete_keyA(HKEY_CLASSES_ROOT, list->viprogid);
333             if (res != ERROR_SUCCESS) goto error_close_coclass_key;
334         }
335     }
336
337 error_close_coclass_key:
338     RegCloseKey(coclass_key);
339 error_return:
340     return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK;
341 }
342
343 /***********************************************************************
344  *              regsvr_key_guid
345  */
346 static LONG register_key_guid(HKEY base, WCHAR const *name, GUID const *guid)
347 {
348     WCHAR buf[39];
349
350     StringFromGUID2(guid, buf, 39);
351     return register_key_defvalueW(base, name, buf);
352 }
353
354 /***********************************************************************
355  *              regsvr_key_defvalueW
356  */
357 static LONG register_key_defvalueW(
358     HKEY base,
359     WCHAR const *name,
360     WCHAR const *value)
361 {
362     LONG res;
363     HKEY key;
364
365     res = RegCreateKeyExW(base, name, 0, NULL, 0,
366                           KEY_READ | KEY_WRITE, NULL, &key, NULL);
367     if (res != ERROR_SUCCESS) return res;
368     res = RegSetValueExW(key, NULL, 0, REG_SZ, (CONST BYTE*)value,
369                          (lstrlenW(value) + 1) * sizeof(WCHAR));
370     RegCloseKey(key);
371     return res;
372 }
373
374 /***********************************************************************
375  *              regsvr_key_defvalueA
376  */
377 static LONG register_key_defvalueA(
378     HKEY base,
379     WCHAR const *name,
380     char const *value)
381 {
382     LONG res;
383     HKEY key;
384
385     res = RegCreateKeyExW(base, name, 0, NULL, 0,
386                           KEY_READ | KEY_WRITE, NULL, &key, NULL);
387     if (res != ERROR_SUCCESS) return res;
388     res = RegSetValueExA(key, NULL, 0, REG_SZ, (CONST BYTE*)value,
389                          lstrlenA(value) + 1);
390     RegCloseKey(key);
391     return res;
392 }
393
394 /***********************************************************************
395  *              regsvr_progid
396  */
397 static LONG register_progid(
398     WCHAR const *clsid,
399     char const *progid,
400     char const *curver_progid,
401     char const *name,
402     char const *extra)
403 {
404     LONG res;
405     HKEY progid_key;
406
407     res = RegCreateKeyExA(HKEY_CLASSES_ROOT, progid, 0,
408                           NULL, 0, KEY_READ | KEY_WRITE, NULL,
409                           &progid_key, NULL);
410     if (res != ERROR_SUCCESS) return res;
411
412     if (name) {
413         res = RegSetValueExA(progid_key, NULL, 0, REG_SZ,
414                              (CONST BYTE*)name, strlen(name) + 1);
415         if (res != ERROR_SUCCESS) goto error_close_progid_key;
416     }
417
418     if (clsid) {
419         res = register_key_defvalueW(progid_key, clsid_keyname, clsid);
420         if (res != ERROR_SUCCESS) goto error_close_progid_key;
421     }
422
423     if (curver_progid) {
424         res = register_key_defvalueA(progid_key, curver_keyname,
425                                      curver_progid);
426         if (res != ERROR_SUCCESS) goto error_close_progid_key;
427     }
428
429     if (extra) {
430         HKEY extra_key;
431
432         res = RegCreateKeyExA(progid_key, extra, 0,
433                               NULL, 0, KEY_READ | KEY_WRITE, NULL,
434                               &extra_key, NULL);
435         if (res == ERROR_SUCCESS)
436             RegCloseKey(extra_key);
437     }
438
439 error_close_progid_key:
440     RegCloseKey(progid_key);
441     return res;
442 }
443
444 /***********************************************************************
445  *              recursive_delete_key
446  */
447 static LONG recursive_delete_key(HKEY key)
448 {
449     LONG res;
450     WCHAR subkey_name[MAX_PATH];
451     DWORD cName;
452     HKEY subkey;
453
454     for (;;) {
455         cName = sizeof(subkey_name) / sizeof(WCHAR);
456         res = RegEnumKeyExW(key, 0, subkey_name, &cName,
457                             NULL, NULL, NULL, NULL);
458         if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA) {
459             res = ERROR_SUCCESS; /* presumably we're done enumerating */
460             break;
461         }
462         res = RegOpenKeyExW(key, subkey_name, 0,
463                             KEY_READ | KEY_WRITE, &subkey);
464         if (res == ERROR_FILE_NOT_FOUND) continue;
465         if (res != ERROR_SUCCESS) break;
466
467         res = recursive_delete_key(subkey);
468         RegCloseKey(subkey);
469         if (res != ERROR_SUCCESS) break;
470     }
471
472     if (res == ERROR_SUCCESS) res = RegDeleteKeyW(key, 0);
473     return res;
474 }
475
476 /***********************************************************************
477  *              recursive_delete_keyA
478  */
479 static LONG recursive_delete_keyA(HKEY base, char const *name)
480 {
481     LONG res;
482     HKEY key;
483
484     res = RegOpenKeyExA(base, name, 0, KEY_READ | KEY_WRITE, &key);
485     if (res == ERROR_FILE_NOT_FOUND) return ERROR_SUCCESS;
486     if (res != ERROR_SUCCESS) return res;
487     res = recursive_delete_key(key);
488     RegCloseKey(key);
489     return res;
490 }
491
492 /***********************************************************************
493  *              recursive_delete_keyW
494  */
495 static LONG recursive_delete_keyW(HKEY base, WCHAR const *name)
496 {
497     LONG res;
498     HKEY key;
499
500     res = RegOpenKeyExW(base, name, 0, KEY_READ | KEY_WRITE, &key);
501     if (res == ERROR_FILE_NOT_FOUND) return ERROR_SUCCESS;
502     if (res != ERROR_SUCCESS) return res;
503     res = recursive_delete_key(key);
504     RegCloseKey(key);
505     return res;
506 }
507
508 /***********************************************************************
509  *              coclass list
510  */
511 static struct regsvr_coclass const coclass_list[] = {
512     {   &CLSID_StdURLMoniker,
513         "URL Moniker",
514         NULL,
515         "urlmon.dll",
516         "Apartment"
517     },
518     {   &CLSID_CdlProtocol,
519         "CDL: Asynchronous Pluggable Protocol Handler",
520         NULL,
521         "urlmon.dll",
522         "Apartment"
523     },
524     {   &CLSID_FileProtocol,
525         "file:, local: Asynchronous Pluggable Protocol Handler",
526         NULL,
527         "urlmon.dll",
528         "Apartment"
529     },
530     {   &CLSID_FtpProtocol,
531         "ftp: Asynchronous Pluggable Protocol Handler",
532         NULL,
533         "urlmon.dll",
534         "Apartment"
535     },
536     {   &CLSID_GopherProtocol,
537         "gopher: Asynchronous Pluggable Protocol Handler",
538         NULL,
539         "urlmon.dll",
540         "Apartment"
541     },
542     {   &CLSID_HttpProtocol,
543         "http: Asynchronous Pluggable Protocol Handler",
544         NULL,
545         "urlmon.dll",
546         "Apartment"
547     },
548     {   &CLSID_HttpSProtocol,
549         "https: Asynchronous Pluggable Protocol Handler",
550         NULL,
551         "urlmon.dll",
552         "Apartment"
553     },
554     {   &CLSID_MkProtocol,
555         "mk: Asynchronous Pluggable Protocol Handler",
556         NULL,
557         "urlmon.dll",
558         "Apartment"
559     },
560     { NULL }                    /* list terminator */
561 };
562
563 /***********************************************************************
564  *              interface list
565  */
566
567 static struct regsvr_interface const interface_list[] = {
568     { NULL }                    /* list terminator */
569 };
570
571 /***********************************************************************
572  *              register_inf
573  */
574
575 #define INF_SET_CLSID(clsid) \
576     pse[i].pszName = "CLSID_" #clsid; \
577     clsids[i++] = &CLSID_ ## clsid;
578
579 static HRESULT register_inf(BOOL doregister)
580 {
581     HRESULT hres;
582     HMODULE hAdvpack;
583     typeof(RegInstallA) *pRegInstall;
584     STRTABLEA strtable;
585     STRENTRYA pse[7];
586     static CLSID const *clsids[34];
587     int i = 0;
588
589     static const WCHAR wszAdvpack[] = {'a','d','v','p','a','c','k','.','d','l','l',0};
590     
591     INF_SET_CLSID(CdlProtocol);
592     INF_SET_CLSID(FileProtocol);
593     INF_SET_CLSID(FtpProtocol);
594     INF_SET_CLSID(GopherProtocol);
595     INF_SET_CLSID(HttpProtocol);
596     INF_SET_CLSID(HttpSProtocol);
597     INF_SET_CLSID(MkProtocol);
598
599     for(i = 0; i < sizeof(pse)/sizeof(pse[0]); i++) {
600         pse[i].pszValue = HeapAlloc(GetProcessHeap(), 0, 39);
601         sprintf(pse[i].pszValue, "{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
602                 clsids[i]->Data1, clsids[i]->Data2, clsids[i]->Data3, clsids[i]->Data4[0],
603                 clsids[i]->Data4[1], clsids[i]->Data4[2], clsids[i]->Data4[3], clsids[i]->Data4[4],
604                 clsids[i]->Data4[5], clsids[i]->Data4[6], clsids[i]->Data4[7]);
605     }
606
607     strtable.cEntries = sizeof(pse)/sizeof(pse[0]);
608     strtable.pse = pse;
609
610     hAdvpack = LoadLibraryW(wszAdvpack);
611     pRegInstall = (typeof(RegInstallA)*)GetProcAddress(hAdvpack, "RegInstall");
612
613     hres = pRegInstall(URLMON_hInstance, doregister ? "RegisterDll" : "UnregisterDll", &strtable);
614
615     for(i=0; i < sizeof(pse)/sizeof(pse[0]); i++)
616         HeapFree(GetProcessHeap(), 0, pse[i].pszValue);
617
618     return hres;
619 }
620
621 #undef INF_SET_CLSID
622
623 /***********************************************************************
624  *              DllRegisterServer (URLMON.@)
625  */
626 HRESULT WINAPI DllRegisterServer(void)
627 {
628     HRESULT hr;
629
630     TRACE("\n");
631
632     hr = register_coclasses(coclass_list);
633     if (SUCCEEDED(hr))
634         hr = register_interfaces(interface_list);
635     if(FAILED(hr))
636         return hr;
637     return register_inf(TRUE);
638 }
639
640 /***********************************************************************
641  *              DllUnregisterServer (URLMON.@)
642  */
643 HRESULT WINAPI DllUnregisterServer(void)
644 {
645     HRESULT hr;
646
647     TRACE("\n");
648
649     hr = unregister_coclasses(coclass_list);
650     if (SUCCEEDED(hr))
651         hr = unregister_interfaces(interface_list);
652     if(FAILED(hr))
653         return hr;
654     return register_inf(FALSE);
655 }