msi: Fix a memory leak.
[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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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     DWORD flags;
76     LPCSTR progid;              /* can be NULL to omit */
77     LPCSTR viprogid;            /* can be NULL to omit */
78     LPCSTR progid_extra;        /* can be NULL to omit */
79 };
80
81 /* flags for regsvr_coclass.flags */
82 #define PROGID_CLSID                  0x00000010
83
84 static HRESULT register_coclasses(struct regsvr_coclass const *list);
85 static HRESULT unregister_coclasses(struct regsvr_coclass 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 curver_keyname[7] = {
106     'C', 'u', 'r', 'V', 'e', 'r', 0 };
107 static WCHAR const iph32_keyname[] = {
108     'I', 'n', 'P', 'r', 'o', 'c', 'H', 'a', 'n', 'd', 'l', 'e', 'r',
109     '3', '2', 0 };
110 static WCHAR const ips_keyname[13] = {
111     'I', 'n', 'P', 'r', 'o', 'c', 'S', 'e', 'r', 'v', 'e', 'r',
112     0 };
113 static WCHAR const ips32_keyname[15] = {
114     'I', 'n', 'P', 'r', 'o', 'c', 'S', 'e', 'r', 'v', 'e', 'r',
115     '3', '2', 0 };
116 static WCHAR const progid_keyname[7] = {
117     'P', 'r', 'o', 'g', 'I', 'D', 0 };
118 static WCHAR const viprogid_keyname[25] = {
119     'V', 'e', 'r', 's', 'i', 'o', 'n', 'I', 'n', 'd', 'e', 'p',
120     'e', 'n', 'd', 'e', 'n', 't', 'P', 'r', 'o', 'g', 'I', 'D',
121     0 };
122 static char const tmodel_valuename[] = "ThreadingModel";
123
124 /***********************************************************************
125  *              static helper functions
126  */
127 static LONG register_key_guid(HKEY base, WCHAR const *name, GUID const *guid);
128 static LONG register_key_defvalueW(HKEY base, WCHAR const *name,
129                                    WCHAR const *value);
130 static LONG register_key_defvalueA(HKEY base, WCHAR const *name,
131                                    char const *value);
132 static LONG register_progid(WCHAR const *clsid,
133                             char const *progid, char const *curver_progid,
134                             char const *name, char const *extra);
135 static LONG recursive_delete_key(HKEY key);
136 static LONG recursive_delete_keyA(HKEY base, char const *name);
137 static LONG recursive_delete_keyW(HKEY base, WCHAR const *name);
138
139 /***********************************************************************
140  *              register_interfaces
141  */
142 static HRESULT register_interfaces(struct regsvr_interface const *list) {
143     LONG res = ERROR_SUCCESS;
144     HKEY interface_key;
145
146     res = RegCreateKeyExW(HKEY_CLASSES_ROOT, interface_keyname, 0, NULL, 0,
147                           KEY_READ | KEY_WRITE, NULL, &interface_key, NULL);
148     if (res != ERROR_SUCCESS) goto error_return;
149
150     for (; res == ERROR_SUCCESS && list->iid; ++list) {
151         WCHAR buf[39];
152         HKEY iid_key;
153
154         StringFromGUID2(list->iid, buf, 39);
155         res = RegCreateKeyExW(interface_key, buf, 0, NULL, 0,
156                               KEY_READ | KEY_WRITE, NULL, &iid_key, NULL);
157         if (res != ERROR_SUCCESS) goto error_close_interface_key;
158
159         if (list->name) {
160             res = RegSetValueExA(iid_key, NULL, 0, REG_SZ,
161                                  (CONST BYTE*)(list->name),
162                                  strlen(list->name) + 1);
163             if (res != ERROR_SUCCESS) goto error_close_iid_key;
164         }
165
166         if (list->base_iid) {
167             res = register_key_guid(iid_key, base_ifa_keyname, list->base_iid);
168             if (res != ERROR_SUCCESS) goto error_close_iid_key;
169         }
170
171         if (0 <= list->num_methods) {
172             static WCHAR const fmt[3] = { '%', 'd', 0 };
173             HKEY key;
174
175             res = RegCreateKeyExW(iid_key, num_methods_keyname, 0, NULL, 0,
176                                   KEY_READ | KEY_WRITE, NULL, &key, NULL);
177             if (res != ERROR_SUCCESS) goto error_close_iid_key;
178
179             wsprintfW(buf, fmt, list->num_methods);
180             res = RegSetValueExW(key, NULL, 0, REG_SZ,
181                                  (CONST BYTE*)buf,
182                                  (lstrlenW(buf) + 1) * sizeof(WCHAR));
183             RegCloseKey(key);
184
185             if (res != ERROR_SUCCESS) goto error_close_iid_key;
186         }
187
188         if (list->ps_clsid) {
189             res = register_key_guid(iid_key, ps_clsid_keyname, list->ps_clsid);
190             if (res != ERROR_SUCCESS) goto error_close_iid_key;
191         }
192
193         if (list->ps_clsid32) {
194             res = register_key_guid(iid_key, ps_clsid32_keyname, list->ps_clsid32);
195             if (res != ERROR_SUCCESS) goto error_close_iid_key;
196         }
197
198     error_close_iid_key:
199         RegCloseKey(iid_key);
200     }
201
202 error_close_interface_key:
203     RegCloseKey(interface_key);
204 error_return:
205     return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK;
206 }
207
208 /***********************************************************************
209  *              unregister_interfaces
210  */
211 static HRESULT unregister_interfaces(struct regsvr_interface const *list) {
212     LONG res = ERROR_SUCCESS;
213     HKEY interface_key;
214
215     res = RegOpenKeyExW(HKEY_CLASSES_ROOT, interface_keyname, 0,
216                         KEY_READ | KEY_WRITE, &interface_key);
217     if (res == ERROR_FILE_NOT_FOUND) return S_OK;
218     if (res != ERROR_SUCCESS) goto error_return;
219
220     for (; res == ERROR_SUCCESS && list->iid; ++list) {
221         WCHAR buf[39];
222
223         StringFromGUID2(list->iid, buf, 39);
224         res = recursive_delete_keyW(interface_key, buf);
225     }
226
227     RegCloseKey(interface_key);
228 error_return:
229     return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK;
230 }
231
232 /***********************************************************************
233  *              register_coclasses
234  */
235 static HRESULT register_coclasses(struct regsvr_coclass const *list) {
236     LONG res = ERROR_SUCCESS;
237     HKEY coclass_key;
238
239     res = RegCreateKeyExW(HKEY_CLASSES_ROOT, clsid_keyname, 0, NULL, 0,
240                           KEY_READ | KEY_WRITE, NULL, &coclass_key, NULL);
241     if (res != ERROR_SUCCESS) goto error_return;
242
243     for (; res == ERROR_SUCCESS && list->clsid; ++list) {
244         WCHAR buf[39];
245         HKEY clsid_key;
246
247         StringFromGUID2(list->clsid, buf, 39);
248         res = RegCreateKeyExW(coclass_key, buf, 0, NULL, 0,
249                               KEY_READ | KEY_WRITE, NULL, &clsid_key, NULL);
250         if (res != ERROR_SUCCESS) goto error_close_coclass_key;
251
252         if (list->name) {
253             res = RegSetValueExA(clsid_key, NULL, 0, REG_SZ,
254                                  (CONST BYTE*)(list->name),
255                                  strlen(list->name) + 1);
256             if (res != ERROR_SUCCESS) goto error_close_clsid_key;
257         }
258
259         if (list->iph32) {
260             HKEY iph32_key;
261
262             res = RegCreateKeyExW(clsid_key, iph32_keyname, 0, NULL, 0,
263                                   KEY_READ | KEY_WRITE, NULL,
264                                   &iph32_key, NULL);
265             if (res != ERROR_SUCCESS) goto error_close_clsid_key;
266
267             res = RegSetValueExA(iph32_key, NULL, 0, REG_SZ,
268                                  (CONST BYTE*)list->iph32,
269                                  lstrlenA(list->iph32) + 1);
270             RegCloseKey(iph32_key);
271             if (res != ERROR_SUCCESS) goto error_close_clsid_key;
272         }
273
274         if (list->ips) {
275             res = register_key_defvalueA(clsid_key, ips_keyname, list->ips);
276             if (res != ERROR_SUCCESS) goto error_close_clsid_key;
277         }
278
279         if (list->ips32) {
280             HKEY ips32_key;
281
282             res = RegCreateKeyExW(clsid_key, ips32_keyname, 0, NULL, 0,
283                                   KEY_READ | KEY_WRITE, NULL,
284                                   &ips32_key, NULL);
285             if (res != ERROR_SUCCESS) goto error_close_clsid_key;
286
287             res = RegSetValueExA(ips32_key, NULL, 0, REG_SZ,
288                                  (CONST BYTE*)list->ips32,
289                                  lstrlenA(list->ips32) + 1);
290             if (res == ERROR_SUCCESS && list->ips32_tmodel)
291                 res = RegSetValueExA(ips32_key, tmodel_valuename, 0, REG_SZ,
292                                      (CONST BYTE*)list->ips32_tmodel,
293                                      strlen(list->ips32_tmodel) + 1);
294             RegCloseKey(ips32_key);
295             if (res != ERROR_SUCCESS) goto error_close_clsid_key;
296         }
297
298         if (list->progid) {
299             res = register_key_defvalueA(clsid_key, progid_keyname,
300                                          list->progid);
301             if (res != ERROR_SUCCESS) goto error_close_clsid_key;
302
303             res = register_progid(list->flags & PROGID_CLSID ? buf : NULL,
304                                   list->progid, NULL,
305                                   list->name, list->progid_extra);
306             if (res != ERROR_SUCCESS) goto error_close_clsid_key;
307         }
308
309         if (list->viprogid) {
310             res = register_key_defvalueA(clsid_key, viprogid_keyname,
311                                          list->viprogid);
312             if (res != ERROR_SUCCESS) goto error_close_clsid_key;
313
314             res = register_progid(list->flags & PROGID_CLSID ? buf : NULL,
315                                   list->viprogid, list->progid,
316                                   list->name, list->progid_extra);
317             if (res != ERROR_SUCCESS) goto error_close_clsid_key;
318         }
319
320     error_close_clsid_key:
321         RegCloseKey(clsid_key);
322     }
323
324 error_close_coclass_key:
325     RegCloseKey(coclass_key);
326 error_return:
327     return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK;
328 }
329
330 /***********************************************************************
331  *              unregister_coclasses
332  */
333 static HRESULT unregister_coclasses(struct regsvr_coclass const *list) {
334     LONG res = ERROR_SUCCESS;
335     HKEY coclass_key;
336
337     res = RegOpenKeyExW(HKEY_CLASSES_ROOT, clsid_keyname, 0,
338                         KEY_READ | KEY_WRITE, &coclass_key);
339     if (res == ERROR_FILE_NOT_FOUND) return S_OK;
340     if (res != ERROR_SUCCESS) goto error_return;
341
342     for (; res == ERROR_SUCCESS && list->clsid; ++list) {
343         WCHAR buf[39];
344
345         StringFromGUID2(list->clsid, buf, 39);
346         res = recursive_delete_keyW(coclass_key, buf);
347         if (res != ERROR_SUCCESS) goto error_close_coclass_key;
348
349         if (list->progid) {
350             res = recursive_delete_keyA(HKEY_CLASSES_ROOT, list->progid);
351             if (res != ERROR_SUCCESS) goto error_close_coclass_key;
352         }
353
354         if (list->viprogid) {
355             res = recursive_delete_keyA(HKEY_CLASSES_ROOT, list->viprogid);
356             if (res != ERROR_SUCCESS) goto error_close_coclass_key;
357         }
358     }
359
360 error_close_coclass_key:
361     RegCloseKey(coclass_key);
362 error_return:
363     return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK;
364 }
365
366 /***********************************************************************
367  *              regsvr_key_guid
368  */
369 static LONG register_key_guid(HKEY base, WCHAR const *name, GUID const *guid) {
370     WCHAR buf[39];
371
372     StringFromGUID2(guid, buf, 39);
373     return register_key_defvalueW(base, name, buf);
374 }
375
376 /***********************************************************************
377  *              regsvr_key_defvalueW
378  */
379 static LONG register_key_defvalueW(
380     HKEY base,
381     WCHAR const *name,
382     WCHAR const *value) {
383     LONG res;
384     HKEY key;
385
386     res = RegCreateKeyExW(base, name, 0, NULL, 0,
387                           KEY_READ | KEY_WRITE, NULL, &key, NULL);
388     if (res != ERROR_SUCCESS) return res;
389     res = RegSetValueExW(key, NULL, 0, REG_SZ, (CONST BYTE*)value,
390                          (lstrlenW(value) + 1) * sizeof(WCHAR));
391     RegCloseKey(key);
392     return res;
393 }
394
395 /***********************************************************************
396  *              regsvr_key_defvalueA
397  */
398 static LONG register_key_defvalueA(
399     HKEY base,
400     WCHAR const *name,
401     char const *value) {
402     LONG res;
403     HKEY key;
404
405     res = RegCreateKeyExW(base, name, 0, NULL, 0,
406                           KEY_READ | KEY_WRITE, NULL, &key, NULL);
407     if (res != ERROR_SUCCESS) return res;
408     res = RegSetValueExA(key, NULL, 0, REG_SZ, (CONST BYTE*)value,
409                          lstrlenA(value) + 1);
410     RegCloseKey(key);
411     return res;
412 }
413
414 /***********************************************************************
415  *              regsvr_progid
416  */
417 static LONG register_progid(
418     WCHAR const *clsid,
419     char const *progid,
420     char const *curver_progid,
421     char const *name,
422     char const *extra) {
423     LONG res;
424     HKEY progid_key;
425
426     res = RegCreateKeyExA(HKEY_CLASSES_ROOT, progid, 0,
427                           NULL, 0, KEY_READ | KEY_WRITE, NULL,
428                           &progid_key, NULL);
429     if (res != ERROR_SUCCESS) return res;
430
431     if (name) {
432         res = RegSetValueExA(progid_key, NULL, 0, REG_SZ,
433                              (CONST BYTE*)name, strlen(name) + 1);
434         if (res != ERROR_SUCCESS) goto error_close_progid_key;
435     }
436
437     if (clsid) {
438         res = register_key_defvalueW(progid_key, clsid_keyname, clsid);
439         if (res != ERROR_SUCCESS) goto error_close_progid_key;
440     }
441
442     if (curver_progid) {
443         res = register_key_defvalueA(progid_key, curver_keyname,
444                                      curver_progid);
445         if (res != ERROR_SUCCESS) goto error_close_progid_key;
446     }
447
448     if (extra) {
449         HKEY extra_key;
450
451         res = RegCreateKeyExA(progid_key, extra, 0,
452                               NULL, 0, KEY_READ | KEY_WRITE, NULL,
453                               &extra_key, NULL);
454         if (res == ERROR_SUCCESS)
455             RegCloseKey(extra_key);
456     }
457
458 error_close_progid_key:
459     RegCloseKey(progid_key);
460     return res;
461 }
462
463 /***********************************************************************
464  *              recursive_delete_key
465  */
466 static LONG recursive_delete_key(HKEY key) {
467     LONG res;
468     WCHAR subkey_name[MAX_PATH];
469     DWORD cName;
470     HKEY subkey;
471
472     for (;;) {
473         cName = sizeof(subkey_name) / sizeof(WCHAR);
474         res = RegEnumKeyExW(key, 0, subkey_name, &cName,
475                             NULL, NULL, NULL, NULL);
476         if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA) {
477             res = ERROR_SUCCESS; /* presumably we're done enumerating */
478             break;
479         }
480         res = RegOpenKeyExW(key, subkey_name, 0,
481                             KEY_READ | KEY_WRITE, &subkey);
482         if (res == ERROR_FILE_NOT_FOUND) continue;
483         if (res != ERROR_SUCCESS) break;
484
485         res = recursive_delete_key(subkey);
486         RegCloseKey(subkey);
487         if (res != ERROR_SUCCESS) break;
488     }
489
490     if (res == ERROR_SUCCESS) res = RegDeleteKeyW(key, 0);
491     return res;
492 }
493
494 /***********************************************************************
495  *              recursive_delete_keyA
496  */
497 static LONG recursive_delete_keyA(HKEY base, char const *name) {
498     LONG res;
499     HKEY key;
500
501     res = RegOpenKeyExA(base, name, 0, KEY_READ | KEY_WRITE, &key);
502     if (res == ERROR_FILE_NOT_FOUND) return ERROR_SUCCESS;
503     if (res != ERROR_SUCCESS) return res;
504     res = recursive_delete_key(key);
505     RegCloseKey(key);
506     return res;
507 }
508
509 /***********************************************************************
510  *              recursive_delete_keyW
511  */
512 static LONG recursive_delete_keyW(HKEY base, WCHAR const *name) {
513     LONG res;
514     HKEY key;
515
516     res = RegOpenKeyExW(base, name, 0, KEY_READ | KEY_WRITE, &key);
517     if (res == ERROR_FILE_NOT_FOUND) return ERROR_SUCCESS;
518     if (res != ERROR_SUCCESS) return res;
519     res = recursive_delete_key(key);
520     RegCloseKey(key);
521     return res;
522 }
523
524 /***********************************************************************
525  *              coclass list
526  */
527 static struct regsvr_coclass const coclass_list[] = {
528     {     
529         &CLSID_IMsiServer,
530         "Msi install server",
531         "ole32.dll",
532         NULL,
533         "msi.dll",
534         "Apartment",
535         PROGID_CLSID,
536         "IMsiServer",
537         NULL
538     },    
539     {     
540         &CLSID_IMsiServerMessage,
541         "Wine Installer Message RPC",
542         NULL,
543         NULL,
544         "msi.dll",
545         NULL,
546         PROGID_CLSID,
547         "WindowsInstaller.Message",
548         NULL
549     },
550     {     
551         &CLSID_IMsiServerX1,
552         "Msi install server",
553         "ole32.dll",
554         NULL,
555         "msi.dll",
556         "Apartment",
557         0,
558         "WindowsInstaller.Installer",
559         NULL
560     },
561     {     
562         &CLSID_IMsiServerX2,
563         "Msi install server",
564         "ole32.dll",
565         NULL,
566         "msi.dll",
567         "Apartment",
568         PROGID_CLSID,
569         "WindowsInstaller.Installer",
570         NULL
571     },
572     {     
573         &CLSID_IMsiServerX3,
574         "Msi install server",
575         "ole32.dll",
576         NULL,
577         "msi.dll",
578         "Apartment",
579         0,
580         "WindowsInstaller.Installer",
581         NULL
582     },
583     { NULL }                    /* list terminator */
584 };
585
586 /***********************************************************************
587  *              interface list
588  */
589 /*
590  * we should declare: (@see ole32/regsvr.c for examples)
591  [-HKEY_CLASSES_ROOT\Interface\{000C101C-0000-0000-C000-000000000046}] 
592  [-HKEY_CLASSES_ROOT\Interface\{000C101D-0000-0000-C000-000000000046}] 
593  [-HKEY_CLASSES_ROOT\Interface\{000C1025-0000-0000-C000-000000000046}] 
594  [-HKEY_CLASSES_ROOT\Interface\{000C1033-0000-0000-C000-000000000046}] 
595  [-HKEY_CLASSES_ROOT\Interface\{000C1090-0000-0000-C000-000000000046}] 
596  [-HKEY_CLASSES_ROOT\Interface\{000C1093-0000-0000-C000-000000000046}] 
597  [-HKEY_CLASSES_ROOT\Interface\{000C1095-0000-0000-C000-000000000046}] 
598  [-HKEY_CLASSES_ROOT\Interface\{000C109A-0000-0000-C000-000000000046}] 
599  [-HKEY_CLASSES_ROOT\Interface\{000C109B-0000-0000-C000-000000000046}] 
600  [-HKEY_CLASSES_ROOT\Interface\{000C109C-0000-0000-C000-000000000046}] 
601  [-HKEY_CLASSES_ROOT\Interface\{000C109D-0000-0000-C000-000000000046}] 
602  [-HKEY_CLASSES_ROOT\Interface\{000C109E-0000-0000-C000-000000000046}] 
603  [-HKEY_CLASSES_ROOT\Interface\{000C109F-0000-0000-C000-000000000046}]
604 */
605 static struct regsvr_interface const interface_list[] = {
606     { NULL }                    /* list terminator */
607 };
608
609 static HRESULT register_msiexec(void)
610 {
611     static const WCHAR key[] = {
612         'S','o','f','t','w','a','r','e',
613         '\\','M','i','c','r','o','s','o','f','t',
614         '\\','W','i','n','d','o','w','s',
615         '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
616         '\\','I','n','s','t','a','l','l','e','r',0 };
617     static const WCHAR val[] = {
618         'I','n','s','t','a','l','l','e','r','L','o','c','a','t','i','o','n',0 };
619     WCHAR path[MAX_PATH];
620     HKEY hkey;
621     LONG res;
622     INT len;
623
624     len = GetSystemDirectoryW(path, MAX_PATH);
625     if (!len || len > MAX_PATH)
626         return E_FAIL;
627
628     res = RegCreateKeyExW(HKEY_LOCAL_MACHINE, key, 0,
629                           NULL, 0, KEY_READ | KEY_WRITE, NULL,
630                           &hkey, NULL);
631     if (res != ERROR_SUCCESS)
632         return E_FAIL;
633
634     res = RegSetValueExW(hkey, val, 0, REG_SZ,
635                          (BYTE*)path, (len + 1)*sizeof(WCHAR));
636
637     RegCloseKey(hkey);
638
639     return (res == ERROR_SUCCESS) ? S_OK : E_FAIL;
640 }
641
642 /***********************************************************************
643  *              DllRegisterServer (MSI.@)
644  */
645 HRESULT WINAPI DllRegisterServer(void)
646 {
647     HRESULT hr;
648
649     TRACE("\n");
650
651     hr = register_coclasses(coclass_list);
652     if (SUCCEEDED(hr))
653         hr = register_interfaces(interface_list);
654     if (SUCCEEDED(hr))
655         hr = register_msiexec();
656     return hr;
657 }
658
659 /***********************************************************************
660  *              DllUnregisterServer (MSI.@)
661  */
662 HRESULT WINAPI DllUnregisterServer(void)
663 {
664     HRESULT hr;
665
666     TRACE("\n");
667
668     hr = unregister_coclasses(coclass_list);
669     if (SUCCEEDED(hr))
670         hr = unregister_interfaces(interface_list);
671     return hr;
672 }