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