usp10: Implement ScriptGetProperties.
[wine] / dlls / msi / regsvr.c
1 /*
2  *      self-registerable dll functions for msi.dll
3  *
4  * Copyright (C) 2004 Raphael Junqueira
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 "config.h"
22
23 #include <stdarg.h>
24 #include <string.h>
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winuser.h"
29 #include "winreg.h"
30 #include "winerror.h"
31
32 #include "ole2.h"
33 #include "olectl.h"
34
35 #include "wine/debug.h"
36
37 #include "msi.h"
38 #include "initguid.h"
39 #include "msipriv.h"
40
41 WINE_DEFAULT_DEBUG_CHANNEL(msi);
42
43 /*
44  * Near the bottom of this file are the exported DllRegisterServer and
45  * DllUnregisterServer, which make all this worthwhile.
46  */
47
48 /***********************************************************************
49  *              interface for self-registering
50  */
51 struct regsvr_interface {
52     IID const *iid;             /* NULL for end of list */
53     LPCSTR name;                /* can be NULL to omit */
54     IID const *base_iid;        /* can be NULL to omit */
55     int num_methods;            /* can be <0 to omit */
56     CLSID const *ps_clsid;      /* can be NULL to omit */
57     CLSID const *ps_clsid32;    /* can be NULL to omit */
58 };
59
60 static HRESULT register_interfaces(struct regsvr_interface const *list);
61 static HRESULT unregister_interfaces(struct regsvr_interface const *list);
62
63 /**
64  * @todo: maybe adding typelibs support here
65  * [Software\\Classes\\CLSID\\{000C1090-0000-0000-C000-000000000046}\\TypeLib] 1080380217
66  * @="{000C1092-0000-0000-C000-000000000046}"
67  */
68 struct regsvr_coclass {
69     CLSID const *clsid;         /* NULL for end of list */
70     LPCSTR name;                /* can be NULL to omit */
71     LPCSTR iph32;               /* can be NULL to omit */
72     LPCSTR ips;                 /* can be NULL to omit */
73     LPCSTR ips32;               /* can be NULL to omit */
74     LPCSTR ips32_tmodel;        /* can be NULL to omit, if apartment, iph32 must be set */
75     LPCSTR progid;              /* can be NULL to omit */
76     LPCSTR viprogid;            /* can be NULL to omit */
77     LPCSTR progid_extra;        /* can be NULL to omit */
78 };
79
80 static HRESULT register_coclasses(struct regsvr_coclass const *list);
81 static HRESULT unregister_coclasses(struct regsvr_coclass const *list);
82
83 /***********************************************************************
84  *              static string constants
85  */
86 static WCHAR const interface_keyname[10] = {
87     'I', 'n', 't', 'e', 'r', 'f', 'a', 'c', 'e', 0 };
88 static WCHAR const base_ifa_keyname[14] = {
89     'B', 'a', 's', 'e', 'I', 'n', 't', 'e', 'r', 'f', 'a', 'c',
90     'e', 0 };
91 static WCHAR const num_methods_keyname[11] = {
92     'N', 'u', 'm', 'M', 'e', 't', 'h', 'o', 'd', 's', 0 };
93 static WCHAR const ps_clsid_keyname[15] = {
94     'P', 'r', 'o', 'x', 'y', 'S', 't', 'u', 'b', 'C', 'l', 's',
95     'i', 'd', 0 };
96 static WCHAR const ps_clsid32_keyname[17] = {
97     'P', 'r', 'o', 'x', 'y', 'S', 't', 'u', 'b', 'C', 'l', 's',
98     'i', 'd', '3', '2', 0 };
99 static WCHAR const clsid_keyname[6] = {
100     'C', 'L', 'S', 'I', 'D', 0 };
101 static WCHAR const curver_keyname[7] = {
102     'C', 'u', 'r', 'V', 'e', 'r', 0 };
103 static WCHAR const iph32_keyname[] = {
104     'I', 'n', 'P', 'r', 'o', 'c', 'H', 'a', 'n', 'd', 'l', 'e', 'r',
105     '3', '2', 0 };
106 static WCHAR const ips_keyname[13] = {
107     'I', 'n', 'P', 'r', 'o', 'c', 'S', 'e', 'r', 'v', 'e', 'r',
108     0 };
109 static WCHAR const ips32_keyname[15] = {
110     'I', 'n', 'P', 'r', 'o', 'c', 'S', 'e', 'r', 'v', 'e', 'r',
111     '3', '2', 0 };
112 static WCHAR const progid_keyname[7] = {
113     'P', 'r', 'o', 'g', 'I', 'D', 0 };
114 static WCHAR const viprogid_keyname[25] = {
115     'V', 'e', 'r', 's', 'i', 'o', 'n', 'I', 'n', 'd', 'e', 'p',
116     'e', 'n', 'd', 'e', 'n', 't', 'P', 'r', 'o', 'g', 'I', 'D',
117     0 };
118 static char const tmodel_valuename[] = "ThreadingModel";
119
120 /***********************************************************************
121  *              static helper functions
122  */
123 static LONG register_key_guid(HKEY base, WCHAR const *name, GUID const *guid);
124 static LONG register_key_defvalueW(HKEY base, WCHAR const *name,
125                                    WCHAR const *value);
126 static LONG register_key_defvalueA(HKEY base, WCHAR const *name,
127                                    char const *value);
128 static LONG register_progid(WCHAR const *clsid,
129                             char const *progid, char const *curver_progid,
130                             char const *name, char const *extra);
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     LONG res = ERROR_SUCCESS;
140     HKEY interface_key;
141
142     res = RegCreateKeyExW(HKEY_CLASSES_ROOT, interface_keyname, 0, NULL, 0,
143                           KEY_READ | KEY_WRITE, NULL, &interface_key, NULL);
144     if (res != ERROR_SUCCESS) goto error_return;
145
146     for (; res == ERROR_SUCCESS && list->iid; ++list) {
147         WCHAR buf[39];
148         HKEY iid_key;
149
150         StringFromGUID2(list->iid, buf, 39);
151         res = RegCreateKeyExW(interface_key, buf, 0, NULL, 0,
152                               KEY_READ | KEY_WRITE, NULL, &iid_key, NULL);
153         if (res != ERROR_SUCCESS) goto error_close_interface_key;
154
155         if (list->name) {
156             res = RegSetValueExA(iid_key, NULL, 0, REG_SZ,
157                                  (CONST BYTE*)(list->name),
158                                  strlen(list->name) + 1);
159             if (res != ERROR_SUCCESS) goto error_close_iid_key;
160         }
161
162         if (list->base_iid) {
163             register_key_guid(iid_key, base_ifa_keyname, list->base_iid);
164             if (res != ERROR_SUCCESS) goto error_close_iid_key;
165         }
166
167         if (0 <= list->num_methods) {
168             static WCHAR const fmt[3] = { '%', 'd', 0 };
169             HKEY key;
170
171             res = RegCreateKeyExW(iid_key, num_methods_keyname, 0, NULL, 0,
172                                   KEY_READ | KEY_WRITE, NULL, &key, NULL);
173             if (res != ERROR_SUCCESS) goto error_close_iid_key;
174
175             wsprintfW(buf, fmt, list->num_methods);
176             res = RegSetValueExW(key, NULL, 0, REG_SZ,
177                                  (CONST BYTE*)buf,
178                                  (lstrlenW(buf) + 1) * sizeof(WCHAR));
179             RegCloseKey(key);
180
181             if (res != ERROR_SUCCESS) goto error_close_iid_key;
182         }
183
184         if (list->ps_clsid) {
185             register_key_guid(iid_key, ps_clsid_keyname, list->ps_clsid);
186             if (res != ERROR_SUCCESS) goto error_close_iid_key;
187         }
188
189         if (list->ps_clsid32) {
190             register_key_guid(iid_key, ps_clsid32_keyname, list->ps_clsid32);
191             if (res != ERROR_SUCCESS) goto error_close_iid_key;
192         }
193
194     error_close_iid_key:
195         RegCloseKey(iid_key);
196     }
197
198 error_close_interface_key:
199     RegCloseKey(interface_key);
200 error_return:
201     return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK;
202 }
203
204 /***********************************************************************
205  *              unregister_interfaces
206  */
207 static HRESULT unregister_interfaces(struct regsvr_interface const *list) {
208     LONG res = ERROR_SUCCESS;
209     HKEY interface_key;
210
211     res = RegOpenKeyExW(HKEY_CLASSES_ROOT, interface_keyname, 0,
212                         KEY_READ | KEY_WRITE, &interface_key);
213     if (res == ERROR_FILE_NOT_FOUND) return S_OK;
214     if (res != ERROR_SUCCESS) goto error_return;
215
216     for (; res == ERROR_SUCCESS && list->iid; ++list) {
217         WCHAR buf[39];
218
219         StringFromGUID2(list->iid, buf, 39);
220         res = recursive_delete_keyW(interface_key, buf);
221     }
222
223     RegCloseKey(interface_key);
224 error_return:
225     return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK;
226 }
227
228 /***********************************************************************
229  *              register_coclasses
230  */
231 static HRESULT register_coclasses(struct regsvr_coclass const *list) {
232     LONG res = ERROR_SUCCESS;
233     HKEY coclass_key;
234
235     res = RegCreateKeyExW(HKEY_CLASSES_ROOT, clsid_keyname, 0, NULL, 0,
236                           KEY_READ | KEY_WRITE, NULL, &coclass_key, NULL);
237     if (res != ERROR_SUCCESS) goto error_return;
238
239     for (; res == ERROR_SUCCESS && list->clsid; ++list) {
240         WCHAR buf[39];
241         HKEY clsid_key;
242
243         StringFromGUID2(list->clsid, buf, 39);
244         res = RegCreateKeyExW(coclass_key, buf, 0, NULL, 0,
245                               KEY_READ | KEY_WRITE, NULL, &clsid_key, NULL);
246         if (res != ERROR_SUCCESS) goto error_close_coclass_key;
247
248         if (list->name) {
249             res = RegSetValueExA(clsid_key, NULL, 0, REG_SZ,
250                                  (CONST BYTE*)(list->name),
251                                  strlen(list->name) + 1);
252             if (res != ERROR_SUCCESS) goto error_close_clsid_key;
253         }
254
255         if (list->iph32) {
256             HKEY iph32_key;
257
258             res = RegCreateKeyExW(clsid_key, iph32_keyname, 0, NULL, 0,
259                                   KEY_READ | KEY_WRITE, NULL,
260                                   &iph32_key, NULL);
261             if (res != ERROR_SUCCESS) goto error_close_clsid_key;
262
263             res = RegSetValueExA(iph32_key, NULL, 0, REG_SZ,
264                                  (CONST BYTE*)list->iph32,
265                                  lstrlenA(list->iph32) + 1);
266             RegCloseKey(iph32_key);
267             if (res != ERROR_SUCCESS) goto error_close_clsid_key;
268         }
269
270         if (list->ips) {
271             res = register_key_defvalueA(clsid_key, ips_keyname, list->ips);
272             if (res != ERROR_SUCCESS) goto error_close_clsid_key;
273         }
274
275         if (list->ips32) {
276             HKEY ips32_key;
277
278             res = RegCreateKeyExW(clsid_key, ips32_keyname, 0, NULL, 0,
279                                   KEY_READ | KEY_WRITE, NULL,
280                                   &ips32_key, NULL);
281             if (res != ERROR_SUCCESS) goto error_close_clsid_key;
282
283             res = RegSetValueExA(ips32_key, NULL, 0, REG_SZ,
284                                  (CONST BYTE*)list->ips32,
285                                  lstrlenA(list->ips32) + 1);
286             if (res == ERROR_SUCCESS && list->ips32_tmodel)
287                 res = RegSetValueExA(ips32_key, tmodel_valuename, 0, REG_SZ,
288                                      (CONST BYTE*)list->ips32_tmodel,
289                                      strlen(list->ips32_tmodel) + 1);
290             RegCloseKey(ips32_key);
291             if (res != ERROR_SUCCESS) goto error_close_clsid_key;
292         }
293
294         if (list->progid) {
295             res = register_key_defvalueA(clsid_key, progid_keyname,
296                                          list->progid);
297             if (res != ERROR_SUCCESS) goto error_close_clsid_key;
298
299             res = register_progid(buf, list->progid, NULL,
300                                   list->name, list->progid_extra);
301             if (res != ERROR_SUCCESS) goto error_close_clsid_key;
302         }
303
304         if (list->viprogid) {
305             res = register_key_defvalueA(clsid_key, viprogid_keyname,
306                                          list->viprogid);
307             if (res != ERROR_SUCCESS) goto error_close_clsid_key;
308
309             res = register_progid(buf, list->viprogid, list->progid,
310                                   list->name, list->progid_extra);
311             if (res != ERROR_SUCCESS) goto error_close_clsid_key;
312         }
313
314     error_close_clsid_key:
315         RegCloseKey(clsid_key);
316     }
317
318 error_close_coclass_key:
319     RegCloseKey(coclass_key);
320 error_return:
321     return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK;
322 }
323
324 /***********************************************************************
325  *              unregister_coclasses
326  */
327 static HRESULT unregister_coclasses(struct regsvr_coclass const *list) {
328     LONG res = ERROR_SUCCESS;
329     HKEY coclass_key;
330
331     res = RegOpenKeyExW(HKEY_CLASSES_ROOT, clsid_keyname, 0,
332                         KEY_READ | KEY_WRITE, &coclass_key);
333     if (res == ERROR_FILE_NOT_FOUND) return S_OK;
334     if (res != ERROR_SUCCESS) goto error_return;
335
336     for (; res == ERROR_SUCCESS && list->clsid; ++list) {
337         WCHAR buf[39];
338
339         StringFromGUID2(list->clsid, buf, 39);
340         res = recursive_delete_keyW(coclass_key, buf);
341         if (res != ERROR_SUCCESS) goto error_close_coclass_key;
342
343         if (list->progid) {
344             res = recursive_delete_keyA(HKEY_CLASSES_ROOT, list->progid);
345             if (res != ERROR_SUCCESS) goto error_close_coclass_key;
346         }
347
348         if (list->viprogid) {
349             res = recursive_delete_keyA(HKEY_CLASSES_ROOT, list->viprogid);
350             if (res != ERROR_SUCCESS) goto error_close_coclass_key;
351         }
352     }
353
354 error_close_coclass_key:
355     RegCloseKey(coclass_key);
356 error_return:
357     return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK;
358 }
359
360 /***********************************************************************
361  *              regsvr_key_guid
362  */
363 static LONG register_key_guid(HKEY base, WCHAR const *name, GUID const *guid) {
364     WCHAR buf[39];
365
366     StringFromGUID2(guid, buf, 39);
367     return register_key_defvalueW(base, name, buf);
368 }
369
370 /***********************************************************************
371  *              regsvr_key_defvalueW
372  */
373 static LONG register_key_defvalueW(
374     HKEY base,
375     WCHAR const *name,
376     WCHAR const *value) {
377     LONG res;
378     HKEY key;
379
380     res = RegCreateKeyExW(base, name, 0, NULL, 0,
381                           KEY_READ | KEY_WRITE, NULL, &key, NULL);
382     if (res != ERROR_SUCCESS) return res;
383     res = RegSetValueExW(key, NULL, 0, REG_SZ, (CONST BYTE*)value,
384                          (lstrlenW(value) + 1) * sizeof(WCHAR));
385     RegCloseKey(key);
386     return res;
387 }
388
389 /***********************************************************************
390  *              regsvr_key_defvalueA
391  */
392 static LONG register_key_defvalueA(
393     HKEY base,
394     WCHAR const *name,
395     char const *value) {
396     LONG res;
397     HKEY key;
398
399     res = RegCreateKeyExW(base, name, 0, NULL, 0,
400                           KEY_READ | KEY_WRITE, NULL, &key, NULL);
401     if (res != ERROR_SUCCESS) return res;
402     res = RegSetValueExA(key, NULL, 0, REG_SZ, (CONST BYTE*)value,
403                          lstrlenA(value) + 1);
404     RegCloseKey(key);
405     return res;
406 }
407
408 /***********************************************************************
409  *              regsvr_progid
410  */
411 static LONG register_progid(
412     WCHAR const *clsid,
413     char const *progid,
414     char const *curver_progid,
415     char const *name,
416     char const *extra) {
417     LONG res;
418     HKEY progid_key;
419
420     res = RegCreateKeyExA(HKEY_CLASSES_ROOT, progid, 0,
421                           NULL, 0, KEY_READ | KEY_WRITE, NULL,
422                           &progid_key, NULL);
423     if (res != ERROR_SUCCESS) return res;
424
425     if (name) {
426         res = RegSetValueExA(progid_key, NULL, 0, REG_SZ,
427                              (CONST BYTE*)name, strlen(name) + 1);
428         if (res != ERROR_SUCCESS) goto error_close_progid_key;
429     }
430
431     if (clsid) {
432         res = register_key_defvalueW(progid_key, clsid_keyname, clsid);
433         if (res != ERROR_SUCCESS) goto error_close_progid_key;
434     }
435
436     if (curver_progid) {
437         res = register_key_defvalueA(progid_key, curver_keyname,
438                                      curver_progid);
439         if (res != ERROR_SUCCESS) goto error_close_progid_key;
440     }
441
442     if (extra) {
443         HKEY extra_key;
444
445         res = RegCreateKeyExA(progid_key, extra, 0,
446                               NULL, 0, KEY_READ | KEY_WRITE, NULL,
447                               &extra_key, NULL);
448         if (res == ERROR_SUCCESS)
449             RegCloseKey(extra_key);
450     }
451
452 error_close_progid_key:
453     RegCloseKey(progid_key);
454     return res;
455 }
456
457 /***********************************************************************
458  *              recursive_delete_key
459  */
460 static LONG recursive_delete_key(HKEY key) {
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     LONG res;
493     HKEY key;
494
495     res = RegOpenKeyExA(base, name, 0, KEY_READ | KEY_WRITE, &key);
496     if (res == ERROR_FILE_NOT_FOUND) return ERROR_SUCCESS;
497     if (res != ERROR_SUCCESS) return res;
498     res = recursive_delete_key(key);
499     RegCloseKey(key);
500     return res;
501 }
502
503 /***********************************************************************
504  *              recursive_delete_keyW
505  */
506 static LONG recursive_delete_keyW(HKEY base, WCHAR const *name) {
507     LONG res;
508     HKEY key;
509
510     res = RegOpenKeyExW(base, name, 0, KEY_READ | KEY_WRITE, &key);
511     if (res == ERROR_FILE_NOT_FOUND) return ERROR_SUCCESS;
512     if (res != ERROR_SUCCESS) return res;
513     res = recursive_delete_key(key);
514     RegCloseKey(key);
515     return res;
516 }
517
518 /***********************************************************************
519  *              coclass list
520  */
521 static struct regsvr_coclass const coclass_list[] = {
522     {     
523         &CLSID_IMsiServer,
524         "Msi install server",
525         "ole32.dll",
526         NULL,
527         "msi.dll",
528         "Apartment",
529         "WindowsInstaller.Installer",
530         NULL
531     },    
532     {     
533         &CLSID_IMsiServerMessage,
534         "Wine Installer Message RPC",
535         NULL,
536         NULL,
537         "msi.dll",
538         NULL,
539         "WindowsInstaller.Message",
540         NULL
541     },
542     {     
543         &CLSID_IMsiServerX1,
544         "Msi install server",
545         "ole32.dll",
546         NULL,
547         "msi.dll",
548         "Apartment",
549         "WindowsInstaller.Installer",
550         NULL
551     },
552     {     
553         &CLSID_IMsiServerX2,
554         "Msi install server",
555         "ole32.dll",
556         NULL,
557         "msi.dll",
558         "Apartment",
559         "WindowsInstaller.Installer",
560         NULL
561     },
562     {     
563         &CLSID_IMsiServerX3,
564         "Msi install server",
565         "ole32.dll",
566         NULL,
567         "msi.dll",
568         "Apartment",
569         "WindowsInstaller.Installer",
570         NULL
571     },
572     { NULL }                    /* list terminator */
573 };
574
575 /***********************************************************************
576  *              interface list
577  */
578 /*
579  * we should declare: (@see ole32/regsvr.c for examples)
580  [-HKEY_CLASSES_ROOT\Interface\{000C101C-0000-0000-C000-000000000046}] 
581  [-HKEY_CLASSES_ROOT\Interface\{000C101D-0000-0000-C000-000000000046}] 
582  [-HKEY_CLASSES_ROOT\Interface\{000C1025-0000-0000-C000-000000000046}] 
583  [-HKEY_CLASSES_ROOT\Interface\{000C1033-0000-0000-C000-000000000046}] 
584  [-HKEY_CLASSES_ROOT\Interface\{000C1090-0000-0000-C000-000000000046}] 
585  [-HKEY_CLASSES_ROOT\Interface\{000C1093-0000-0000-C000-000000000046}] 
586  [-HKEY_CLASSES_ROOT\Interface\{000C1095-0000-0000-C000-000000000046}] 
587  [-HKEY_CLASSES_ROOT\Interface\{000C109A-0000-0000-C000-000000000046}] 
588  [-HKEY_CLASSES_ROOT\Interface\{000C109B-0000-0000-C000-000000000046}] 
589  [-HKEY_CLASSES_ROOT\Interface\{000C109C-0000-0000-C000-000000000046}] 
590  [-HKEY_CLASSES_ROOT\Interface\{000C109D-0000-0000-C000-000000000046}] 
591  [-HKEY_CLASSES_ROOT\Interface\{000C109E-0000-0000-C000-000000000046}] 
592  [-HKEY_CLASSES_ROOT\Interface\{000C109F-0000-0000-C000-000000000046}]
593 */
594 static struct regsvr_interface const interface_list[] = {
595     { NULL }                    /* list terminator */
596 };
597
598 /***********************************************************************
599  *              DllRegisterServer (MSI.@)
600  */
601 HRESULT WINAPI DllRegisterServer(void)
602 {
603     HRESULT hr;
604
605     TRACE("\n");
606
607     hr = register_coclasses(coclass_list);
608     if (SUCCEEDED(hr))
609         hr = register_interfaces(interface_list);
610     return hr;
611 }
612
613 /***********************************************************************
614  *              DllUnregisterServer (MSI.@)
615  */
616 HRESULT WINAPI DllUnregisterServer(void)
617 {
618     HRESULT hr;
619
620     TRACE("\n");
621
622     hr = unregister_coclasses(coclass_list);
623     if (SUCCEEDED(hr))
624         hr = unregister_interfaces(interface_list);
625     return hr;
626 }