xref: /reactos/dll/win32/msi/registry.c (revision b36d9bd9)
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2005 Mike McCormack for CodeWeavers
5  * Copyright 2005 Aric Stewart for CodeWeavers
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 
24 #define COBJMACROS
25 
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winreg.h"
29 #include "winnls.h"
30 #include "shlwapi.h"
31 #include "wine/debug.h"
32 #include "msi.h"
33 #include "msipriv.h"
34 #include "wincrypt.h"
35 #include "winver.h"
36 #include "winuser.h"
37 #include "sddl.h"
38 
39 WINE_DEFAULT_DEBUG_CHANNEL(msi);
40 
41 BOOL unsquash_guid(LPCWSTR in, LPWSTR out)
42 {
43     DWORD i,n=0;
44 
45     if (lstrlenW(in) != 32)
46         return FALSE;
47 
48     out[n++]='{';
49     for(i=0; i<8; i++)
50         out[n++] = in[7-i];
51     out[n++]='-';
52     for(i=0; i<4; i++)
53         out[n++] = in[11-i];
54     out[n++]='-';
55     for(i=0; i<4; i++)
56         out[n++] = in[15-i];
57     out[n++]='-';
58     for(i=0; i<2; i++)
59     {
60         out[n++] = in[17+i*2];
61         out[n++] = in[16+i*2];
62     }
63     out[n++]='-';
64     for( ; i<8; i++)
65     {
66         out[n++] = in[17+i*2];
67         out[n++] = in[16+i*2];
68     }
69     out[n++]='}';
70     out[n]=0;
71     return TRUE;
72 }
73 
74 BOOL squash_guid(LPCWSTR in, LPWSTR out)
75 {
76     DWORD i,n=1;
77     GUID guid;
78 
79     out[0] = 0;
80 
81     if (FAILED(CLSIDFromString((LPCOLESTR)in, &guid)))
82         return FALSE;
83 
84     for(i=0; i<8; i++)
85         out[7-i] = in[n++];
86     n++;
87     for(i=0; i<4; i++)
88         out[11-i] = in[n++];
89     n++;
90     for(i=0; i<4; i++)
91         out[15-i] = in[n++];
92     n++;
93     for(i=0; i<2; i++)
94     {
95         out[17+i*2] = in[n++];
96         out[16+i*2] = in[n++];
97     }
98     n++;
99     for( ; i<8; i++)
100     {
101         out[17+i*2] = in[n++];
102         out[16+i*2] = in[n++];
103     }
104     out[32]=0;
105     return TRUE;
106 }
107 
108 
109 /* tables for encoding and decoding base85 */
110 static const unsigned char table_dec85[0x80] = {
111 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
112 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
113 0xff,0x00,0xff,0xff,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0xff,
114 0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0xff,0xff,0xff,0x16,0xff,0x17,
115 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,
116 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x32,0x33,0xff,0x34,0x35,0x36,
117 0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x40,0x41,0x42,0x43,0x44,0x45,0x46,
118 0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x50,0x51,0x52,0xff,0x53,0x54,0xff,
119 };
120 
121 static const char table_enc85[] =
122 "!$%&'()*+,-.0123456789=?@ABCDEFGHIJKLMNO"
123 "PQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwx"
124 "yz{}~";
125 
126 /*
127  *  Converts a base85 encoded guid into a GUID pointer
128  *  Base85 encoded GUIDs should be 20 characters long.
129  *
130  *  returns TRUE if successful, FALSE if not
131  */
132 BOOL decode_base85_guid( LPCWSTR str, GUID *guid )
133 {
134     DWORD i, val = 0, base = 1, *p;
135 
136     if (!str)
137         return FALSE;
138 
139     p = (DWORD*) guid;
140     for( i=0; i<20; i++ )
141     {
142         if( (i%5) == 0 )
143         {
144             val = 0;
145             base = 1;
146         }
147         val += table_dec85[str[i]] * base;
148         if( str[i] >= 0x80 )
149             return FALSE;
150         if( table_dec85[str[i]] == 0xff )
151             return FALSE;
152         if( (i%5) == 4 )
153             p[i/5] = val;
154         base *= 85;
155     }
156     return TRUE;
157 }
158 
159 /*
160  *  Encodes a base85 guid given a GUID pointer
161  *  Caller should provide a 21 character buffer for the encoded string.
162  *
163  *  returns TRUE if successful, FALSE if not
164  */
165 BOOL encode_base85_guid( GUID *guid, LPWSTR str )
166 {
167     unsigned int x, *p, i;
168 
169     p = (unsigned int*) guid;
170     for( i=0; i<4; i++ )
171     {
172         x = p[i];
173         *str++ = table_enc85[x%85];
174         x = x/85;
175         *str++ = table_enc85[x%85];
176         x = x/85;
177         *str++ = table_enc85[x%85];
178         x = x/85;
179         *str++ = table_enc85[x%85];
180         x = x/85;
181         *str++ = table_enc85[x%85];
182     }
183     *str = 0;
184 
185     return TRUE;
186 }
187 
188 DWORD msi_version_str_to_dword(LPCWSTR p)
189 {
190     DWORD major, minor = 0, build = 0, version = 0;
191 
192     if (!p)
193         return version;
194 
195     major = wcstol(p, NULL, 10);
196 
197     p = wcschr(p, '.');
198     if (p)
199     {
200         minor = wcstol(p+1, NULL, 10);
201         p = wcschr(p+1, '.');
202         if (p)
203             build = wcstol(p+1, NULL, 10);
204     }
205 
206     return MAKELONG(build, MAKEWORD(minor, major));
207 }
208 
209 LONG msi_reg_set_val_str( HKEY hkey, LPCWSTR name, LPCWSTR value )
210 {
211     DWORD len;
212     if (!value) value = L"";
213     len = (lstrlenW(value) + 1) * sizeof (WCHAR);
214     return RegSetValueExW( hkey, name, 0, REG_SZ, (const BYTE *)value, len );
215 }
216 
217 LONG msi_reg_set_val_multi_str( HKEY hkey, LPCWSTR name, LPCWSTR value )
218 {
219     LPCWSTR p = value;
220     while (*p) p += lstrlenW(p) + 1;
221     return RegSetValueExW( hkey, name, 0, REG_MULTI_SZ,
222                            (const BYTE *)value, (p + 1 - value) * sizeof(WCHAR) );
223 }
224 
225 LONG msi_reg_set_val_dword( HKEY hkey, LPCWSTR name, DWORD val )
226 {
227     return RegSetValueExW( hkey, name, 0, REG_DWORD, (LPBYTE)&val, sizeof (DWORD) );
228 }
229 
230 LONG msi_reg_set_subkey_val( HKEY hkey, LPCWSTR path, LPCWSTR name, LPCWSTR val )
231 {
232     HKEY hsubkey = 0;
233     LONG r;
234 
235     r = RegCreateKeyW( hkey, path, &hsubkey );
236     if (r != ERROR_SUCCESS)
237         return r;
238     r = msi_reg_set_val_str( hsubkey, name, val );
239     RegCloseKey( hsubkey );
240     return r;
241 }
242 
243 LPWSTR msi_reg_get_val_str( HKEY hkey, LPCWSTR name )
244 {
245     DWORD len = 0;
246     LPWSTR val;
247     LONG r;
248 
249     r = RegQueryValueExW(hkey, name, NULL, NULL, NULL, &len);
250     if (r != ERROR_SUCCESS)
251         return NULL;
252 
253     len += sizeof (WCHAR);
254     val = msi_alloc( len );
255     if (!val)
256         return NULL;
257     val[0] = 0;
258     RegQueryValueExW(hkey, name, NULL, NULL, (LPBYTE) val, &len);
259     return val;
260 }
261 
262 BOOL msi_reg_get_val_dword( HKEY hkey, LPCWSTR name, DWORD *val)
263 {
264     DWORD type, len = sizeof (DWORD);
265     LONG r = RegQueryValueExW(hkey, name, NULL, &type, (LPBYTE) val, &len);
266     return r == ERROR_SUCCESS && type == REG_DWORD;
267 }
268 
269 static WCHAR *get_user_sid(void)
270 {
271     HANDLE token;
272     DWORD size = 256;
273     TOKEN_USER *user;
274     WCHAR *ret;
275 
276     if (!OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, &token )) return NULL;
277     if (!(user = msi_alloc( size )))
278     {
279         CloseHandle( token );
280         return NULL;
281     }
282     if (!GetTokenInformation( token, TokenUser, user, size, &size ))
283     {
284         msi_free( user );
285         if (GetLastError() != ERROR_INSUFFICIENT_BUFFER || !(user = msi_alloc( size )))
286         {
287             CloseHandle( token );
288             return NULL;
289         }
290         GetTokenInformation( token, TokenUser, user, size, &size );
291     }
292     CloseHandle( token );
293     if (!ConvertSidToStringSidW( user->User.Sid, &ret ))
294     {
295         msi_free( user );
296         return NULL;
297     }
298     msi_free( user );
299     return ret;
300 }
301 
302 UINT MSIREG_OpenUninstallKey(const WCHAR *product, enum platform platform, HKEY *key, BOOL create)
303 {
304     REGSAM access = KEY_ALL_ACCESS;
305     WCHAR keypath[0x200];
306 
307     TRACE("%s\n", debugstr_w(product));
308 
309     if (platform == PLATFORM_INTEL)
310         access |= KEY_WOW64_32KEY;
311     else
312         access |= KEY_WOW64_64KEY;
313     lstrcpyW(keypath, L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\");
314     lstrcatW(keypath, product);
315     if (create) return RegCreateKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, NULL, 0, access, NULL, key, NULL);
316     return RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, access, key);
317 }
318 
319 UINT MSIREG_DeleteUninstallKey(const WCHAR *product, enum platform platform)
320 {
321     REGSAM access = KEY_ALL_ACCESS;
322     HKEY parent;
323     LONG r;
324 
325     TRACE("%s\n", debugstr_w(product));
326 
327     if (platform == PLATFORM_INTEL)
328         access |= KEY_WOW64_32KEY;
329     else
330         access |= KEY_WOW64_64KEY;
331     if ((r = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\",
332                            0, access, &parent))) return r;
333     r = RegDeleteTreeW(parent, product);
334     RegCloseKey(parent);
335     return r;
336 }
337 
338 UINT MSIREG_OpenProductKey(LPCWSTR szProduct, LPCWSTR szUserSid, MSIINSTALLCONTEXT context, HKEY *key, BOOL create)
339 {
340     HKEY root = HKEY_LOCAL_MACHINE;
341     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
342     WCHAR *usersid = NULL, squashed_pc[SQUASHED_GUID_SIZE], keypath[MAX_PATH];
343 
344     if (!squash_guid( szProduct, squashed_pc )) return ERROR_FUNCTION_FAILED;
345     TRACE("%s squashed %s\n", debugstr_w(szProduct), debugstr_w(squashed_pc));
346 
347     if (context == MSIINSTALLCONTEXT_MACHINE)
348     {
349         lstrcpyW(keypath, L"Software\\Classes\\Installer\\Products\\");
350         lstrcatW( keypath, squashed_pc );
351     }
352     else if (context == MSIINSTALLCONTEXT_USERUNMANAGED)
353     {
354         root = HKEY_CURRENT_USER;
355         lstrcpyW( keypath, L"Software\\Microsoft\\Installer\\Products\\" );
356         lstrcatW( keypath, squashed_pc );
357     }
358     else
359     {
360         if (!szUserSid)
361         {
362             if (!(usersid = get_user_sid()))
363             {
364                 ERR("Failed to retrieve user SID\n");
365                 return ERROR_FUNCTION_FAILED;
366             }
367             szUserSid = usersid;
368         }
369         swprintf( keypath, ARRAY_SIZE(keypath),
370                   L"Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\Managed\\%s\\Installer\\Products\\%s",
371                   szUserSid, squashed_pc );
372         LocalFree(usersid);
373     }
374     if (create) return RegCreateKeyExW(root, keypath, 0, NULL, 0, access, NULL, key, NULL);
375     return RegOpenKeyExW(root, keypath, 0, access, key);
376 }
377 
378 UINT MSIREG_DeleteUserProductKey(LPCWSTR szProduct)
379 {
380     WCHAR squashed_pc[SQUASHED_GUID_SIZE], keypath[0x200];
381 
382     if (!squash_guid( szProduct, squashed_pc )) return ERROR_FUNCTION_FAILED;
383     TRACE("%s squashed %s\n", debugstr_w(szProduct), debugstr_w(squashed_pc));
384 
385     lstrcpyW( keypath, L"Software\\Microsoft\\Installer\\Products\\" );
386     lstrcatW( keypath, squashed_pc );
387     return RegDeleteTreeW(HKEY_CURRENT_USER, keypath);
388 }
389 
390 UINT MSIREG_OpenUserPatchesKey(LPCWSTR szPatch, HKEY *key, BOOL create)
391 {
392     WCHAR squashed_pc[SQUASHED_GUID_SIZE], keypath[0x200];
393 
394     if (!squash_guid( szPatch, squashed_pc )) return ERROR_FUNCTION_FAILED;
395     TRACE("%s squashed %s\n", debugstr_w(szPatch), debugstr_w(squashed_pc));
396 
397     lstrcpyW( keypath, L"Software\\Microsoft\\Installer\\Patches\\" );
398     lstrcatW( keypath, squashed_pc );
399 
400     if (create) return RegCreateKeyW(HKEY_CURRENT_USER, keypath, key);
401     return RegOpenKeyW(HKEY_CURRENT_USER, keypath, key);
402 }
403 
404 UINT MSIREG_OpenFeaturesKey(LPCWSTR szProduct, LPCWSTR szUserSid, MSIINSTALLCONTEXT context,
405                             HKEY *key, BOOL create)
406 {
407     HKEY root = HKEY_LOCAL_MACHINE;
408     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
409     WCHAR squashed_pc[SQUASHED_GUID_SIZE], keypath[MAX_PATH], *usersid = NULL;
410 
411     if (!squash_guid( szProduct, squashed_pc )) return ERROR_FUNCTION_FAILED;
412     TRACE("%s squashed %s\n", debugstr_w(szProduct), debugstr_w(squashed_pc));
413 
414     if (context == MSIINSTALLCONTEXT_MACHINE)
415     {
416         lstrcpyW(keypath, L"Software\\Classes\\Installer\\Features\\");
417         lstrcatW( keypath, squashed_pc );
418     }
419     else if (context == MSIINSTALLCONTEXT_USERUNMANAGED)
420     {
421         root = HKEY_CURRENT_USER;
422         lstrcpyW(keypath, L"Software\\Microsoft\\Installer\\Features\\");
423         lstrcatW( keypath, squashed_pc );
424     }
425     else
426     {
427         if (!szUserSid)
428         {
429             if (!(usersid = get_user_sid()))
430             {
431                 ERR("Failed to retrieve user SID\n");
432                 return ERROR_FUNCTION_FAILED;
433             }
434             szUserSid = usersid;
435         }
436         swprintf( keypath, ARRAY_SIZE(keypath),
437                   L"Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\Managed\\%s\\Installer\\Features\\%s",
438                   szUserSid, squashed_pc );
439         LocalFree(usersid);
440     }
441     if (create) return RegCreateKeyExW(root, keypath, 0, NULL, 0, access, NULL, key, NULL);
442     return RegOpenKeyExW(root, keypath, 0, access, key);
443 }
444 
445 UINT MSIREG_DeleteUserFeaturesKey(LPCWSTR szProduct)
446 {
447     WCHAR squashed_pc[SQUASHED_GUID_SIZE], keypath[0x200];
448 
449     if (!squash_guid( szProduct, squashed_pc )) return ERROR_FUNCTION_FAILED;
450     TRACE("%s squashed %s\n", debugstr_w(szProduct), debugstr_w(squashed_pc));
451 
452     lstrcpyW( keypath, L"Software\\Microsoft\\Installer\\Features\\" );
453     lstrcatW( keypath, squashed_pc );
454     return RegDeleteTreeW(HKEY_CURRENT_USER, keypath);
455 }
456 
457 static UINT MSIREG_OpenInstallerFeaturesKey(LPCWSTR szProduct, HKEY *key, BOOL create)
458 {
459     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
460     WCHAR squashed_pc[SQUASHED_GUID_SIZE], keypath[0x200];
461 
462     if (!squash_guid( szProduct, squashed_pc )) return ERROR_FUNCTION_FAILED;
463     TRACE("%s squashed %s\n", debugstr_w(szProduct), debugstr_w(squashed_pc));
464 
465     lstrcpyW(keypath, L"Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\Features\\");
466     lstrcatW( keypath, squashed_pc );
467 
468     if (create) return RegCreateKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, NULL, 0, access, NULL, key, NULL);
469     return RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, access, key);
470 }
471 
472 UINT MSIREG_OpenUserDataFeaturesKey(LPCWSTR szProduct, LPCWSTR szUserSid, MSIINSTALLCONTEXT context,
473                                     HKEY *key, BOOL create)
474 {
475     static const WCHAR fmtW[] =
476         L"Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\%s\\Products\\%s\\Features";
477     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
478     WCHAR squashed_pc[SQUASHED_GUID_SIZE], keypath[0x200], *usersid = NULL;
479 
480     if (!squash_guid( szProduct, squashed_pc )) return ERROR_FUNCTION_FAILED;
481     TRACE("%s squashed %s\n", debugstr_w(szProduct), debugstr_w(squashed_pc));
482 
483     if (context == MSIINSTALLCONTEXT_MACHINE)
484     {
485         swprintf( keypath, ARRAY_SIZE(keypath), fmtW, L"S-1-5-18", squashed_pc );
486     }
487     else
488     {
489         if (!szUserSid)
490         {
491             if (!(usersid = get_user_sid()))
492             {
493                 ERR("Failed to retrieve user SID\n");
494                 return ERROR_FUNCTION_FAILED;
495             }
496             szUserSid = usersid;
497         }
498         swprintf( keypath, ARRAY_SIZE(keypath), fmtW, szUserSid, squashed_pc );
499         LocalFree(usersid);
500     }
501     if (create) return RegCreateKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, NULL, 0, access, NULL, key, NULL);
502     return RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, access, key);
503 }
504 
505 UINT MSIREG_OpenUserComponentsKey(LPCWSTR szComponent, HKEY *key, BOOL create)
506 {
507     WCHAR squashed_cc[SQUASHED_GUID_SIZE], keypath[0x200];
508     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
509     UINT ret;
510 
511     if (!squash_guid( szComponent, squashed_cc)) return ERROR_FUNCTION_FAILED;
512     TRACE("%s squashed %s\n", debugstr_w(szComponent), debugstr_w(squashed_cc));
513 
514     lstrcpyW(keypath, L"Software\\Microsoft\\Installer\\Components\\");
515     lstrcatW( keypath, squashed_cc );
516 
517     if (create) return RegCreateKeyW(HKEY_CURRENT_USER, keypath, key);
518     ret = RegOpenKeyW(HKEY_CURRENT_USER, keypath, key);
519     if (ret != ERROR_FILE_NOT_FOUND) return ret;
520 
521     lstrcpyW(keypath, L"Software\\Classes\\Installer\\Components\\");
522     lstrcatW( keypath, squashed_cc );
523     return RegOpenKeyExW( HKEY_LOCAL_MACHINE, keypath, 0, access, key );
524 }
525 
526 UINT MSIREG_OpenUserDataComponentKey(LPCWSTR szComponent, LPCWSTR szUserSid, HKEY *key, BOOL create)
527 {
528     static const WCHAR fmtW[] =
529         L"Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\%s\\Components\\%s";
530     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
531     WCHAR *usersid, squashed_comp[SQUASHED_GUID_SIZE], keypath[0x200];
532 
533     if (!squash_guid( szComponent, squashed_comp )) return ERROR_FUNCTION_FAILED;
534     TRACE("%s squashed %s\n", debugstr_w(szComponent), debugstr_w(squashed_comp));
535 
536     if (!szUserSid)
537     {
538         if (!(usersid = get_user_sid()))
539         {
540             ERR("Failed to retrieve user SID\n");
541             return ERROR_FUNCTION_FAILED;
542         }
543         swprintf( keypath, ARRAY_SIZE(keypath), fmtW, usersid, squashed_comp );
544         LocalFree(usersid);
545     }
546     else swprintf( keypath, ARRAY_SIZE(keypath), fmtW, szUserSid, squashed_comp );
547 
548     if (create) return RegCreateKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, NULL, 0, access, NULL, key, NULL);
549     return RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, access, key);
550 }
551 
552 UINT MSIREG_DeleteUserDataComponentKey(LPCWSTR szComponent, LPCWSTR szUserSid)
553 {
554     static const WCHAR fmtW[] =
555         L"Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\%s\\Components";
556     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
557     WCHAR *usersid, squashed_comp[SQUASHED_GUID_SIZE], keypath[0x200];
558     HKEY hkey;
559     LONG r;
560 
561     if (!squash_guid( szComponent, squashed_comp )) return ERROR_FUNCTION_FAILED;
562     TRACE("%s squashed %s\n", debugstr_w(szComponent), debugstr_w(squashed_comp));
563 
564     if (!szUserSid)
565     {
566         if (!(usersid = get_user_sid()))
567         {
568             ERR("Failed to retrieve user SID\n");
569             return ERROR_FUNCTION_FAILED;
570         }
571         swprintf(keypath, ARRAY_SIZE(keypath), fmtW, usersid);
572         LocalFree(usersid);
573     }
574     else swprintf(keypath, ARRAY_SIZE(keypath), fmtW, szUserSid);
575 
576     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, access, &hkey)) return ERROR_SUCCESS;
577     r = RegDeleteTreeW( hkey, squashed_comp );
578     RegCloseKey(hkey);
579     return r;
580 }
581 
582 UINT MSIREG_OpenUserDataProductKey(LPCWSTR szProduct, MSIINSTALLCONTEXT dwContext, LPCWSTR szUserSid, HKEY *key, BOOL create)
583 {
584     static const WCHAR fmtW[] =
585         L"Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\%s\\Products\\%s";
586     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
587     WCHAR *usersid, squashed_pc[SQUASHED_GUID_SIZE], keypath[0x200];
588 
589     if (!squash_guid( szProduct, squashed_pc )) return ERROR_FUNCTION_FAILED;
590     TRACE("%s squashed %s\n", debugstr_w(szProduct), debugstr_w(squashed_pc));
591 
592     if (dwContext == MSIINSTALLCONTEXT_MACHINE)
593         swprintf( keypath, ARRAY_SIZE(keypath), fmtW, L"S-1-5-18", squashed_pc );
594     else if (szUserSid)
595         swprintf( keypath, ARRAY_SIZE(keypath), fmtW, szUserSid, squashed_pc );
596     else
597     {
598         if (!(usersid = get_user_sid()))
599         {
600             ERR("Failed to retrieve user SID\n");
601             return ERROR_FUNCTION_FAILED;
602         }
603         swprintf( keypath, ARRAY_SIZE(keypath), fmtW, usersid, squashed_pc );
604         LocalFree(usersid);
605     }
606     if (create) return RegCreateKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, NULL, 0, access, NULL, key, NULL);
607     return RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, access, key);
608 }
609 
610 UINT MSIREG_OpenUserDataPatchKey(LPCWSTR szPatch, MSIINSTALLCONTEXT dwContext, HKEY *key, BOOL create)
611 {
612     static const WCHAR fmtW[] =
613         L"Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\%s\\Patches\\%s";
614     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
615     WCHAR *usersid, squashed_patch[SQUASHED_GUID_SIZE], keypath[0x200];
616 
617     if (!squash_guid( szPatch, squashed_patch )) return ERROR_FUNCTION_FAILED;
618     TRACE("%s squashed %s\n", debugstr_w(szPatch), debugstr_w(squashed_patch));
619 
620     if (dwContext == MSIINSTALLCONTEXT_MACHINE)
621         swprintf( keypath, ARRAY_SIZE(keypath), fmtW, L"S-1-5-18", squashed_patch );
622     else
623     {
624         if (!(usersid = get_user_sid()))
625         {
626             ERR("Failed to retrieve user SID\n");
627             return ERROR_FUNCTION_FAILED;
628         }
629         swprintf( keypath, ARRAY_SIZE(keypath), fmtW, usersid, squashed_patch );
630         LocalFree(usersid);
631     }
632     if (create) return RegCreateKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, NULL, 0, access, NULL, key, NULL);
633     return RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, access, key);
634 }
635 
636 UINT MSIREG_DeleteUserDataPatchKey(LPCWSTR patch, MSIINSTALLCONTEXT context)
637 {
638     static const WCHAR fmtW[] =
639         L"Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\%s\\Patches";
640     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
641     WCHAR *usersid, squashed_patch[SQUASHED_GUID_SIZE], keypath[0x200];
642     HKEY hkey;
643     LONG r;
644 
645     if (!squash_guid( patch, squashed_patch )) return ERROR_FUNCTION_FAILED;
646     TRACE("%s squashed %s\n", debugstr_w(patch), debugstr_w(squashed_patch));
647 
648     if (context == MSIINSTALLCONTEXT_MACHINE)
649         swprintf(keypath, ARRAY_SIZE(keypath), fmtW, L"S-1-5-18");
650     else
651     {
652         if (!(usersid = get_user_sid()))
653         {
654             ERR("Failed to retrieve user SID\n");
655             return ERROR_FUNCTION_FAILED;
656         }
657         swprintf(keypath, ARRAY_SIZE(keypath), fmtW, usersid);
658         LocalFree(usersid);
659     }
660     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, access, &hkey)) return ERROR_SUCCESS;
661     r = RegDeleteTreeW( hkey, squashed_patch );
662     RegCloseKey(hkey);
663     return r;
664 }
665 
666 UINT MSIREG_OpenUserDataProductPatchesKey(LPCWSTR product, MSIINSTALLCONTEXT context, HKEY *key, BOOL create)
667 {
668     static const WCHAR fmtW[] =
669         L"Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\%s\\Products\\%s\\Patches";
670     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
671     WCHAR *usersid, squashed_product[SQUASHED_GUID_SIZE], keypath[0x200];
672 
673     if (!squash_guid( product, squashed_product )) return ERROR_FUNCTION_FAILED;
674     TRACE("%s squashed %s\n", debugstr_w(product), debugstr_w(squashed_product));
675 
676     if (context == MSIINSTALLCONTEXT_MACHINE)
677         swprintf( keypath, ARRAY_SIZE(keypath), fmtW, L"S-1-5-18", squashed_product );
678     else
679     {
680         if (!(usersid = get_user_sid()))
681         {
682             ERR("Failed to retrieve user SID\n");
683             return ERROR_FUNCTION_FAILED;
684         }
685         swprintf( keypath, ARRAY_SIZE(keypath), fmtW, usersid, squashed_product );
686         LocalFree(usersid);
687     }
688     if (create) return RegCreateKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, NULL, 0, access, NULL, key, NULL);
689     return RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, access, key);
690 }
691 
692 UINT MSIREG_OpenInstallProps(LPCWSTR szProduct, MSIINSTALLCONTEXT dwContext, LPCWSTR szUserSid, HKEY *key, BOOL create)
693 {
694     static const WCHAR fmtW[] =
695         L"Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\%s\\Products\\%s\\InstallProperties";
696     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
697     WCHAR *usersid, squashed_pc[SQUASHED_GUID_SIZE], keypath[0x200];
698 
699     if (!squash_guid( szProduct, squashed_pc )) return ERROR_FUNCTION_FAILED;
700     TRACE("%s squashed %s\n", debugstr_w(szProduct), debugstr_w(squashed_pc));
701 
702     if (dwContext == MSIINSTALLCONTEXT_MACHINE)
703         swprintf( keypath, ARRAY_SIZE(keypath), fmtW, L"S-1-5-18", squashed_pc );
704     else if (szUserSid)
705         swprintf( keypath, ARRAY_SIZE(keypath), fmtW, szUserSid, squashed_pc );
706     else
707     {
708         if (!(usersid = get_user_sid()))
709         {
710             ERR("Failed to retrieve user SID\n");
711             return ERROR_FUNCTION_FAILED;
712         }
713         swprintf( keypath, ARRAY_SIZE(keypath), fmtW, usersid, squashed_pc );
714         LocalFree(usersid);
715     }
716     if (create) return RegCreateKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, NULL, 0, access, NULL, key, NULL);
717     return RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, access, key);
718 }
719 
720 UINT MSIREG_DeleteUserDataProductKey(LPCWSTR szProduct, MSIINSTALLCONTEXT context)
721 {
722     static const WCHAR fmtW[] =
723         L"Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\%s\\Products";
724     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
725     WCHAR *usersid, squashed_pc[SQUASHED_GUID_SIZE], keypath[0x200];
726     HKEY hkey;
727     LONG r;
728 
729     if (!squash_guid( szProduct, squashed_pc )) return ERROR_FUNCTION_FAILED;
730     TRACE("%s squashed %s\n", debugstr_w(szProduct), debugstr_w(squashed_pc));
731 
732     if (context == MSIINSTALLCONTEXT_MACHINE)
733         swprintf(keypath, ARRAY_SIZE(keypath), fmtW, L"S-1-5-18");
734     else
735     {
736         if (!(usersid = get_user_sid()))
737         {
738             ERR("Failed to retrieve user SID\n");
739             return ERROR_FUNCTION_FAILED;
740         }
741         swprintf(keypath, ARRAY_SIZE(keypath), fmtW, usersid);
742         LocalFree(usersid);
743     }
744 
745     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, access, &hkey)) return ERROR_SUCCESS;
746     r = RegDeleteTreeW( hkey, squashed_pc );
747     RegCloseKey(hkey);
748     return r;
749 }
750 
751 UINT MSIREG_DeleteProductKey(LPCWSTR szProduct)
752 {
753     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
754     WCHAR squashed_pc[SQUASHED_GUID_SIZE];
755     HKEY hkey;
756     LONG r;
757 
758     if (!squash_guid( szProduct, squashed_pc )) return ERROR_FUNCTION_FAILED;
759     TRACE("%s squashed %s\n", debugstr_w(szProduct), debugstr_w(squashed_pc));
760 
761     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\Products",
762                       0, access, &hkey)) return ERROR_SUCCESS;
763     r = RegDeleteTreeW( hkey, squashed_pc );
764     RegCloseKey(hkey);
765     return r;
766 }
767 
768 UINT MSIREG_OpenPatchesKey(LPCWSTR szPatch, HKEY *key, BOOL create)
769 {
770     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
771     WCHAR squashed_pc[SQUASHED_GUID_SIZE], keypath[0x200];
772 
773     if (!squash_guid( szPatch, squashed_pc )) return ERROR_FUNCTION_FAILED;
774     TRACE("%s squashed %s\n", debugstr_w(szPatch), debugstr_w(squashed_pc));
775 
776     lstrcpyW( keypath, L"Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\Patches\\" );
777     lstrcatW( keypath, squashed_pc );
778 
779     if (create) return RegCreateKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, NULL, 0, access, NULL, key, NULL);
780     return RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, access, key);
781 }
782 
783 UINT MSIREG_OpenUpgradeCodesKey(LPCWSTR szUpgradeCode, HKEY *key, BOOL create)
784 {
785     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
786     WCHAR squashed_uc[SQUASHED_GUID_SIZE], keypath[0x200];
787 
788     if (!squash_guid( szUpgradeCode, squashed_uc )) return ERROR_FUNCTION_FAILED;
789     TRACE("%s squashed %s\n", debugstr_w(szUpgradeCode), debugstr_w(squashed_uc));
790 
791     lstrcpyW( keypath, L"Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UpgradeCodes\\" );
792     lstrcatW( keypath, squashed_uc );
793 
794     if (create) return RegCreateKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, NULL, 0, access, NULL, key, NULL);
795     return RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, access, key);
796 }
797 
798 UINT MSIREG_OpenUserUpgradeCodesKey(LPCWSTR szUpgradeCode, HKEY* key, BOOL create)
799 {
800     WCHAR squashed_uc[SQUASHED_GUID_SIZE], keypath[0x200];
801 
802     if (!squash_guid( szUpgradeCode, squashed_uc )) return ERROR_FUNCTION_FAILED;
803     TRACE("%s squashed %s\n", debugstr_w(szUpgradeCode), debugstr_w(squashed_uc));
804 
805     lstrcpyW(keypath, L"Software\\Microsoft\\Installer\\UpgradeCodes\\");
806     lstrcatW( keypath, squashed_uc );
807 
808     if (create) return RegCreateKeyW(HKEY_CURRENT_USER, keypath, key);
809     return RegOpenKeyW(HKEY_CURRENT_USER, keypath, key);
810 }
811 
812 UINT MSIREG_DeleteUpgradeCodesKey( const WCHAR *code )
813 {
814     WCHAR squashed_code[SQUASHED_GUID_SIZE];
815     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
816     HKEY hkey;
817     LONG ret;
818 
819     if (!squash_guid( code, squashed_code )) return ERROR_FUNCTION_FAILED;
820     TRACE( "%s squashed %s\n", debugstr_w(code), debugstr_w(squashed_code) );
821 
822     if (RegOpenKeyExW( HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UpgradeCodes\\",
823                        0, access, &hkey )) return ERROR_SUCCESS;
824     ret = RegDeleteTreeW( hkey, squashed_code );
825     RegCloseKey( hkey );
826     return ret;
827 }
828 
829 UINT MSIREG_DeleteUserUpgradeCodesKey(LPCWSTR szUpgradeCode)
830 {
831     WCHAR squashed_uc[SQUASHED_GUID_SIZE], keypath[0x200];
832 
833     if (!squash_guid( szUpgradeCode, squashed_uc )) return ERROR_FUNCTION_FAILED;
834     TRACE("%s squashed %s\n", debugstr_w(szUpgradeCode), debugstr_w(squashed_uc));
835 
836     lstrcpyW(keypath, L"Software\\Microsoft\\Installer\\UpgradeCodes\\");
837     lstrcatW( keypath, squashed_uc );
838     return RegDeleteTreeW(HKEY_CURRENT_USER, keypath);
839 }
840 
841 UINT MSIREG_DeleteLocalClassesProductKey(LPCWSTR szProductCode)
842 {
843     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
844     WCHAR squashed_pc[SQUASHED_GUID_SIZE];
845     HKEY hkey;
846     LONG r;
847 
848     if (!squash_guid( szProductCode, squashed_pc )) return ERROR_FUNCTION_FAILED;
849     TRACE("%s squashed %s\n", debugstr_w(szProductCode), debugstr_w(squashed_pc));
850 
851     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Classes\\Installer\\Products", 0, access, &hkey))
852         return ERROR_SUCCESS;
853     r = RegDeleteTreeW( hkey, squashed_pc );
854     RegCloseKey(hkey);
855     return r;
856 }
857 
858 UINT MSIREG_DeleteLocalClassesFeaturesKey(LPCWSTR szProductCode)
859 {
860     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
861     WCHAR squashed_pc[SQUASHED_GUID_SIZE];
862     HKEY hkey;
863     LONG r;
864 
865     if (!squash_guid( szProductCode, squashed_pc )) return ERROR_FUNCTION_FAILED;
866     TRACE("%s squashed %s\n", debugstr_w(szProductCode), debugstr_w(squashed_pc));
867 
868     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Classes\\Installer\\Features", 0, access, &hkey))
869         return ERROR_SUCCESS;
870     r = RegDeleteTreeW( hkey, squashed_pc );
871     RegCloseKey(hkey);
872     return r;
873 }
874 
875 UINT MSIREG_OpenClassesUpgradeCodesKey(LPCWSTR szUpgradeCode, HKEY *key, BOOL create)
876 {
877     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
878     WCHAR squashed_uc[SQUASHED_GUID_SIZE], keypath[0x200];
879 
880     if (!squash_guid( szUpgradeCode, squashed_uc )) return ERROR_FUNCTION_FAILED;
881     TRACE("%s squashed %s\n", debugstr_w(szUpgradeCode), debugstr_w(squashed_uc));
882 
883     lstrcpyW(keypath, L"Software\\Classes\\Installer\\UpgradeCodes\\");
884     lstrcatW( keypath, squashed_uc );
885 
886     if (create) return RegCreateKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, NULL, 0, access, NULL, key, NULL);
887     return RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, access, key);
888 }
889 
890 UINT MSIREG_DeleteClassesUpgradeCodesKey(LPCWSTR szUpgradeCode)
891 {
892     REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS;
893     WCHAR squashed_uc[SQUASHED_GUID_SIZE];
894     HKEY hkey;
895     LONG r;
896 
897     if (!squash_guid( szUpgradeCode, squashed_uc )) return ERROR_FUNCTION_FAILED;
898     TRACE("%s squashed %s\n", debugstr_w(szUpgradeCode), debugstr_w(squashed_uc));
899 
900     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Classes\\Installer\\UpgradeCodes", 0, access, &hkey))
901         return ERROR_SUCCESS;
902     r = RegDeleteTreeW( hkey, squashed_uc );
903     RegCloseKey(hkey);
904     return r;
905 }
906 
907 /*************************************************************************
908  *  MsiDecomposeDescriptorW   [MSI.@]
909  *
910  * Decomposes an MSI descriptor into product, feature and component parts.
911  * An MSI descriptor is a string of the form:
912  *   [base 85 guid] [feature code] '>' [base 85 guid] or
913  *   [base 85 guid] [feature code] '<'
914  *
915  * PARAMS
916  *   szDescriptor  [I]  the descriptor to decompose
917  *   szProduct     [O]  buffer of MAX_FEATURE_CHARS+1 for the product guid
918  *   szFeature     [O]  buffer of MAX_FEATURE_CHARS+1 for the feature code
919  *   szComponent   [O]  buffer of MAX_FEATURE_CHARS+1 for the component guid
920  *   pUsed         [O]  the length of the descriptor
921  *
922  * RETURNS
923  *   ERROR_SUCCESS             if everything worked correctly
924  *   ERROR_INVALID_PARAMETER   if the descriptor was invalid
925  *
926  */
927 UINT WINAPI MsiDecomposeDescriptorW( LPCWSTR szDescriptor, LPWSTR szProduct,
928                 LPWSTR szFeature, LPWSTR szComponent, LPDWORD pUsed )
929 {
930     UINT len;
931     const WCHAR *p;
932     GUID product, component;
933 
934     TRACE("%s %p %p %p %p\n", debugstr_w(szDescriptor), szProduct,
935           szFeature, szComponent, pUsed);
936 
937     if (!decode_base85_guid( szDescriptor, &product ))
938         return ERROR_INVALID_PARAMETER;
939 
940     TRACE("product %s\n", debugstr_guid( &product ));
941 
942     if (!(p = wcschr( &szDescriptor[20], '>' )))
943         p = wcschr( &szDescriptor[20], '<' );
944     if (!p)
945         return ERROR_INVALID_PARAMETER;
946 
947     len = (p - &szDescriptor[20]);
948     if( len > MAX_FEATURE_CHARS )
949         return ERROR_INVALID_PARAMETER;
950 
951     TRACE("feature %s\n", debugstr_wn( &szDescriptor[20], len ));
952 
953     if (*p == '>')
954     {
955         if (!decode_base85_guid( p+1, &component ))
956             return ERROR_INVALID_PARAMETER;
957         TRACE( "component %s\n", debugstr_guid(&component) );
958     }
959 
960     if (szProduct)
961         StringFromGUID2( &product, szProduct, MAX_FEATURE_CHARS+1 );
962     if (szComponent)
963     {
964         if (*p == '>')
965             StringFromGUID2( &component, szComponent, MAX_FEATURE_CHARS+1 );
966         else
967             szComponent[0] = 0;
968     }
969     if (szFeature)
970     {
971         memcpy( szFeature, &szDescriptor[20], len*sizeof(WCHAR) );
972         szFeature[len] = 0;
973     }
974 
975     len = p - szDescriptor + 1;
976     if (*p == '>') len += 20;
977 
978     TRACE("length = %d\n", len);
979     if (pUsed) *pUsed = len;
980 
981     return ERROR_SUCCESS;
982 }
983 
984 UINT WINAPI MsiDecomposeDescriptorA( LPCSTR szDescriptor, LPSTR szProduct,
985                 LPSTR szFeature, LPSTR szComponent, LPDWORD pUsed )
986 {
987     WCHAR product[MAX_FEATURE_CHARS+1];
988     WCHAR feature[MAX_FEATURE_CHARS+1];
989     WCHAR component[MAX_FEATURE_CHARS+1];
990     LPWSTR str = NULL, p = NULL, f = NULL, c = NULL;
991     UINT r;
992 
993     TRACE("%s %p %p %p %p\n", debugstr_a(szDescriptor), szProduct,
994           szFeature, szComponent, pUsed);
995 
996     str = strdupAtoW( szDescriptor );
997     if( szDescriptor && !str )
998         return ERROR_OUTOFMEMORY;
999 
1000     if (szProduct)
1001         p = product;
1002     if (szFeature)
1003         f = feature;
1004     if (szComponent)
1005         c = component;
1006 
1007     r = MsiDecomposeDescriptorW( str, p, f, c, pUsed );
1008 
1009     if (r == ERROR_SUCCESS)
1010     {
1011         WideCharToMultiByte( CP_ACP, 0, p, -1,
1012                              szProduct, MAX_FEATURE_CHARS+1, NULL, NULL );
1013         WideCharToMultiByte( CP_ACP, 0, f, -1,
1014                              szFeature, MAX_FEATURE_CHARS+1, NULL, NULL );
1015         WideCharToMultiByte( CP_ACP, 0, c, -1,
1016                              szComponent, MAX_FEATURE_CHARS+1, NULL, NULL );
1017     }
1018 
1019     msi_free( str );
1020 
1021     return r;
1022 }
1023 
1024 UINT WINAPI MsiEnumProductsA( DWORD index, char *lpguid )
1025 {
1026     DWORD r;
1027     WCHAR szwGuid[GUID_SIZE];
1028 
1029     TRACE( "%lu, %p\n", index, lpguid );
1030 
1031     if (NULL == lpguid)
1032         return ERROR_INVALID_PARAMETER;
1033     r = MsiEnumProductsW(index, szwGuid);
1034     if( r == ERROR_SUCCESS )
1035         WideCharToMultiByte(CP_ACP, 0, szwGuid, -1, lpguid, GUID_SIZE, NULL, NULL);
1036 
1037     return r;
1038 }
1039 
1040 UINT WINAPI MsiEnumProductsW( DWORD index, WCHAR *lpguid )
1041 {
1042     TRACE("%lu, %p\n", index, lpguid );
1043 
1044     if (NULL == lpguid)
1045         return ERROR_INVALID_PARAMETER;
1046 
1047     return MsiEnumProductsExW( NULL, L"S-1-1-0", MSIINSTALLCONTEXT_ALL, index, lpguid,
1048                                NULL, NULL, NULL );
1049 }
1050 
1051 UINT WINAPI MsiEnumFeaturesA( const char *szProduct, DWORD index, char *szFeature, char *szParent )
1052 {
1053     DWORD r;
1054     WCHAR szwFeature[GUID_SIZE], szwParent[GUID_SIZE];
1055     WCHAR *szwProduct = NULL;
1056 
1057     TRACE( "%s, %lu, %p, %p\n", debugstr_a(szProduct), index, szFeature, szParent );
1058 
1059     if( szProduct )
1060     {
1061         szwProduct = strdupAtoW( szProduct );
1062         if( !szwProduct )
1063             return ERROR_OUTOFMEMORY;
1064     }
1065 
1066     r = MsiEnumFeaturesW(szwProduct, index, szwFeature, szwParent);
1067     if( r == ERROR_SUCCESS )
1068     {
1069         WideCharToMultiByte(CP_ACP, 0, szwFeature, -1, szFeature, GUID_SIZE, NULL, NULL);
1070         WideCharToMultiByte(CP_ACP, 0, szwParent, -1, szParent, GUID_SIZE, NULL, NULL);
1071     }
1072 
1073     msi_free( szwProduct);
1074 
1075     return r;
1076 }
1077 
1078 UINT WINAPI MsiEnumFeaturesW( const WCHAR *szProduct, DWORD index, WCHAR *szFeature, WCHAR *szParent )
1079 {
1080     HKEY hkeyProduct = 0;
1081     DWORD r, sz;
1082 
1083     TRACE( "%s, %lu, %p, %p\n", debugstr_w(szProduct), index, szFeature, szParent );
1084 
1085     if( !szProduct )
1086         return ERROR_INVALID_PARAMETER;
1087 
1088     r = MSIREG_OpenInstallerFeaturesKey(szProduct,&hkeyProduct,FALSE);
1089     if( r != ERROR_SUCCESS )
1090         return ERROR_NO_MORE_ITEMS;
1091 
1092     sz = GUID_SIZE;
1093     r = RegEnumValueW(hkeyProduct, index, szFeature, &sz, NULL, NULL, NULL, NULL);
1094     RegCloseKey(hkeyProduct);
1095 
1096     return r;
1097 }
1098 
1099 UINT WINAPI MsiEnumComponentsA( DWORD index, char *lpguid )
1100 {
1101     DWORD r;
1102     WCHAR szwGuid[GUID_SIZE];
1103 
1104     TRACE( "%lu, %p\n", index, lpguid );
1105 
1106     if (!lpguid) return ERROR_INVALID_PARAMETER;
1107 
1108     r = MsiEnumComponentsW(index, szwGuid);
1109     if( r == ERROR_SUCCESS )
1110         WideCharToMultiByte(CP_ACP, 0, szwGuid, -1, lpguid, GUID_SIZE, NULL, NULL);
1111 
1112     return r;
1113 }
1114 
1115 UINT WINAPI MsiEnumComponentsW( DWORD index, WCHAR *lpguid )
1116 {
1117     TRACE( "%lu, %p\n", index, lpguid );
1118 
1119     if (!lpguid) return ERROR_INVALID_PARAMETER;
1120 
1121     return MsiEnumComponentsExW( L"S-1-1-0", MSIINSTALLCONTEXT_ALL, index, lpguid, NULL, NULL, NULL );
1122 }
1123 
1124 UINT WINAPI MsiEnumComponentsExA( const char *user_sid, DWORD ctx, DWORD index, CHAR guid[39],
1125                                   MSIINSTALLCONTEXT *installed_ctx, char *sid, DWORD *sid_len )
1126 {
1127     UINT r;
1128     WCHAR *user_sidW = NULL, *sidW = NULL, guidW[GUID_SIZE];
1129 
1130     TRACE( "%s, %#lx, %lu, %p, %p, %p, %p\n", debugstr_a(user_sid), ctx, index, guid, installed_ctx,
1131            sid, sid_len );
1132 
1133     if (sid && !sid_len) return ERROR_INVALID_PARAMETER;
1134     if (user_sid && !(user_sidW = strdupAtoW( user_sid ))) return ERROR_OUTOFMEMORY;
1135     if (sid && !(sidW = msi_alloc( *sid_len * sizeof(WCHAR) )))
1136     {
1137         msi_free( user_sidW );
1138         return ERROR_OUTOFMEMORY;
1139     }
1140     r = MsiEnumComponentsExW( user_sidW, ctx, index, guidW, installed_ctx, sidW, sid_len );
1141     if (r == ERROR_SUCCESS)
1142     {
1143         if (guid) WideCharToMultiByte( CP_ACP, 0, guidW, GUID_SIZE, guid, GUID_SIZE, NULL, NULL );
1144         if (sid) WideCharToMultiByte( CP_ACP, 0, sidW, *sid_len + 1, sid, *sid_len + 1, NULL, NULL );
1145     }
1146     msi_free( user_sidW );
1147     msi_free( sidW );
1148     return r;
1149 }
1150 
1151 static UINT fetch_machine_component( DWORD ctx, DWORD index, DWORD *idx, WCHAR guid[39],
1152                                      MSIINSTALLCONTEXT *installed_ctx, LPWSTR sid, LPDWORD sid_len )
1153 {
1154     UINT r = ERROR_SUCCESS;
1155     WCHAR component[SQUASHED_GUID_SIZE];
1156     DWORD i = 0, len_component;
1157     REGSAM access = KEY_ENUMERATE_SUB_KEYS | KEY_WOW64_64KEY;
1158     HKEY key_components;
1159 
1160     if (RegOpenKeyExW( HKEY_LOCAL_MACHINE,
1161                        L"Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\S-1-5-18\\Components",
1162                        0, access, &key_components ))
1163         return ERROR_NO_MORE_ITEMS;
1164 
1165     len_component = ARRAY_SIZE( component );
1166     while (!RegEnumKeyExW( key_components, i, component, &len_component, NULL, NULL, NULL, NULL ))
1167     {
1168         if (*idx == index) goto found;
1169         (*idx)++;
1170         len_component = ARRAY_SIZE( component );
1171         i++;
1172     }
1173     RegCloseKey( key_components );
1174     return ERROR_NO_MORE_ITEMS;
1175 
1176 found:
1177     if (sid_len)
1178     {
1179         if (*sid_len < 1)
1180         {
1181             *sid_len = 1;
1182             r = ERROR_MORE_DATA;
1183         }
1184         else if (sid)
1185         {
1186             *sid_len = 0;
1187             sid[0] = 0;
1188         }
1189     }
1190     if (guid) unsquash_guid( component, guid );
1191     if (installed_ctx) *installed_ctx = MSIINSTALLCONTEXT_MACHINE;
1192     RegCloseKey( key_components );
1193     return r;
1194 }
1195 
1196 static UINT fetch_user_component( const WCHAR *usersid, DWORD ctx, DWORD index, DWORD *idx,
1197                                   WCHAR guid[39], MSIINSTALLCONTEXT *installed_ctx, LPWSTR sid,
1198                                   LPDWORD sid_len )
1199 {
1200     UINT r = ERROR_SUCCESS;
1201     WCHAR path[MAX_PATH], component[SQUASHED_GUID_SIZE], user[128];
1202     DWORD i = 0, j = 0, len_component, len_user;
1203     REGSAM access = KEY_ENUMERATE_SUB_KEYS | KEY_WOW64_64KEY;
1204     HKEY key_users, key_components;
1205 
1206     if (ctx == MSIINSTALLCONTEXT_USERMANAGED) /* FIXME: where to find these? */
1207         return ERROR_NO_MORE_ITEMS;
1208 
1209     if (RegOpenKeyExW( HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData",
1210                        0, access, &key_users )) return ERROR_NO_MORE_ITEMS;
1211 
1212     len_user = ARRAY_SIZE( user );
1213     while (!RegEnumKeyExW( key_users, i, user, &len_user, NULL, NULL, NULL, NULL ))
1214     {
1215         if ((wcscmp( usersid, L"S-1-1-0" ) && wcscmp( usersid, user )) ||
1216             !wcscmp( L"S-1-5-18", user ))
1217         {
1218             i++;
1219             len_user = ARRAY_SIZE( user );
1220             continue;
1221         }
1222         lstrcpyW( path, user );
1223         lstrcatW( path, L"\\Components" );
1224         if (RegOpenKeyExW( key_users, path, 0, access, &key_components ))
1225         {
1226             i++;
1227             len_user = ARRAY_SIZE( user );
1228             continue;
1229         }
1230         len_component = ARRAY_SIZE( component );
1231         while (!RegEnumKeyExW( key_components, j, component, &len_component, NULL, NULL, NULL, NULL ))
1232         {
1233             if (*idx == index) goto found;
1234             (*idx)++;
1235             len_component = ARRAY_SIZE( component );
1236             j++;
1237         }
1238         RegCloseKey( key_components );
1239         len_user = ARRAY_SIZE( user );
1240         i++;
1241     }
1242     RegCloseKey( key_users );
1243     return ERROR_NO_MORE_ITEMS;
1244 
1245 found:
1246     if (sid_len)
1247     {
1248         if (*sid_len < len_user + 1)
1249         {
1250             *sid_len = len_user + 1;
1251             r = ERROR_MORE_DATA;
1252         }
1253         else if (sid)
1254         {
1255             *sid_len = len_user;
1256             lstrcpyW( sid, user );
1257         }
1258     }
1259     if (guid) unsquash_guid( component, guid );
1260     if (installed_ctx) *installed_ctx = ctx;
1261     RegCloseKey( key_components );
1262     RegCloseKey( key_users );
1263     return r;
1264 }
1265 
1266 static UINT enum_components( const WCHAR *usersid, DWORD ctx, DWORD index, DWORD *idx, WCHAR guid[39],
1267                              MSIINSTALLCONTEXT *installed_ctx, LPWSTR sid, LPDWORD sid_len )
1268 {
1269     UINT r = ERROR_NO_MORE_ITEMS;
1270     WCHAR *user = NULL;
1271 
1272     if (!usersid)
1273     {
1274         usersid = user = get_user_sid();
1275         if (!user) return ERROR_FUNCTION_FAILED;
1276     }
1277     if (ctx & MSIINSTALLCONTEXT_USERMANAGED)
1278     {
1279         r = fetch_user_component( usersid, MSIINSTALLCONTEXT_USERMANAGED, index, idx, guid,
1280                                   installed_ctx, sid, sid_len );
1281         if (r != ERROR_NO_MORE_ITEMS) goto done;
1282     }
1283     if (ctx & MSIINSTALLCONTEXT_USERUNMANAGED)
1284     {
1285         r = fetch_user_component( usersid, MSIINSTALLCONTEXT_USERUNMANAGED, index, idx, guid,
1286                                   installed_ctx, sid, sid_len );
1287         if (r != ERROR_NO_MORE_ITEMS) goto done;
1288     }
1289     if (ctx & MSIINSTALLCONTEXT_MACHINE)
1290     {
1291         r = fetch_machine_component( MSIINSTALLCONTEXT_MACHINE, index, idx, guid, installed_ctx,
1292                                      sid, sid_len );
1293         if (r != ERROR_NO_MORE_ITEMS) goto done;
1294     }
1295 
1296 done:
1297     LocalFree( user );
1298     return r;
1299 }
1300 
1301 UINT WINAPI MsiEnumComponentsExW( const WCHAR *user_sid, DWORD ctx, DWORD index, WCHAR guid[39],
1302                                   MSIINSTALLCONTEXT *installed_ctx, WCHAR *sid, DWORD *sid_len )
1303 {
1304     UINT r;
1305     DWORD idx = 0;
1306     static DWORD last_index;
1307 
1308     TRACE( "%s, %#lx, %lu, %p, %p, %p, %p\n", debugstr_w(user_sid), ctx, index, guid, installed_ctx,
1309           sid, sid_len );
1310 
1311     if ((sid && !sid_len) || !ctx || (user_sid && ctx == MSIINSTALLCONTEXT_MACHINE))
1312         return ERROR_INVALID_PARAMETER;
1313 
1314     if (index && index - last_index != 1)
1315         return ERROR_INVALID_PARAMETER;
1316 
1317     if (!index) last_index = 0;
1318 
1319     r = enum_components( user_sid, ctx, index, &idx, guid, installed_ctx, sid, sid_len );
1320     if (r == ERROR_SUCCESS)
1321         last_index = index;
1322     else
1323         last_index = 0;
1324 
1325     return r;
1326 }
1327 
1328 UINT WINAPI MsiEnumClientsA( const char *szComponent, DWORD index, char *szProduct )
1329 {
1330     DWORD r;
1331     WCHAR szwProduct[GUID_SIZE];
1332     WCHAR *szwComponent = NULL;
1333 
1334     TRACE( "%s, %lu, %p\n", debugstr_a(szComponent), index, szProduct );
1335 
1336     if ( !szProduct )
1337         return ERROR_INVALID_PARAMETER;
1338 
1339     if( szComponent )
1340     {
1341         szwComponent = strdupAtoW( szComponent );
1342         if( !szwComponent )
1343             return ERROR_OUTOFMEMORY;
1344     }
1345 
1346     r = MsiEnumClientsW(szComponent?szwComponent:NULL, index, szwProduct);
1347     if( r == ERROR_SUCCESS )
1348         WideCharToMultiByte(CP_ACP, 0, szwProduct, -1, szProduct, GUID_SIZE, NULL, NULL);
1349 
1350     msi_free( szwComponent);
1351 
1352     return r;
1353 }
1354 
1355 UINT WINAPI MsiEnumClientsW( const WCHAR *szComponent, DWORD index, WCHAR *szProduct )
1356 {
1357     HKEY hkeyComp = 0;
1358     DWORD r, sz;
1359     WCHAR szValName[SQUASHED_GUID_SIZE];
1360 
1361     TRACE( "%s, %lu, %p\n", debugstr_w(szComponent), index, szProduct );
1362 
1363     if (!szComponent || !*szComponent || !szProduct)
1364         return ERROR_INVALID_PARAMETER;
1365 
1366     if (MSIREG_OpenUserDataComponentKey(szComponent, NULL, &hkeyComp, FALSE) != ERROR_SUCCESS &&
1367         MSIREG_OpenUserDataComponentKey(szComponent, L"S-1-5-18", &hkeyComp, FALSE) != ERROR_SUCCESS)
1368         return ERROR_UNKNOWN_COMPONENT;
1369 
1370     /* see if there are any products at all */
1371     sz = SQUASHED_GUID_SIZE;
1372     r = RegEnumValueW(hkeyComp, 0, szValName, &sz, NULL, NULL, NULL, NULL);
1373     if (r != ERROR_SUCCESS)
1374     {
1375         RegCloseKey(hkeyComp);
1376 
1377         if (index != 0)
1378             return ERROR_INVALID_PARAMETER;
1379 
1380         return ERROR_UNKNOWN_COMPONENT;
1381     }
1382 
1383     sz = SQUASHED_GUID_SIZE;
1384     r = RegEnumValueW(hkeyComp, index, szValName, &sz, NULL, NULL, NULL, NULL);
1385     if( r == ERROR_SUCCESS )
1386     {
1387         unsquash_guid(szValName, szProduct);
1388         TRACE("-> %s\n", debugstr_w(szProduct));
1389     }
1390     RegCloseKey(hkeyComp);
1391     return r;
1392 }
1393 
1394 UINT WINAPI MsiEnumClientsExA( const char *component, const char *usersid, DWORD ctx, DWORD index,
1395                                char installed_product[GUID_SIZE], MSIINSTALLCONTEXT *installed_ctx, char *sid,
1396                                DWORD *sid_len )
1397 {
1398     FIXME( "%s, %s, %#lx, %lu, %p, %p, %p, %p\n", debugstr_a(component), debugstr_a(usersid), ctx, index,
1399            installed_product, installed_ctx, sid, sid_len );
1400     return ERROR_ACCESS_DENIED;
1401 }
1402 
1403 UINT WINAPI MsiEnumClientsExW( const WCHAR *component, const WCHAR *usersid, DWORD ctx, DWORD index,
1404                                WCHAR installed_product[GUID_SIZE], MSIINSTALLCONTEXT *installed_ctx, WCHAR *sid,
1405                                DWORD *sid_len )
1406 {
1407     FIXME( "%s, %s, %#lx, %lu, %p, %p, %p, %p\n", debugstr_w(component), debugstr_w(usersid), ctx, index,
1408            installed_product, installed_ctx, sid, sid_len );
1409     return ERROR_ACCESS_DENIED;
1410 }
1411 
1412 static UINT MSI_EnumComponentQualifiers( const WCHAR *szComponent, DWORD iIndex, awstring *lpQualBuf,
1413                                          DWORD *pcchQual, awstring *lpAppBuf, DWORD *pcchAppBuf )
1414 {
1415     DWORD name_sz, val_sz, name_max, val_max, type, ofs;
1416     WCHAR *name = NULL, *val = NULL;
1417     UINT r, r2;
1418     HKEY key;
1419 
1420     TRACE( "%s, %lu, %p, %p, %p, %p\n", debugstr_w(szComponent), iIndex, lpQualBuf, pcchQual, lpAppBuf, pcchAppBuf );
1421 
1422     if (!szComponent)
1423         return ERROR_INVALID_PARAMETER;
1424 
1425     r = MSIREG_OpenUserComponentsKey( szComponent, &key, FALSE );
1426     if (r != ERROR_SUCCESS)
1427         return ERROR_UNKNOWN_COMPONENT;
1428 
1429     /* figure out how big the name is we want to return */
1430     name_max = 0x10;
1431     r = ERROR_OUTOFMEMORY;
1432     name = msi_alloc( name_max * sizeof(WCHAR) );
1433     if (!name)
1434         goto end;
1435 
1436     val_max = 0x10;
1437     r = ERROR_OUTOFMEMORY;
1438     val = msi_alloc( val_max );
1439     if (!val)
1440         goto end;
1441 
1442     /* loop until we allocate enough memory */
1443     while (1)
1444     {
1445         name_sz = name_max;
1446         val_sz = val_max;
1447         r = RegEnumValueW( key, iIndex, name, &name_sz, NULL, &type, (BYTE *)val, &val_sz );
1448         if (r == ERROR_SUCCESS)
1449             break;
1450         if (r != ERROR_MORE_DATA)
1451             goto end;
1452 
1453         if (type != REG_MULTI_SZ)
1454         {
1455             ERR( "component data has wrong type (%lu)\n", type );
1456             goto end;
1457         }
1458 
1459         r = ERROR_OUTOFMEMORY;
1460         if (name_sz + 1 >= name_max)
1461         {
1462             name_max *= 2;
1463             msi_free( name );
1464             name = msi_alloc( name_max * sizeof (WCHAR) );
1465             if (!name)
1466                 goto end;
1467             continue;
1468         }
1469         if (val_sz > val_max)
1470         {
1471             val_max = val_sz + sizeof (WCHAR);
1472             msi_free( val );
1473             val = msi_alloc( val_max * sizeof (WCHAR) );
1474             if (!val)
1475                 goto end;
1476             continue;
1477         }
1478         ERR( "should be enough data, but isn't %lu %lu\n", name_sz, val_sz );
1479         goto end;
1480     }
1481 
1482     ofs = 0;
1483     r = MsiDecomposeDescriptorW( val, NULL, NULL, NULL, &ofs );
1484     if (r != ERROR_SUCCESS)
1485         goto end;
1486 
1487     TRACE("Providing %s and %s\n", debugstr_w(name), debugstr_w(val+ofs));
1488 
1489     r = msi_strcpy_to_awstring( name, -1, lpQualBuf, pcchQual );
1490     r2 = msi_strcpy_to_awstring( val+ofs, -1, lpAppBuf, pcchAppBuf );
1491 
1492     if (r2 != ERROR_SUCCESS)
1493         r = r2;
1494 
1495 end:
1496     msi_free(val);
1497     msi_free(name);
1498     RegCloseKey(key);
1499     return r;
1500 }
1501 
1502 /*************************************************************************
1503  *  MsiEnumComponentQualifiersA [MSI.@]
1504  */
1505 UINT WINAPI MsiEnumComponentQualifiersA( const char *szComponent, DWORD iIndex, char *lpQualifierBuf,
1506                                          DWORD *pcchQualifierBuf, char *lpApplicationDataBuf,
1507                                          DWORD *pcchApplicationDataBuf )
1508 {
1509     awstring qual, appdata;
1510     WCHAR *comp;
1511     UINT r;
1512 
1513     TRACE( "%s, %lu, %p, %p, %p, %p\n", debugstr_a(szComponent), iIndex, lpQualifierBuf, pcchQualifierBuf,
1514            lpApplicationDataBuf, pcchApplicationDataBuf );
1515 
1516     comp = strdupAtoW( szComponent );
1517     if (szComponent && !comp)
1518         return ERROR_OUTOFMEMORY;
1519 
1520     qual.unicode = FALSE;
1521     qual.str.a = lpQualifierBuf;
1522 
1523     appdata.unicode = FALSE;
1524     appdata.str.a = lpApplicationDataBuf;
1525 
1526     r = MSI_EnumComponentQualifiers( comp, iIndex,
1527               &qual, pcchQualifierBuf, &appdata, pcchApplicationDataBuf );
1528     msi_free( comp );
1529     return r;
1530 }
1531 
1532 /*************************************************************************
1533  *  MsiEnumComponentQualifiersW [MSI.@]
1534  */
1535 UINT WINAPI MsiEnumComponentQualifiersW( const WCHAR *szComponent, DWORD iIndex, WCHAR *lpQualifierBuf,
1536                                          DWORD *pcchQualifierBuf, WCHAR *lpApplicationDataBuf,
1537                                          DWORD *pcchApplicationDataBuf )
1538 {
1539     awstring qual, appdata;
1540 
1541     TRACE( "%s, %lu, %p, %p, %p, %p\n", debugstr_w(szComponent), iIndex, lpQualifierBuf, pcchQualifierBuf,
1542            lpApplicationDataBuf, pcchApplicationDataBuf );
1543 
1544     qual.unicode = TRUE;
1545     qual.str.w = lpQualifierBuf;
1546 
1547     appdata.unicode = TRUE;
1548     appdata.str.w = lpApplicationDataBuf;
1549 
1550     return MSI_EnumComponentQualifiers( szComponent, iIndex, &qual, pcchQualifierBuf, &appdata, pcchApplicationDataBuf );
1551 }
1552 
1553 /*************************************************************************
1554  *  MsiEnumRelatedProductsW   [MSI.@]
1555  *
1556  */
1557 UINT WINAPI MsiEnumRelatedProductsW( const WCHAR *szUpgradeCode, DWORD dwReserved, DWORD iProductIndex,
1558                                      WCHAR *lpProductBuf )
1559 {
1560     UINT r;
1561     HKEY hkey;
1562     WCHAR szKeyName[SQUASHED_GUID_SIZE];
1563     DWORD dwSize = ARRAY_SIZE(szKeyName);
1564 
1565     TRACE( "%s, %#lx, %lu, %p\n", debugstr_w(szUpgradeCode), dwReserved, iProductIndex, lpProductBuf );
1566 
1567     if (NULL == szUpgradeCode)
1568         return ERROR_INVALID_PARAMETER;
1569     if (NULL == lpProductBuf)
1570         return ERROR_INVALID_PARAMETER;
1571 
1572     r = MSIREG_OpenUpgradeCodesKey(szUpgradeCode, &hkey, FALSE);
1573     if (r != ERROR_SUCCESS)
1574         return ERROR_NO_MORE_ITEMS;
1575 
1576     r = RegEnumValueW(hkey, iProductIndex, szKeyName, &dwSize, NULL, NULL, NULL, NULL);
1577     if( r == ERROR_SUCCESS )
1578         unsquash_guid(szKeyName, lpProductBuf);
1579     RegCloseKey(hkey);
1580 
1581     return r;
1582 }
1583 
1584 /*************************************************************************
1585  *  MsiEnumRelatedProductsA   [MSI.@]
1586  *
1587  */
1588 UINT WINAPI MsiEnumRelatedProductsA( const char *szUpgradeCode, DWORD dwReserved, DWORD iProductIndex,
1589                                      char *lpProductBuf )
1590 {
1591     WCHAR *szwUpgradeCode = NULL;
1592     WCHAR productW[GUID_SIZE];
1593     UINT r;
1594 
1595     TRACE( "%s, %#lx, %lu, %p\n", debugstr_a(szUpgradeCode), dwReserved, iProductIndex, lpProductBuf );
1596 
1597     if (szUpgradeCode)
1598     {
1599         szwUpgradeCode = strdupAtoW( szUpgradeCode );
1600         if( !szwUpgradeCode )
1601             return ERROR_OUTOFMEMORY;
1602     }
1603 
1604     r = MsiEnumRelatedProductsW( szwUpgradeCode, dwReserved,
1605                                  iProductIndex, productW );
1606     if (r == ERROR_SUCCESS)
1607     {
1608         WideCharToMultiByte( CP_ACP, 0, productW, GUID_SIZE,
1609                              lpProductBuf, GUID_SIZE, NULL, NULL );
1610     }
1611     msi_free( szwUpgradeCode);
1612     return r;
1613 }
1614 
1615 /***********************************************************************
1616  * MsiEnumPatchesExA            [MSI.@]
1617  */
1618 UINT WINAPI MsiEnumPatchesExA( const char *szProductCode, const char *szUserSid, DWORD dwContext, DWORD dwFilter,
1619                                DWORD dwIndex, char *szPatchCode, char *szTargetProductCode,
1620                                MSIINSTALLCONTEXT *pdwTargetProductContext, char *szTargetUserSid,
1621                                DWORD *pcchTargetUserSid )
1622 {
1623     WCHAR *prodcode = NULL, *usersid = NULL, *targsid = NULL;
1624     WCHAR patch[GUID_SIZE], targprod[GUID_SIZE];
1625     DWORD len;
1626     UINT r;
1627 
1628     TRACE( "%s, %s, %#lx, %lu, %lu, %p, %p, %p, %p, %p\n", debugstr_a(szProductCode), debugstr_a(szUserSid),
1629            dwContext, dwFilter, dwIndex, szPatchCode, szTargetProductCode, pdwTargetProductContext, szTargetUserSid,
1630            pcchTargetUserSid );
1631 
1632     if (szTargetUserSid && !pcchTargetUserSid)
1633         return ERROR_INVALID_PARAMETER;
1634 
1635     if (szProductCode) prodcode = strdupAtoW(szProductCode);
1636     if (szUserSid) usersid = strdupAtoW(szUserSid);
1637 
1638     r = MsiEnumPatchesExW(prodcode, usersid, dwContext, dwFilter, dwIndex,
1639                           patch, targprod, pdwTargetProductContext,
1640                           NULL, &len);
1641     if (r != ERROR_SUCCESS)
1642         goto done;
1643 
1644     WideCharToMultiByte(CP_ACP, 0, patch, -1, szPatchCode,
1645                         GUID_SIZE, NULL, NULL);
1646     WideCharToMultiByte(CP_ACP, 0, targprod, -1, szTargetProductCode,
1647                         GUID_SIZE, NULL, NULL);
1648 
1649     if (!szTargetUserSid)
1650     {
1651         if (pcchTargetUserSid)
1652             *pcchTargetUserSid = len;
1653 
1654         goto done;
1655     }
1656 
1657     targsid = msi_alloc(++len * sizeof(WCHAR));
1658     if (!targsid)
1659     {
1660         r = ERROR_OUTOFMEMORY;
1661         goto done;
1662     }
1663 
1664     r = MsiEnumPatchesExW(prodcode, usersid, dwContext, dwFilter, dwIndex,
1665                           patch, targprod, pdwTargetProductContext,
1666                           targsid, &len);
1667     if (r != ERROR_SUCCESS || !szTargetUserSid)
1668         goto done;
1669 
1670     WideCharToMultiByte(CP_ACP, 0, targsid, -1, szTargetUserSid,
1671                         *pcchTargetUserSid, NULL, NULL);
1672 
1673     len = lstrlenW(targsid);
1674     if (*pcchTargetUserSid < len + 1)
1675     {
1676         r = ERROR_MORE_DATA;
1677         *pcchTargetUserSid = len * sizeof(WCHAR);
1678     }
1679     else
1680         *pcchTargetUserSid = len;
1681 
1682 done:
1683     msi_free(prodcode);
1684     msi_free(usersid);
1685     msi_free(targsid);
1686 
1687     return r;
1688 }
1689 
1690 static UINT msi_get_patch_state(LPCWSTR prodcode, LPCWSTR usersid,
1691                                 MSIINSTALLCONTEXT context,
1692                                 LPWSTR patch, MSIPATCHSTATE *state)
1693 {
1694     DWORD type, val, size;
1695     HKEY prod, hkey = 0;
1696     HKEY udpatch = 0;
1697     LONG res;
1698     UINT r = ERROR_NO_MORE_ITEMS;
1699 
1700     *state = MSIPATCHSTATE_INVALID;
1701 
1702     r = MSIREG_OpenUserDataProductKey(prodcode, context,
1703                                       usersid, &prod, FALSE);
1704     if (r != ERROR_SUCCESS)
1705         return ERROR_NO_MORE_ITEMS;
1706 
1707     res = RegOpenKeyExW(prod, L"Patches", 0, KEY_READ, &hkey);
1708     if (res != ERROR_SUCCESS)
1709         goto done;
1710 
1711     res = RegOpenKeyExW(hkey, patch, 0, KEY_READ, &udpatch);
1712     if (res != ERROR_SUCCESS)
1713         goto done;
1714 
1715     size = sizeof(DWORD);
1716     res = RegGetValueW(udpatch, NULL, L"State", RRF_RT_DWORD, &type, &val, &size);
1717     if (res != ERROR_SUCCESS ||
1718         val < MSIPATCHSTATE_APPLIED || val > MSIPATCHSTATE_REGISTERED)
1719     {
1720         r = ERROR_BAD_CONFIGURATION;
1721         goto done;
1722     }
1723 
1724     *state = val;
1725     r = ERROR_SUCCESS;
1726 
1727 done:
1728     RegCloseKey(udpatch);
1729     RegCloseKey(hkey);
1730     RegCloseKey(prod);
1731 
1732     return r;
1733 }
1734 
1735 static UINT msi_check_product_patches(LPCWSTR prodcode, LPCWSTR usersid,
1736         MSIINSTALLCONTEXT context, DWORD filter, DWORD index, DWORD *idx,
1737         LPWSTR patch, LPWSTR targetprod, MSIINSTALLCONTEXT *targetctx,
1738         LPWSTR targetsid, DWORD *sidsize, LPWSTR *transforms)
1739 {
1740     MSIPATCHSTATE state = MSIPATCHSTATE_INVALID;
1741     LPWSTR ptr, patches = NULL;
1742     HKEY prod, patchkey = 0;
1743     HKEY localprod = 0, localpatch = 0;
1744     DWORD type, size;
1745     LONG res;
1746     UINT temp, r = ERROR_NO_MORE_ITEMS;
1747 
1748     if (MSIREG_OpenProductKey(prodcode, usersid, context,
1749                               &prod, FALSE) != ERROR_SUCCESS)
1750         return ERROR_NO_MORE_ITEMS;
1751 
1752     size = 0;
1753     res = RegGetValueW(prod, L"Patches", L"Patches", RRF_RT_ANY, &type, NULL,
1754                        &size);
1755     if (res != ERROR_SUCCESS)
1756         goto done;
1757 
1758     if (type != REG_MULTI_SZ)
1759     {
1760         r = ERROR_BAD_CONFIGURATION;
1761         goto done;
1762     }
1763 
1764     patches = msi_alloc(size);
1765     if (!patches)
1766     {
1767         r = ERROR_OUTOFMEMORY;
1768         goto done;
1769     }
1770 
1771     res = RegGetValueW(prod, L"Patches", L"Patches", RRF_RT_ANY, &type,
1772                        patches, &size);
1773     if (res != ERROR_SUCCESS)
1774         goto done;
1775 
1776     for (ptr = patches; *ptr && r == ERROR_NO_MORE_ITEMS; ptr += lstrlenW(ptr) + 1)
1777     {
1778         if (!unsquash_guid(ptr, patch))
1779         {
1780             r = ERROR_BAD_CONFIGURATION;
1781             goto done;
1782         }
1783 
1784         size = 0;
1785         res = RegGetValueW(prod, L"Patches", ptr, RRF_RT_REG_SZ,
1786                            &type, NULL, &size);
1787         if (res != ERROR_SUCCESS)
1788             continue;
1789 
1790         if (transforms)
1791         {
1792             *transforms = msi_alloc(size);
1793             if (!*transforms)
1794             {
1795                 r = ERROR_OUTOFMEMORY;
1796                 goto done;
1797             }
1798 
1799             res = RegGetValueW(prod, L"Patches", ptr, RRF_RT_REG_SZ,
1800                                &type, *transforms, &size);
1801             if (res != ERROR_SUCCESS)
1802                 continue;
1803         }
1804 
1805         if (context == MSIINSTALLCONTEXT_USERMANAGED)
1806         {
1807             if (!(filter & MSIPATCHSTATE_APPLIED))
1808             {
1809                 temp = msi_get_patch_state(prodcode, usersid, context,
1810                                            ptr, &state);
1811                 if (temp == ERROR_BAD_CONFIGURATION)
1812                 {
1813                     r = ERROR_BAD_CONFIGURATION;
1814                     goto done;
1815                 }
1816 
1817                 if (temp != ERROR_SUCCESS || !(filter & state))
1818                     continue;
1819             }
1820         }
1821         else if (context == MSIINSTALLCONTEXT_USERUNMANAGED)
1822         {
1823             if (!(filter & MSIPATCHSTATE_APPLIED))
1824             {
1825                 temp = msi_get_patch_state(prodcode, usersid, context,
1826                                            ptr, &state);
1827                 if (temp == ERROR_BAD_CONFIGURATION)
1828                 {
1829                     r = ERROR_BAD_CONFIGURATION;
1830                     goto done;
1831                 }
1832 
1833                 if (temp != ERROR_SUCCESS || !(filter & state))
1834                     continue;
1835             }
1836             else
1837             {
1838                 temp = MSIREG_OpenUserDataPatchKey(patch, context,
1839                                                    &patchkey, FALSE);
1840                 RegCloseKey(patchkey);
1841                 if (temp != ERROR_SUCCESS)
1842                     continue;
1843             }
1844         }
1845         else if (context == MSIINSTALLCONTEXT_MACHINE)
1846         {
1847             usersid = L"";
1848 
1849             if (MSIREG_OpenUserDataProductKey(prodcode, context, NULL, &localprod, FALSE) == ERROR_SUCCESS &&
1850                 RegOpenKeyExW(localprod, L"Patches", 0, KEY_READ, &localpatch) == ERROR_SUCCESS &&
1851                 RegOpenKeyExW(localpatch, ptr, 0, KEY_READ, &patchkey) == ERROR_SUCCESS)
1852             {
1853                 res = RegGetValueW(patchkey, NULL, L"State", RRF_RT_REG_DWORD,
1854                                    &type, &state, &size);
1855 
1856                 if (!(filter & state))
1857                     res = ERROR_NO_MORE_ITEMS;
1858 
1859                 RegCloseKey(patchkey);
1860             }
1861 
1862             RegCloseKey(localpatch);
1863             RegCloseKey(localprod);
1864 
1865             if (res != ERROR_SUCCESS)
1866                 continue;
1867         }
1868 
1869         if (*idx < index)
1870         {
1871             (*idx)++;
1872             continue;
1873         }
1874 
1875         r = ERROR_SUCCESS;
1876         if (targetprod)
1877             lstrcpyW(targetprod, prodcode);
1878 
1879         if (targetctx)
1880             *targetctx = context;
1881 
1882         if (targetsid)
1883         {
1884             lstrcpynW(targetsid, usersid, *sidsize);
1885             if (lstrlenW(usersid) >= *sidsize)
1886                 r = ERROR_MORE_DATA;
1887         }
1888 
1889         if (sidsize)
1890         {
1891             *sidsize = lstrlenW(usersid);
1892             if (!targetsid)
1893                 *sidsize *= sizeof(WCHAR);
1894         }
1895     }
1896 
1897 done:
1898     RegCloseKey(prod);
1899     msi_free(patches);
1900 
1901     return r;
1902 }
1903 
1904 static UINT msi_enum_patches(LPCWSTR szProductCode, LPCWSTR szUserSid,
1905         DWORD dwContext, DWORD dwFilter, DWORD dwIndex, DWORD *idx,
1906         LPWSTR szPatchCode, LPWSTR szTargetProductCode,
1907         MSIINSTALLCONTEXT *pdwTargetProductContext, LPWSTR szTargetUserSid,
1908         LPDWORD pcchTargetUserSid, LPWSTR *szTransforms)
1909 {
1910     LPWSTR usersid = NULL;
1911     UINT r = ERROR_INVALID_PARAMETER;
1912 
1913     if (!szUserSid)
1914     {
1915         szUserSid = usersid = get_user_sid();
1916         if (!usersid) return ERROR_FUNCTION_FAILED;
1917     }
1918 
1919     if (dwContext & MSIINSTALLCONTEXT_USERMANAGED)
1920     {
1921         r = msi_check_product_patches(szProductCode, szUserSid,
1922                                       MSIINSTALLCONTEXT_USERMANAGED, dwFilter,
1923                                       dwIndex, idx, szPatchCode,
1924                                       szTargetProductCode,
1925                                       pdwTargetProductContext, szTargetUserSid,
1926                                       pcchTargetUserSid, szTransforms);
1927         if (r != ERROR_NO_MORE_ITEMS)
1928             goto done;
1929     }
1930 
1931     if (dwContext & MSIINSTALLCONTEXT_USERUNMANAGED)
1932     {
1933         r = msi_check_product_patches(szProductCode, szUserSid,
1934                                       MSIINSTALLCONTEXT_USERUNMANAGED, dwFilter,
1935                                       dwIndex, idx, szPatchCode,
1936                                       szTargetProductCode,
1937                                       pdwTargetProductContext, szTargetUserSid,
1938                                       pcchTargetUserSid, szTransforms);
1939         if (r != ERROR_NO_MORE_ITEMS)
1940             goto done;
1941     }
1942 
1943     if (dwContext & MSIINSTALLCONTEXT_MACHINE)
1944     {
1945         r = msi_check_product_patches(szProductCode, szUserSid,
1946                                       MSIINSTALLCONTEXT_MACHINE, dwFilter,
1947                                       dwIndex, idx, szPatchCode,
1948                                       szTargetProductCode,
1949                                       pdwTargetProductContext, szTargetUserSid,
1950                                       pcchTargetUserSid, szTransforms);
1951         if (r != ERROR_NO_MORE_ITEMS)
1952             goto done;
1953     }
1954 
1955 done:
1956     LocalFree(usersid);
1957     return r;
1958 }
1959 
1960 /***********************************************************************
1961  * MsiEnumPatchesExW            [MSI.@]
1962  */
1963 UINT WINAPI MsiEnumPatchesExW( const WCHAR *szProductCode, const WCHAR *szUserSid, DWORD dwContext, DWORD dwFilter,
1964                                DWORD dwIndex, WCHAR *szPatchCode, WCHAR *szTargetProductCode,
1965                                MSIINSTALLCONTEXT *pdwTargetProductContext, WCHAR *szTargetUserSid,
1966                                DWORD *pcchTargetUserSid )
1967 {
1968     WCHAR squashed_pc[SQUASHED_GUID_SIZE];
1969     DWORD idx = 0;
1970     UINT r;
1971 
1972     static DWORD last_index;
1973 
1974     TRACE( "%s, %s, %#lx, %lu, %lu, %p, %p, %p, %p, %p)\n", debugstr_w(szProductCode), debugstr_w(szUserSid),
1975            dwContext, dwFilter, dwIndex, szPatchCode, szTargetProductCode, pdwTargetProductContext, szTargetUserSid,
1976            pcchTargetUserSid );
1977 
1978     if (!szProductCode || !squash_guid( szProductCode, squashed_pc ))
1979         return ERROR_INVALID_PARAMETER;
1980 
1981     if (szUserSid && !wcscmp( szUserSid, L"S-1-5-18" ))
1982         return ERROR_INVALID_PARAMETER;
1983 
1984     if (dwContext & MSIINSTALLCONTEXT_MACHINE && szUserSid)
1985         return ERROR_INVALID_PARAMETER;
1986 
1987     if (dwContext <= MSIINSTALLCONTEXT_NONE ||
1988         dwContext > MSIINSTALLCONTEXT_ALL)
1989         return ERROR_INVALID_PARAMETER;
1990 
1991     if (dwFilter <= MSIPATCHSTATE_INVALID || dwFilter > MSIPATCHSTATE_ALL)
1992         return ERROR_INVALID_PARAMETER;
1993 
1994     if (dwIndex && dwIndex - last_index != 1)
1995         return ERROR_INVALID_PARAMETER;
1996 
1997     if (dwIndex == 0)
1998         last_index = 0;
1999 
2000     r = msi_enum_patches(szProductCode, szUserSid, dwContext, dwFilter,
2001                          dwIndex, &idx, szPatchCode, szTargetProductCode,
2002                          pdwTargetProductContext, szTargetUserSid,
2003                          pcchTargetUserSid, NULL);
2004 
2005     if (r == ERROR_SUCCESS)
2006         last_index = dwIndex;
2007     else
2008         last_index = 0;
2009 
2010     return r;
2011 }
2012 
2013 /***********************************************************************
2014  * MsiEnumPatchesA            [MSI.@]
2015  */
2016 UINT WINAPI MsiEnumPatchesA( const char *szProduct, DWORD iPatchIndex, char *lpPatchBuf, char *lpTransformsBuf,
2017                              DWORD *pcchTransformsBuf )
2018 {
2019     WCHAR *product, *transforms, patch[GUID_SIZE];
2020     DWORD len;
2021     UINT r;
2022 
2023     TRACE( "%s, %lu, %p, %p, %p\n", debugstr_a(szProduct), iPatchIndex, lpPatchBuf, lpTransformsBuf,
2024            pcchTransformsBuf );
2025 
2026     if (!szProduct || !lpPatchBuf || !lpTransformsBuf || !pcchTransformsBuf)
2027         return ERROR_INVALID_PARAMETER;
2028 
2029     product = strdupAtoW(szProduct);
2030     if (!product)
2031         return ERROR_OUTOFMEMORY;
2032 
2033     len = *pcchTransformsBuf;
2034     transforms = msi_alloc( len * sizeof(WCHAR) );
2035     if (!transforms)
2036     {
2037         r = ERROR_OUTOFMEMORY;
2038         goto done;
2039     }
2040 
2041     r = MsiEnumPatchesW(product, iPatchIndex, patch, transforms, &len);
2042     if (r != ERROR_SUCCESS && r != ERROR_MORE_DATA)
2043         goto done;
2044 
2045     WideCharToMultiByte(CP_ACP, 0, patch, -1, lpPatchBuf,
2046                         GUID_SIZE, NULL, NULL);
2047 
2048     if (!WideCharToMultiByte(CP_ACP, 0, transforms, -1, lpTransformsBuf,
2049                              *pcchTransformsBuf, NULL, NULL))
2050         r = ERROR_MORE_DATA;
2051 
2052     if (r == ERROR_MORE_DATA)
2053     {
2054         lpTransformsBuf[*pcchTransformsBuf - 1] = '\0';
2055         *pcchTransformsBuf = len * 2;
2056     }
2057     else
2058         *pcchTransformsBuf = strlen( lpTransformsBuf );
2059 
2060 done:
2061     msi_free(transforms);
2062     msi_free(product);
2063 
2064     return r;
2065 }
2066 
2067 /***********************************************************************
2068  * MsiEnumPatchesW            [MSI.@]
2069  */
2070 UINT WINAPI MsiEnumPatchesW( const WCHAR *szProduct, DWORD iPatchIndex, WCHAR *lpPatchBuf, WCHAR *lpTransformsBuf,
2071                              DWORD *pcchTransformsBuf )
2072 {
2073     WCHAR *transforms = NULL, squashed_pc[SQUASHED_GUID_SIZE];
2074     HKEY prod;
2075     DWORD idx = 0;
2076     UINT r;
2077 
2078     TRACE( "%s, %lu, %p, %p, %p)\n", debugstr_w(szProduct), iPatchIndex, lpPatchBuf, lpTransformsBuf,
2079            pcchTransformsBuf );
2080 
2081     if (!szProduct || !squash_guid( szProduct, squashed_pc ))
2082         return ERROR_INVALID_PARAMETER;
2083 
2084     if (!lpPatchBuf || !lpTransformsBuf || !pcchTransformsBuf)
2085         return ERROR_INVALID_PARAMETER;
2086 
2087     if (MSIREG_OpenProductKey(szProduct, NULL, MSIINSTALLCONTEXT_USERMANAGED,
2088                               &prod, FALSE) != ERROR_SUCCESS &&
2089         MSIREG_OpenProductKey(szProduct, NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
2090                               &prod, FALSE) != ERROR_SUCCESS &&
2091         MSIREG_OpenProductKey(szProduct, NULL, MSIINSTALLCONTEXT_MACHINE,
2092                               &prod, FALSE) != ERROR_SUCCESS)
2093         return ERROR_UNKNOWN_PRODUCT;
2094 
2095     RegCloseKey(prod);
2096 
2097     r = msi_enum_patches(szProduct, NULL, MSIINSTALLCONTEXT_ALL,
2098                          MSIPATCHSTATE_ALL, iPatchIndex, &idx, lpPatchBuf,
2099                          NULL, NULL, NULL, NULL, &transforms);
2100     if (r != ERROR_SUCCESS)
2101         goto done;
2102 
2103     lstrcpynW(lpTransformsBuf, transforms, *pcchTransformsBuf);
2104     if (*pcchTransformsBuf <= lstrlenW(transforms))
2105     {
2106         r = ERROR_MORE_DATA;
2107         *pcchTransformsBuf = lstrlenW(transforms);
2108     }
2109     else
2110         *pcchTransformsBuf = lstrlenW(transforms);
2111 
2112 done:
2113     msi_free(transforms);
2114     return r;
2115 }
2116 
2117 UINT WINAPI MsiEnumProductsExA( const char *product, const char *usersid, DWORD ctx, DWORD index,
2118                                 char installed_product[GUID_SIZE], MSIINSTALLCONTEXT *installed_ctx, char *sid,
2119                                 DWORD *sid_len )
2120 {
2121     UINT r;
2122     WCHAR installed_productW[GUID_SIZE], *productW = NULL, *usersidW = NULL, *sidW = NULL;
2123 
2124     TRACE( "%s, %s, %#lx, %lu, %p, %p, %p, %p\n", debugstr_a(product), debugstr_a(usersid), ctx, index,
2125            installed_product, installed_ctx, sid, sid_len );
2126 
2127     if (sid && !sid_len) return ERROR_INVALID_PARAMETER;
2128     if (product && !(productW = strdupAtoW( product ))) return ERROR_OUTOFMEMORY;
2129     if (usersid && !(usersidW = strdupAtoW( usersid )))
2130     {
2131         msi_free( productW );
2132         return ERROR_OUTOFMEMORY;
2133     }
2134     if (sid && !(sidW = msi_alloc( *sid_len * sizeof(WCHAR) )))
2135     {
2136         msi_free( usersidW );
2137         msi_free( productW );
2138         return ERROR_OUTOFMEMORY;
2139     }
2140     r = MsiEnumProductsExW( productW, usersidW, ctx, index, installed_productW,
2141                             installed_ctx, sidW, sid_len );
2142     if (r == ERROR_SUCCESS)
2143     {
2144         if (installed_product) WideCharToMultiByte( CP_ACP, 0, installed_productW, GUID_SIZE,
2145                                                     installed_product, GUID_SIZE, NULL, NULL );
2146         if (sid) WideCharToMultiByte( CP_ACP, 0, sidW, *sid_len + 1, sid, *sid_len + 1, NULL, NULL );
2147     }
2148     msi_free( productW );
2149     msi_free( usersidW );
2150     msi_free( sidW );
2151     return r;
2152 }
2153 
2154 static UINT fetch_machine_product( const WCHAR *match, DWORD index, DWORD *idx,
2155                                    WCHAR installed_product[GUID_SIZE],
2156                                    MSIINSTALLCONTEXT *installed_ctx, WCHAR *sid, DWORD *sid_len )
2157 {
2158     UINT r;
2159     WCHAR product[SQUASHED_GUID_SIZE];
2160     DWORD i = 0, len;
2161     REGSAM access = KEY_ENUMERATE_SUB_KEYS | KEY_WOW64_64KEY;
2162     HKEY key;
2163 
2164     if (RegOpenKeyExW( HKEY_LOCAL_MACHINE, L"Software\\Classes\\Installer\\Products", 0, access, &key ))
2165         return ERROR_NO_MORE_ITEMS;
2166 
2167     len = ARRAY_SIZE( product );
2168     while (!RegEnumKeyExW( key, i, product, &len, NULL, NULL, NULL, NULL ))
2169     {
2170         if (match && wcscmp( match, product ))
2171         {
2172             i++;
2173             len = ARRAY_SIZE( product );
2174             continue;
2175         }
2176         if (*idx == index) goto found;
2177         (*idx)++;
2178         len = ARRAY_SIZE( product );
2179         i++;
2180     }
2181     RegCloseKey( key );
2182     return ERROR_NO_MORE_ITEMS;
2183 
2184 found:
2185     if (sid_len && *sid_len < 1)
2186     {
2187         *sid_len = 1;
2188         r = ERROR_MORE_DATA;
2189     }
2190     else
2191     {
2192         if (installed_product) unsquash_guid( product, installed_product );
2193         if (installed_ctx) *installed_ctx = MSIINSTALLCONTEXT_MACHINE;
2194         if (sid)
2195         {
2196             sid[0] = 0;
2197             *sid_len = 0;
2198         }
2199         r = ERROR_SUCCESS;
2200     }
2201     RegCloseKey( key );
2202     return r;
2203 }
2204 
2205 static UINT fetch_user_product( const WCHAR *match, const WCHAR *usersid, DWORD ctx, DWORD index,
2206                                 DWORD *idx, WCHAR installed_product[GUID_SIZE],
2207                                 MSIINSTALLCONTEXT *installed_ctx, WCHAR *sid, DWORD *sid_len )
2208 {
2209     UINT r;
2210     const WCHAR *subkey;
2211     WCHAR path[MAX_PATH], product[SQUASHED_GUID_SIZE], user[128];
2212     DWORD i = 0, j = 0, len_product, len_user;
2213     REGSAM access = KEY_ENUMERATE_SUB_KEYS | KEY_WOW64_64KEY;
2214     HKEY key_users, key_products;
2215 
2216     if (ctx == MSIINSTALLCONTEXT_USERMANAGED)
2217     {
2218         subkey = L"\\Installer\\Products";
2219         if (RegOpenKeyExW( HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\Managed",
2220                            0, access, &key_users )) return ERROR_NO_MORE_ITEMS;
2221     }
2222     else if (ctx == MSIINSTALLCONTEXT_USERUNMANAGED)
2223     {
2224         subkey = L"\\Software\\Microsoft\\Installer\\Products";
2225         if (RegOpenKeyExW( HKEY_USERS, NULL, 0, access, &key_users ))
2226             return ERROR_NO_MORE_ITEMS;
2227     }
2228     else return ERROR_INVALID_PARAMETER;
2229 
2230     len_user = ARRAY_SIZE( user );
2231     while (!RegEnumKeyExW( key_users, i, user, &len_user, NULL, NULL, NULL, NULL ))
2232     {
2233         if (wcscmp( usersid, user ) && wcscmp( usersid, L"S-1-1-0" ))
2234         {
2235             i++;
2236             len_user = ARRAY_SIZE( user );
2237             continue;
2238         }
2239         lstrcpyW( path, user );
2240         lstrcatW( path, subkey );
2241         if (RegOpenKeyExW( key_users, path, 0, access, &key_products ))
2242         {
2243             i++;
2244             len_user = ARRAY_SIZE( user );
2245             continue;
2246         }
2247         len_product = ARRAY_SIZE( product );
2248         while (!RegEnumKeyExW( key_products, j, product, &len_product, NULL, NULL, NULL, NULL ))
2249         {
2250             if (match && wcscmp( match, product ))
2251             {
2252                 j++;
2253                 len_product = ARRAY_SIZE( product );
2254                 continue;
2255             }
2256             if (*idx == index) goto found;
2257             (*idx)++;
2258             len_product = ARRAY_SIZE( product );
2259             j++;
2260         }
2261         RegCloseKey( key_products );
2262         len_user = ARRAY_SIZE( user );
2263         i++;
2264     }
2265     RegCloseKey( key_users );
2266     return ERROR_NO_MORE_ITEMS;
2267 
2268 found:
2269     if (sid_len && *sid_len <= len_user)
2270     {
2271         *sid_len = len_user;
2272         r = ERROR_MORE_DATA;
2273     }
2274     else
2275     {
2276         if (installed_product) unsquash_guid( product, installed_product );
2277         if (installed_ctx) *installed_ctx = ctx;
2278         if (sid)
2279         {
2280             lstrcpyW( sid, user );
2281             *sid_len = len_user;
2282         }
2283         r = ERROR_SUCCESS;
2284     }
2285     RegCloseKey( key_products );
2286     RegCloseKey( key_users );
2287     return r;
2288 }
2289 
2290 static UINT enum_products( const WCHAR *product, const WCHAR *usersid, DWORD ctx, DWORD index,
2291                            DWORD *idx, WCHAR installed_product[GUID_SIZE],
2292                            MSIINSTALLCONTEXT *installed_ctx, WCHAR *sid, DWORD *sid_len )
2293 {
2294     UINT r = ERROR_NO_MORE_ITEMS;
2295     WCHAR *user = NULL;
2296 
2297     if (!usersid)
2298     {
2299         usersid = user = get_user_sid();
2300         if (!user) return ERROR_FUNCTION_FAILED;
2301     }
2302     if (ctx & MSIINSTALLCONTEXT_MACHINE)
2303     {
2304         r = fetch_machine_product( product, index, idx, installed_product, installed_ctx,
2305                                    sid, sid_len );
2306         if (r != ERROR_NO_MORE_ITEMS) goto done;
2307     }
2308     if (ctx & MSIINSTALLCONTEXT_USERUNMANAGED)
2309     {
2310         r = fetch_user_product( product, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, index,
2311                                 idx, installed_product, installed_ctx, sid, sid_len );
2312         if (r != ERROR_NO_MORE_ITEMS) goto done;
2313     }
2314     if (ctx & MSIINSTALLCONTEXT_USERMANAGED)
2315     {
2316         r = fetch_user_product( product, usersid, MSIINSTALLCONTEXT_USERMANAGED, index,
2317                                 idx, installed_product, installed_ctx, sid, sid_len );
2318         if (r != ERROR_NO_MORE_ITEMS) goto done;
2319     }
2320 
2321 done:
2322     LocalFree( user );
2323     return r;
2324 }
2325 
2326 UINT WINAPI MsiEnumProductsExW( const WCHAR *product, const WCHAR *usersid, DWORD ctx, DWORD index,
2327                                 WCHAR installed_product[GUID_SIZE], MSIINSTALLCONTEXT *installed_ctx, WCHAR *sid,
2328                                 DWORD *sid_len )
2329 {
2330     UINT r;
2331     DWORD idx = 0;
2332     static DWORD last_index;
2333 
2334     TRACE( "%s, %s, %#lx, %lu, %p, %p, %p, %p\n", debugstr_w(product), debugstr_w(usersid), ctx, index,
2335            installed_product, installed_ctx, sid, sid_len );
2336 
2337     if ((sid && !sid_len) || !ctx || (usersid && ctx == MSIINSTALLCONTEXT_MACHINE))
2338         return ERROR_INVALID_PARAMETER;
2339 
2340     if (index && index - last_index != 1)
2341         return ERROR_INVALID_PARAMETER;
2342 
2343     if (!index) last_index = 0;
2344 
2345     r = enum_products( product, usersid, ctx, index, &idx, installed_product, installed_ctx,
2346                        sid, sid_len );
2347     if (r == ERROR_SUCCESS)
2348         last_index = index;
2349     else
2350         last_index = 0;
2351 
2352     return r;
2353 }
2354