gdi32: Allow a rounding error in the text justification test.
[wine] / dlls / msi / registry.c
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 #define NONAMELESSUNION
26
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winreg.h"
30 #include "winnls.h"
31 #include "shlwapi.h"
32 #include "wine/debug.h"
33 #include "msi.h"
34 #include "msipriv.h"
35 #include "wincrypt.h"
36 #include "wine/unicode.h"
37 #include "winver.h"
38 #include "winuser.h"
39 #include "sddl.h"
40
41 WINE_DEFAULT_DEBUG_CHANNEL(msi);
42
43
44 /* 
45  * This module will be all the helper functions for registry access by the
46  * installer bits. 
47  */
48 static const WCHAR szUserFeatures_fmt[] = {
49 'S','o','f','t','w','a','r','e','\\',
50 'M','i','c','r','o','s','o','f','t','\\',
51 'I','n','s','t','a','l','l','e','r','\\',
52 'F','e','a','t','u','r','e','s','\\',
53 '%','s',0};
54
55 static const WCHAR szUserDataFeatures_fmt[] = {
56 'S','o','f','t','w','a','r','e','\\',
57 'M','i','c','r','o','s','o','f','t','\\',
58 'W','i','n','d','o','w','s','\\',
59 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
60 'I','n','s','t','a','l','l','e','r','\\',
61 'U','s','e','r','D','a','t','a','\\',
62 '%','s','\\','P','r','o','d','u','c','t','s','\\',
63 '%','s','\\','F','e','a','t','u','r','e','s',0};
64
65 static const WCHAR szInstaller_Features_fmt[] = {
66 'S','o','f','t','w','a','r','e','\\',
67 'M','i','c','r','o','s','o','f','t','\\',
68 'W','i','n','d','o','w','s','\\',
69 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
70 'I','n','s','t','a','l','l','e','r','\\',
71 'F','e','a','t','u','r','e','s','\\',
72 '%','s',0};
73
74 static const WCHAR szInstaller_Components[] = {
75 'S','o','f','t','w','a','r','e','\\',
76 'M','i','c','r','o','s','o','f','t','\\',
77 'W','i','n','d','o','w','s','\\',
78 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
79 'I','n','s','t','a','l','l','e','r','\\',
80 'C','o','m','p','o','n','e','n','t','s',0 };
81
82 static const WCHAR szUser_Components_fmt[] = {
83 'S','o','f','t','w','a','r','e','\\',
84 'M','i','c','r','o','s','o','f','t','\\',
85 'I','n','s','t','a','l','l','e','r','\\',
86 'C','o','m','p','o','n','e','n','t','s','\\',
87 '%','s',0};
88
89 static const WCHAR szUserDataComp_fmt[] = {
90 'S','o','f','t','w','a','r','e','\\',
91 'M','i','c','r','o','s','o','f','t','\\',
92 'W','i','n','d','o','w','s','\\',
93 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
94 'I','n','s','t','a','l','l','e','r','\\',
95 'U','s','e','r','D','a','t','a','\\',
96 '%','s','\\','C','o','m','p','o','n','e','n','t','s','\\','%','s',0};
97
98 static const WCHAR szUninstall_fmt[] = {
99 'S','o','f','t','w','a','r','e','\\',
100 'M','i','c','r','o','s','o','f','t','\\',
101 'W','i','n','d','o','w','s','\\',
102 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
103 'U','n','i','n','s','t','a','l','l','\\',
104 '%','s',0 };
105
106 static const WCHAR szUserProduct_fmt[] = {
107 'S','o','f','t','w','a','r','e','\\',
108 'M','i','c','r','o','s','o','f','t','\\',
109 'I','n','s','t','a','l','l','e','r','\\',
110 'P','r','o','d','u','c','t','s','\\',
111 '%','s',0};
112
113 static const WCHAR szUserPatch_fmt[] = {
114 'S','o','f','t','w','a','r','e','\\',
115 'M','i','c','r','o','s','o','f','t','\\',
116 'I','n','s','t','a','l','l','e','r','\\',
117 'P','a','t','c','h','e','s','\\',
118 '%','s',0};
119
120 static const WCHAR szInstaller_Products[] = {
121 'S','o','f','t','w','a','r','e','\\',
122 'M','i','c','r','o','s','o','f','t','\\',
123 'W','i','n','d','o','w','s','\\',
124 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
125 'I','n','s','t','a','l','l','e','r','\\',
126 'P','r','o','d','u','c','t','s',0};
127
128 static const WCHAR szInstaller_Products_fmt[] = {
129 'S','o','f','t','w','a','r','e','\\',
130 'M','i','c','r','o','s','o','f','t','\\',
131 'W','i','n','d','o','w','s','\\',
132 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
133 'I','n','s','t','a','l','l','e','r','\\',
134 'P','r','o','d','u','c','t','s','\\',
135 '%','s',0};
136
137 static const WCHAR szInstaller_Patches_fmt[] = {
138 'S','o','f','t','w','a','r','e','\\',
139 'M','i','c','r','o','s','o','f','t','\\',
140 'W','i','n','d','o','w','s','\\',
141 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
142 'I','n','s','t','a','l','l','e','r','\\',
143 'P','a','t','c','h','e','s','\\',
144 '%','s',0};
145
146 static const WCHAR szInstaller_UpgradeCodes_fmt[] = {
147 'S','o','f','t','w','a','r','e','\\',
148 'M','i','c','r','o','s','o','f','t','\\',
149 'W','i','n','d','o','w','s','\\',
150 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
151 'I','n','s','t','a','l','l','e','r','\\',
152 'U','p','g','r','a','d','e','C','o','d','e','s','\\',
153 '%','s',0};
154
155 static const WCHAR szInstaller_UserUpgradeCodes_fmt[] = {
156 'S','o','f','t','w','a','r','e','\\',
157 'M','i','c','r','o','s','o','f','t','\\',
158 'I','n','s','t','a','l','l','e','r','\\',
159 'U','p','g','r','a','d','e','C','o','d','e','s','\\',
160 '%','s',0};
161
162 static const WCHAR szUserDataProd_fmt[] = {
163 'S','o','f','t','w','a','r','e','\\',
164 'M','i','c','r','o','s','o','f','t','\\',
165 'W','i','n','d','o','w','s','\\',
166 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
167 'I','n','s','t','a','l','l','e','r','\\',
168 'U','s','e','r','D','a','t','a','\\',
169 '%','s','\\','P','r','o','d','u','c','t','s','\\','%','s',0};
170
171 static const WCHAR szInstallProperties_fmt[] = {
172 'S','o','f','t','w','a','r','e','\\',
173 'M','i','c','r','o','s','o','f','t','\\',
174 'W','i','n','d','o','w','s','\\',
175 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
176 'I','n','s','t','a','l','l','e','r','\\',
177 'U','s','e','r','D','a','t','a','\\',
178 '%','s','\\','P','r','o','d','u','c','t','s','\\','%','s','\\',
179 'I','n','s','t','a','l','l','P','r','o','p','e','r','t','i','e','s',0};
180
181 static const WCHAR szInstaller_LocalSystemProductCodes_fmt[] = {
182 'S','o','f','t','w','a','r','e','\\',
183 'M','i','c','r','o','s','o','f','t','\\',
184 'W','i','n','d','o','w','s','\\',
185 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
186 'I','n','s','t','a','l','l','e','r','\\',
187 'U','s','e','r','D','a','t','a','\\',
188 'S','-','1','-','5','-','1','8','\\','P','r','o','d','u','c','t','s','\\',
189 '%','s','\\','I','n','s','t','a','l','l','P','r','o','p','e','r','t','i','e','s',0};
190
191 static const WCHAR szInstaller_LocalSystemComponent_fmt[] = {
192 'S','o','f','t','w','a','r','e','\\',
193 'M','i','c','r','o','s','o','f','t','\\',
194 'W','i','n','d','o','w','s','\\',
195 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
196 'I','n','s','t','a','l','l','e','r','\\',
197 'U','s','e','r','D','a','t','a','\\',
198 'S','-','1','-','5','-','1','8','\\',
199 'C','o','m','p','o','n','e','n','t','s','\\','%','s',0};
200
201 static const WCHAR szInstaller_LocalClassesProd_fmt[] = {
202 'S','o','f','t','w','a','r','e','\\',
203 'C','l','a','s','s','e','s','\\',
204 'I','n','s','t','a','l','l','e','r','\\',
205 'P','r','o','d','u','c','t','s','\\','%','s',0};
206
207 static const WCHAR szInstaller_LocalClassesFeat_fmt[] = {
208 'S','o','f','t','w','a','r','e','\\',
209 'C','l','a','s','s','e','s','\\',
210 'I','n','s','t','a','l','l','e','r','\\',
211 'F','e','a','t','u','r','e','s','\\','%','s',0};
212
213 static const WCHAR szInstaller_LocalManagedProd_fmt[] = {
214 'S','o','f','t','w','a','r','e','\\',
215 'M','i','c','r','o','s','o','f','t','\\',
216 'W','i','n','d','o','w','s','\\',
217 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
218 'I','n','s','t','a','l','l','e','r','\\',
219 'M','a','n','a','g','e','d','\\','%','s','\\',
220 'I','n','s','t','a','l','l','e','r','\\',
221 'P','r','o','d','u','c','t','s','\\','%','s',0};
222
223 static const WCHAR szInstaller_LocalManagedFeat_fmt[] = {
224 'S','o','f','t','w','a','r','e','\\',
225 'M','i','c','r','o','s','o','f','t','\\',
226 'W','i','n','d','o','w','s','\\',
227 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
228 'I','n','s','t','a','l','l','e','r','\\',
229 'M','a','n','a','g','e','d','\\','%','s','\\',
230 'I','n','s','t','a','l','l','e','r','\\',
231 'F','e','a','t','u','r','e','s','\\','%','s',0};
232
233 static const WCHAR szInstaller_ClassesUpgrade_fmt[] = {
234 'I','n','s','t','a','l','l','e','r','\\',
235 'U','p','g','r','a','d','e','C','o','d','e','s','\\',
236 '%','s',0};
237
238 static const WCHAR localsid[] = {'S','-','1','-','5','-','1','8',0};
239
240 BOOL unsquash_guid(LPCWSTR in, LPWSTR out)
241 {
242     DWORD i,n=0;
243
244     out[n++]='{';
245     for(i=0; i<8; i++)
246         out[n++] = in[7-i];
247     out[n++]='-';
248     for(i=0; i<4; i++)
249         out[n++] = in[11-i];
250     out[n++]='-';
251     for(i=0; i<4; i++)
252         out[n++] = in[15-i];
253     out[n++]='-';
254     for(i=0; i<2; i++)
255     {
256         out[n++] = in[17+i*2];
257         out[n++] = in[16+i*2];
258     }
259     out[n++]='-';
260     for( ; i<8; i++)
261     {
262         out[n++] = in[17+i*2];
263         out[n++] = in[16+i*2];
264     }
265     out[n++]='}';
266     out[n]=0;
267     return TRUE;
268 }
269
270 BOOL squash_guid(LPCWSTR in, LPWSTR out)
271 {
272     DWORD i,n=1;
273     GUID guid;
274
275     out[0] = 0;
276
277     if (FAILED(CLSIDFromString((LPOLESTR)in, &guid)))
278         return FALSE;
279
280     for(i=0; i<8; i++)
281         out[7-i] = in[n++];
282     n++;
283     for(i=0; i<4; i++)
284         out[11-i] = in[n++];
285     n++;
286     for(i=0; i<4; i++)
287         out[15-i] = in[n++];
288     n++;
289     for(i=0; i<2; i++)
290     {
291         out[17+i*2] = in[n++];
292         out[16+i*2] = in[n++];
293     }
294     n++;
295     for( ; i<8; i++)
296     {
297         out[17+i*2] = in[n++];
298         out[16+i*2] = in[n++];
299     }
300     out[32]=0;
301     return TRUE;
302 }
303
304
305 /* tables for encoding and decoding base85 */
306 static const unsigned char table_dec85[0x80] = {
307 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
308 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
309 0xff,0x00,0xff,0xff,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0xff,
310 0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0xff,0xff,0xff,0x16,0xff,0x17,
311 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,
312 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x32,0x33,0xff,0x34,0x35,0x36,
313 0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x40,0x41,0x42,0x43,0x44,0x45,0x46,
314 0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x50,0x51,0x52,0xff,0x53,0x54,0xff,
315 };
316
317 static const char table_enc85[] =
318 "!$%&'()*+,-.0123456789=?@ABCDEFGHIJKLMNO"
319 "PQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwx"
320 "yz{}~";
321
322 /*
323  *  Converts a base85 encoded guid into a GUID pointer
324  *  Base85 encoded GUIDs should be 20 characters long.
325  *
326  *  returns TRUE if successful, FALSE if not
327  */
328 BOOL decode_base85_guid( LPCWSTR str, GUID *guid )
329 {
330     DWORD i, val = 0, base = 1, *p;
331
332     if (!str)
333         return FALSE;
334
335     p = (DWORD*) guid;
336     for( i=0; i<20; i++ )
337     {
338         if( (i%5) == 0 )
339         {
340             val = 0;
341             base = 1;
342         }
343         val += table_dec85[str[i]] * base;
344         if( str[i] >= 0x80 )
345             return FALSE;
346         if( table_dec85[str[i]] == 0xff )
347             return FALSE;
348         if( (i%5) == 4 )
349             p[i/5] = val;
350         base *= 85;
351     }
352     return TRUE;
353 }
354
355 /*
356  *  Encodes a base85 guid given a GUID pointer
357  *  Caller should provide a 21 character buffer for the encoded string.
358  *
359  *  returns TRUE if successful, FALSE if not
360  */
361 BOOL encode_base85_guid( GUID *guid, LPWSTR str )
362 {
363     unsigned int x, *p, i;
364
365     p = (unsigned int*) guid;
366     for( i=0; i<4; i++ )
367     {
368         x = p[i];
369         *str++ = table_enc85[x%85];
370         x = x/85;
371         *str++ = table_enc85[x%85];
372         x = x/85;
373         *str++ = table_enc85[x%85];
374         x = x/85;
375         *str++ = table_enc85[x%85];
376         x = x/85;
377         *str++ = table_enc85[x%85];
378     }
379     *str = 0;
380
381     return TRUE;
382 }
383
384 DWORD msi_version_str_to_dword(LPCWSTR p)
385 {
386     DWORD major, minor = 0, build = 0, version = 0;
387
388     if (!p)
389         return version;
390
391     major = atoiW(p);
392
393     p = strchrW(p, '.');
394     if (p)
395     {
396         minor = atoiW(p+1);
397         p = strchrW(p+1, '.');
398         if (p)
399             build = atoiW(p+1);
400     }
401
402     return MAKELONG(build, MAKEWORD(minor, major));
403 }
404
405 LPWSTR msi_version_dword_to_str(DWORD version)
406 {
407     static const WCHAR fmt[] = { '%','u','.','%','u','.','%','u',0 };
408     LPWSTR str = msi_alloc(20);
409     sprintfW(str, fmt,
410              (version&0xff000000)>>24,
411              (version&0x00ff0000)>>16,
412               version&0x0000ffff);
413     return str;
414 }
415
416 LONG msi_reg_set_val_str( HKEY hkey, LPCWSTR name, LPCWSTR value )
417 {
418     static const WCHAR emptyW[] = {0};
419     DWORD len;
420     if (!value) value = emptyW;
421     len = (lstrlenW(value) + 1) * sizeof (WCHAR);
422     return RegSetValueExW( hkey, name, 0, REG_SZ, (const BYTE *)value, len );
423 }
424
425 LONG msi_reg_set_val_multi_str( HKEY hkey, LPCWSTR name, LPCWSTR value )
426 {
427     LPCWSTR p = value;
428     while (*p) p += lstrlenW(p) + 1;
429     return RegSetValueExW( hkey, name, 0, REG_MULTI_SZ,
430                            (const BYTE *)value, (p + 1 - value) * sizeof(WCHAR) );
431 }
432
433 LONG msi_reg_set_val_dword( HKEY hkey, LPCWSTR name, DWORD val )
434 {
435     return RegSetValueExW( hkey, name, 0, REG_DWORD, (LPBYTE)&val, sizeof (DWORD) );
436 }
437
438 LONG msi_reg_set_subkey_val( HKEY hkey, LPCWSTR path, LPCWSTR name, LPCWSTR val )
439 {
440     HKEY hsubkey = 0;
441     LONG r;
442
443     r = RegCreateKeyW( hkey, path, &hsubkey );
444     if (r != ERROR_SUCCESS)
445         return r;
446     r = msi_reg_set_val_str( hsubkey, name, val );
447     RegCloseKey( hsubkey );
448     return r;
449 }
450
451 LPWSTR msi_reg_get_val_str( HKEY hkey, LPCWSTR name )
452 {
453     DWORD len = 0;
454     LPWSTR val;
455     LONG r;
456
457     r = RegQueryValueExW(hkey, name, NULL, NULL, NULL, &len);
458     if (r != ERROR_SUCCESS)
459         return NULL;
460
461     len += sizeof (WCHAR);
462     val = msi_alloc( len );
463     if (!val)
464         return NULL;
465     val[0] = 0;
466     RegQueryValueExW(hkey, name, NULL, NULL, (LPBYTE) val, &len);
467     return val;
468 }
469
470 BOOL msi_reg_get_val_dword( HKEY hkey, LPCWSTR name, DWORD *val)
471 {
472     DWORD type, len = sizeof (DWORD);
473     LONG r = RegQueryValueExW(hkey, name, NULL, &type, (LPBYTE) val, &len);
474     return r == ERROR_SUCCESS && type == REG_DWORD;
475 }
476
477 static UINT get_user_sid(LPWSTR *usersid)
478 {
479     HANDLE token;
480     BYTE buf[1024];
481     DWORD size;
482     PTOKEN_USER user;
483
484     if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
485         return ERROR_FUNCTION_FAILED;
486
487     size = sizeof(buf);
488     if (!GetTokenInformation(token, TokenUser, (void *)buf, size, &size)) {
489         CloseHandle(token);
490         return ERROR_FUNCTION_FAILED;
491     }
492
493     user = (PTOKEN_USER)buf;
494     if (!ConvertSidToStringSidW(user->User.Sid, usersid)) {
495         CloseHandle(token);
496         return ERROR_FUNCTION_FAILED;
497     }
498     CloseHandle(token);
499     return ERROR_SUCCESS;
500 }
501
502 UINT MSIREG_OpenUninstallKey(LPCWSTR szProduct, HKEY* key, BOOL create)
503 {
504     UINT rc;
505     WCHAR keypath[0x200];
506     TRACE("%s\n",debugstr_w(szProduct));
507
508     sprintfW(keypath,szUninstall_fmt,szProduct);
509
510     if (create)
511         rc = RegCreateKeyW(HKEY_LOCAL_MACHINE, keypath, key);
512     else
513         rc = RegOpenKeyW(HKEY_LOCAL_MACHINE, keypath, key);
514
515     return rc;
516 }
517
518 UINT MSIREG_DeleteUninstallKey(LPCWSTR szProduct)
519 {
520     WCHAR keypath[0x200];
521     TRACE("%s\n",debugstr_w(szProduct));
522
523     sprintfW(keypath,szUninstall_fmt,szProduct);
524
525     return RegDeleteTreeW(HKEY_LOCAL_MACHINE, keypath);
526 }
527
528 UINT MSIREG_OpenUserProductsKey(LPCWSTR szProduct, HKEY* key, BOOL create)
529 {
530     UINT rc;
531     WCHAR squished_pc[GUID_SIZE];
532     WCHAR keypath[0x200];
533
534     TRACE("%s\n",debugstr_w(szProduct));
535     if (!squash_guid(szProduct,squished_pc))
536         return ERROR_FUNCTION_FAILED;
537     TRACE("squished (%s)\n", debugstr_w(squished_pc));
538
539     sprintfW(keypath,szUserProduct_fmt,squished_pc);
540
541     if (create)
542         rc = RegCreateKeyW(HKEY_CURRENT_USER,keypath,key);
543     else
544         rc = RegOpenKeyW(HKEY_CURRENT_USER,keypath,key);
545
546     return rc;
547 }
548
549 UINT MSIREG_DeleteUserProductKey(LPCWSTR szProduct)
550 {
551     WCHAR squished_pc[GUID_SIZE];
552     WCHAR keypath[0x200];
553
554     TRACE("%s\n",debugstr_w(szProduct));
555     if (!squash_guid(szProduct,squished_pc))
556         return ERROR_FUNCTION_FAILED;
557     TRACE("squished (%s)\n", debugstr_w(squished_pc));
558
559     sprintfW(keypath,szUserProduct_fmt,squished_pc);
560
561     return RegDeleteTreeW(HKEY_CURRENT_USER, keypath);
562 }
563
564 UINT MSIREG_OpenUserPatchesKey(LPCWSTR szPatch, HKEY* key, BOOL create)
565 {
566     UINT rc;
567     WCHAR squished_pc[GUID_SIZE];
568     WCHAR keypath[0x200];
569
570     TRACE("%s\n",debugstr_w(szPatch));
571     if (!squash_guid(szPatch,squished_pc))
572         return ERROR_FUNCTION_FAILED;
573     TRACE("squished (%s)\n", debugstr_w(squished_pc));
574
575     sprintfW(keypath,szUserPatch_fmt,squished_pc);
576
577     if (create)
578         rc = RegCreateKeyW(HKEY_CURRENT_USER,keypath,key);
579     else
580         rc = RegOpenKeyW(HKEY_CURRENT_USER,keypath,key);
581
582     return rc;
583 }
584
585 UINT MSIREG_OpenUserFeaturesKey(LPCWSTR szProduct, HKEY* key, BOOL create)
586 {
587     UINT rc;
588     WCHAR squished_pc[GUID_SIZE];
589     WCHAR keypath[0x200];
590
591     TRACE("%s\n",debugstr_w(szProduct));
592     if (!squash_guid(szProduct,squished_pc))
593         return ERROR_FUNCTION_FAILED;
594     TRACE("squished (%s)\n", debugstr_w(squished_pc));
595
596     sprintfW(keypath,szUserFeatures_fmt,squished_pc);
597
598     if (create)
599         rc = RegCreateKeyW(HKEY_CURRENT_USER,keypath,key);
600     else
601         rc = RegOpenKeyW(HKEY_CURRENT_USER,keypath,key);
602
603     return rc;
604 }
605
606 UINT MSIREG_DeleteUserFeaturesKey(LPCWSTR szProduct)
607 {
608     WCHAR squished_pc[GUID_SIZE];
609     WCHAR keypath[0x200];
610
611     TRACE("%s\n",debugstr_w(szProduct));
612     if (!squash_guid(szProduct,squished_pc))
613         return ERROR_FUNCTION_FAILED;
614     TRACE("squished (%s)\n", debugstr_w(squished_pc));
615
616     sprintfW(keypath,szUserFeatures_fmt,squished_pc);
617     return RegDeleteTreeW(HKEY_CURRENT_USER, keypath);
618 }
619
620 UINT MSIREG_OpenFeaturesKey(LPCWSTR szProduct, HKEY* key, BOOL create)
621 {
622     UINT rc;
623     WCHAR squished_pc[GUID_SIZE];
624     WCHAR keypath[0x200];
625
626     TRACE("%s\n",debugstr_w(szProduct));
627     if (!squash_guid(szProduct,squished_pc))
628         return ERROR_FUNCTION_FAILED;
629     TRACE("squished (%s)\n", debugstr_w(squished_pc));
630
631     sprintfW(keypath,szInstaller_Features_fmt,squished_pc);
632
633     if (create)
634         rc = RegCreateKeyW(HKEY_LOCAL_MACHINE,keypath,key);
635     else
636         rc = RegOpenKeyW(HKEY_LOCAL_MACHINE,keypath,key);
637
638     return rc;
639 }
640
641 UINT MSIREG_OpenUserDataFeaturesKey(LPCWSTR szProduct, HKEY *key, BOOL create)
642 {
643     UINT rc;
644     WCHAR squished_pc[GUID_SIZE];
645     WCHAR keypath[0x200];
646     LPWSTR usersid;
647
648     TRACE("%s\n", debugstr_w(szProduct));
649     if (!squash_guid(szProduct, squished_pc))
650         return ERROR_FUNCTION_FAILED;
651     TRACE("squished (%s)\n", debugstr_w(squished_pc));
652
653     rc = get_user_sid(&usersid);
654     if (rc != ERROR_SUCCESS || !usersid)
655     {
656         ERR("Failed to retrieve user SID: %d\n", rc);
657         return rc;
658     }
659
660     sprintfW(keypath, szUserDataFeatures_fmt, usersid, squished_pc);
661
662     if (create)
663         rc = RegCreateKeyW(HKEY_LOCAL_MACHINE, keypath, key);
664     else
665         rc = RegOpenKeyW(HKEY_LOCAL_MACHINE, keypath, key);
666
667     LocalFree(usersid);
668     return rc;
669 }
670
671 UINT MSIREG_OpenLocalUserDataFeaturesKey(LPCWSTR szProduct, HKEY *key, BOOL create)
672 {
673     WCHAR squished_pc[GUID_SIZE];
674     WCHAR keypath[0x200];
675
676     TRACE("%s\n", debugstr_w(szProduct));
677     if (!squash_guid(szProduct, squished_pc))
678         return ERROR_FUNCTION_FAILED;
679     TRACE("squished (%s)\n", debugstr_w(squished_pc));
680
681     sprintfW(keypath, szUserDataFeatures_fmt, localsid, squished_pc);
682
683     if (create)
684         return RegCreateKeyW(HKEY_LOCAL_MACHINE, keypath, key);
685
686     return RegOpenKeyW(HKEY_LOCAL_MACHINE, keypath, key);
687 }
688
689 UINT MSIREG_OpenUserComponentsKey(LPCWSTR szComponent, HKEY* key, BOOL create)
690 {
691     UINT rc;
692     WCHAR squished_cc[GUID_SIZE];
693     WCHAR keypath[0x200];
694
695     TRACE("%s\n",debugstr_w(szComponent));
696     if (!squash_guid(szComponent,squished_cc))
697         return ERROR_FUNCTION_FAILED;
698     TRACE("squished (%s)\n", debugstr_w(squished_cc));
699
700     sprintfW(keypath,szUser_Components_fmt,squished_cc);
701
702     if (create)
703         rc = RegCreateKeyW(HKEY_CURRENT_USER,keypath,key);
704     else
705         rc = RegOpenKeyW(HKEY_CURRENT_USER,keypath,key);
706
707     return rc;
708 }
709
710 UINT MSIREG_OpenLocalUserDataComponentKey(LPCWSTR szComponent, HKEY *key, BOOL create)
711 {
712     WCHAR comp[GUID_SIZE];
713     WCHAR keypath[0x200];
714
715     TRACE("%s\n", debugstr_w(szComponent));
716     if (!squash_guid(szComponent, comp))
717         return ERROR_FUNCTION_FAILED;
718     TRACE("squished (%s)\n", debugstr_w(comp));
719
720     sprintfW(keypath, szUserDataComp_fmt, localsid, comp);
721
722     if (create)
723         return RegCreateKeyW(HKEY_LOCAL_MACHINE, keypath, key);
724
725     return RegOpenKeyW(HKEY_LOCAL_MACHINE, keypath, key);
726 }
727
728 UINT MSIREG_DeleteLocalUserDataComponentKey(LPCWSTR szComponent)
729 {
730     WCHAR comp[GUID_SIZE];
731     WCHAR keypath[0x200];
732
733     TRACE("%s\n", debugstr_w(szComponent));
734     if (!squash_guid(szComponent, comp))
735         return ERROR_FUNCTION_FAILED;
736     TRACE("squished (%s)\n", debugstr_w(comp));
737
738     sprintfW(keypath, szUserDataComp_fmt, localsid, comp);
739     return RegDeleteTreeW(HKEY_LOCAL_MACHINE, keypath);
740 }
741
742 UINT MSIREG_OpenUserDataComponentKey(LPCWSTR szComponent, HKEY *key, BOOL create)
743 {
744     UINT rc;
745     WCHAR comp[GUID_SIZE];
746     WCHAR keypath[0x200];
747     LPWSTR usersid;
748
749     TRACE("%s\n", debugstr_w(szComponent));
750     if (!squash_guid(szComponent, comp))
751         return ERROR_FUNCTION_FAILED;
752     TRACE("squished (%s)\n", debugstr_w(comp));
753
754     rc = get_user_sid(&usersid);
755     if (rc != ERROR_SUCCESS || !usersid)
756     {
757         ERR("Failed to retrieve user SID: %d\n", rc);
758         return rc;
759     }
760
761     sprintfW(keypath, szUserDataComp_fmt, usersid, comp);
762
763     if (create)
764         rc = RegCreateKeyW(HKEY_LOCAL_MACHINE, keypath, key);
765     else
766         rc = RegOpenKeyW(HKEY_LOCAL_MACHINE, keypath, key);
767
768     LocalFree(usersid);
769     return rc;
770 }
771
772 UINT MSIREG_DeleteUserDataComponentKey(LPCWSTR szComponent)
773 {
774     UINT rc;
775     WCHAR comp[GUID_SIZE];
776     WCHAR keypath[0x200];
777     LPWSTR usersid;
778
779     TRACE("%s\n", debugstr_w(szComponent));
780     if (!squash_guid(szComponent, comp))
781         return ERROR_FUNCTION_FAILED;
782     TRACE("squished (%s)\n", debugstr_w(comp));
783
784     rc = get_user_sid(&usersid);
785     if (rc != ERROR_SUCCESS || !usersid)
786     {
787         ERR("Failed to retrieve user SID: %d\n", rc);
788         return rc;
789     }
790
791     sprintfW(keypath, szUserDataComp_fmt, usersid, comp);
792
793     LocalFree(usersid);
794     return RegDeleteTreeW(HKEY_LOCAL_MACHINE, keypath);
795 }
796
797 UINT MSIREG_OpenUserDataProductKey(LPCWSTR szProduct, HKEY *key, BOOL create)
798 {
799     UINT rc;
800     WCHAR squished_pc[GUID_SIZE];
801     WCHAR keypath[0x200];
802     LPWSTR usersid;
803
804     TRACE("%s\n", debugstr_w(szProduct));
805     if (!squash_guid(szProduct, squished_pc))
806         return ERROR_FUNCTION_FAILED;
807     TRACE("squished (%s)\n", debugstr_w(squished_pc));
808
809     rc = get_user_sid(&usersid);
810     if (rc != ERROR_SUCCESS || !usersid)
811     {
812         ERR("Failed to retrieve user SID: %d\n", rc);
813         return rc;
814     }
815
816     sprintfW(keypath, szUserDataProd_fmt, usersid, squished_pc);
817
818     if (create)
819         rc = RegCreateKeyW(HKEY_LOCAL_MACHINE, keypath, key);
820     else
821         rc = RegOpenKeyW(HKEY_LOCAL_MACHINE, keypath, key);
822
823     LocalFree(usersid);
824     return rc;
825 }
826
827 UINT MSIREG_OpenLocalUserDataProductKey(LPCWSTR szProduct, HKEY *key, BOOL create)
828 {
829     WCHAR squished_pc[GUID_SIZE];
830     WCHAR keypath[0x200];
831
832     TRACE("%s\n", debugstr_w(szProduct));
833     if (!squash_guid(szProduct, squished_pc))
834         return ERROR_FUNCTION_FAILED;
835     TRACE("squished (%s)\n", debugstr_w(squished_pc));
836
837     sprintfW(keypath, szUserDataProd_fmt, localsid, squished_pc);
838
839     if (create)
840         return RegCreateKeyW(HKEY_LOCAL_MACHINE, keypath, key);
841
842     return RegOpenKeyW(HKEY_LOCAL_MACHINE, keypath, key);
843 }
844
845 static UINT MSIREG_OpenInstallProps(LPCWSTR szProduct, LPCWSTR szUserSID,
846                                     HKEY *key, BOOL create)
847 {
848     UINT rc;
849     WCHAR squished_pc[GUID_SIZE];
850     WCHAR keypath[0x200];
851
852     TRACE("%s\n", debugstr_w(szProduct));
853     if (!squash_guid(szProduct, squished_pc))
854         return ERROR_FUNCTION_FAILED;
855     TRACE("squished (%s)\n", debugstr_w(squished_pc));
856
857     sprintfW(keypath, szInstallProperties_fmt, szUserSID, squished_pc);
858
859     if (create)
860         rc = RegCreateKeyW(HKEY_LOCAL_MACHINE, keypath, key);
861     else
862         rc = RegOpenKeyW(HKEY_LOCAL_MACHINE, keypath, key);
863
864     return rc;
865 }
866
867 UINT MSIREG_OpenCurrentUserInstallProps(LPCWSTR szProduct, HKEY *key,
868                                                BOOL create)
869 {
870     UINT rc;
871     LPWSTR usersid;
872
873     rc = get_user_sid(&usersid);
874     if (rc != ERROR_SUCCESS || !usersid)
875     {
876         ERR("Failed to retrieve user SID: %d\n", rc);
877         return rc;
878     }
879
880     rc = MSIREG_OpenInstallProps(szProduct, usersid, key, create);
881
882     LocalFree(usersid);
883     return rc;
884 }
885
886 UINT MSIREG_OpenLocalSystemInstallProps(LPCWSTR szProduct, HKEY *key,
887                                         BOOL create)
888 {
889     return MSIREG_OpenInstallProps(szProduct, localsid, key, create);
890 }
891
892 UINT MSIREG_DeleteUserDataProductKey(LPCWSTR szProduct)
893 {
894     UINT rc;
895     WCHAR squished_pc[GUID_SIZE];
896     WCHAR keypath[0x200];
897     LPWSTR usersid;
898
899     TRACE("%s\n", debugstr_w(szProduct));
900     if (!squash_guid(szProduct, squished_pc))
901         return ERROR_FUNCTION_FAILED;
902     TRACE("squished (%s)\n", debugstr_w(squished_pc));
903
904     rc = get_user_sid(&usersid);
905     if (rc != ERROR_SUCCESS || !usersid)
906     {
907         ERR("Failed to retrieve user SID: %d\n", rc);
908         return rc;
909     }
910
911     sprintfW(keypath, szUserDataProd_fmt, usersid, squished_pc);
912
913     LocalFree(usersid);
914     return RegDeleteTreeW(HKEY_LOCAL_MACHINE, keypath);
915 }
916
917 UINT MSIREG_OpenProductsKey(LPCWSTR szProduct, HKEY* key, BOOL create)
918 {
919     UINT rc;
920     WCHAR squished_pc[GUID_SIZE];
921     WCHAR keypath[0x200];
922
923     TRACE("%s\n",debugstr_w(szProduct));
924     if (!squash_guid(szProduct,squished_pc))
925         return ERROR_FUNCTION_FAILED;
926     TRACE("squished (%s)\n", debugstr_w(squished_pc));
927
928     sprintfW(keypath,szInstaller_Products_fmt,squished_pc);
929
930     if (create)
931         rc = RegCreateKeyW(HKEY_LOCAL_MACHINE,keypath,key);
932     else
933         rc = RegOpenKeyW(HKEY_LOCAL_MACHINE,keypath,key);
934
935     return rc;
936 }
937
938 UINT MSIREG_DeleteProductKey(LPCWSTR szProduct)
939 {
940     WCHAR squished_pc[GUID_SIZE];
941     WCHAR keypath[0x200];
942
943     TRACE("%s\n", debugstr_w(szProduct));
944     if (!squash_guid(szProduct, squished_pc))
945         return ERROR_FUNCTION_FAILED;
946     TRACE("squished (%s)\n", debugstr_w(squished_pc));
947
948     sprintfW(keypath, szInstaller_Products_fmt, squished_pc);
949
950     return RegDeleteTreeW(HKEY_LOCAL_MACHINE, keypath);
951 }
952
953 UINT MSIREG_OpenPatchesKey(LPCWSTR szPatch, HKEY* key, BOOL create)
954 {
955     UINT rc;
956     WCHAR squished_pc[GUID_SIZE];
957     WCHAR keypath[0x200];
958
959     TRACE("%s\n",debugstr_w(szPatch));
960     if (!squash_guid(szPatch,squished_pc))
961         return ERROR_FUNCTION_FAILED;
962     TRACE("squished (%s)\n", debugstr_w(squished_pc));
963
964     sprintfW(keypath,szInstaller_Patches_fmt,squished_pc);
965
966     if (create)
967         rc = RegCreateKeyW(HKEY_LOCAL_MACHINE,keypath,key);
968     else
969         rc = RegOpenKeyW(HKEY_LOCAL_MACHINE,keypath,key);
970
971     return rc;
972 }
973
974 UINT MSIREG_OpenUpgradeCodesKey(LPCWSTR szUpgradeCode, HKEY* key, BOOL create)
975 {
976     UINT rc;
977     WCHAR squished_pc[GUID_SIZE];
978     WCHAR keypath[0x200];
979
980     TRACE("%s\n",debugstr_w(szUpgradeCode));
981     if (!squash_guid(szUpgradeCode,squished_pc))
982         return ERROR_FUNCTION_FAILED;
983     TRACE("squished (%s)\n", debugstr_w(squished_pc));
984
985     sprintfW(keypath,szInstaller_UpgradeCodes_fmt,squished_pc);
986
987     if (create)
988         rc = RegCreateKeyW(HKEY_LOCAL_MACHINE,keypath,key);
989     else
990         rc = RegOpenKeyW(HKEY_LOCAL_MACHINE,keypath,key);
991
992     return rc;
993 }
994
995 UINT MSIREG_OpenUserUpgradeCodesKey(LPCWSTR szUpgradeCode, HKEY* key, BOOL create)
996 {
997     UINT rc;
998     WCHAR squished_pc[GUID_SIZE];
999     WCHAR keypath[0x200];
1000
1001     TRACE("%s\n",debugstr_w(szUpgradeCode));
1002     if (!squash_guid(szUpgradeCode,squished_pc))
1003         return ERROR_FUNCTION_FAILED;
1004     TRACE("squished (%s)\n", debugstr_w(squished_pc));
1005
1006     sprintfW(keypath,szInstaller_UserUpgradeCodes_fmt,squished_pc);
1007
1008     if (create)
1009         rc = RegCreateKeyW(HKEY_CURRENT_USER,keypath,key);
1010     else
1011         rc = RegOpenKeyW(HKEY_CURRENT_USER,keypath,key);
1012
1013     return rc;
1014 }
1015
1016 UINT MSIREG_DeleteUserUpgradeCodesKey(LPCWSTR szUpgradeCode)
1017 {
1018     WCHAR squished_pc[GUID_SIZE];
1019     WCHAR keypath[0x200];
1020
1021     TRACE("%s\n",debugstr_w(szUpgradeCode));
1022     if (!squash_guid(szUpgradeCode,squished_pc))
1023         return ERROR_FUNCTION_FAILED;
1024     TRACE("squished (%s)\n", debugstr_w(squished_pc));
1025
1026     sprintfW(keypath,szInstaller_UserUpgradeCodes_fmt,squished_pc);
1027
1028     return RegDeleteTreeW(HKEY_CURRENT_USER, keypath);
1029 }
1030
1031 UINT MSIREG_OpenLocalSystemProductKey(LPCWSTR szProductCode, HKEY *key, BOOL create)
1032 {
1033     WCHAR squished_pc[GUID_SIZE];
1034     WCHAR keypath[0x200];
1035
1036     TRACE("%s\n", debugstr_w(szProductCode));
1037
1038     if (!squash_guid(szProductCode, squished_pc))
1039         return ERROR_FUNCTION_FAILED;
1040
1041     TRACE("squished (%s)\n", debugstr_w(squished_pc));
1042
1043     sprintfW(keypath, szInstaller_LocalSystemProductCodes_fmt, squished_pc);
1044
1045     if (create)
1046         return RegCreateKeyW(HKEY_LOCAL_MACHINE, keypath, key);
1047
1048     return RegOpenKeyW(HKEY_LOCAL_MACHINE, keypath, key);
1049 }
1050
1051 UINT MSIREG_OpenLocalSystemComponentKey(LPCWSTR szComponent, HKEY *key, BOOL create)
1052 {
1053     WCHAR squished_pc[GUID_SIZE];
1054     WCHAR keypath[0x200];
1055
1056     TRACE("%s\n", debugstr_w(szComponent));
1057
1058     if (!squash_guid(szComponent, squished_pc))
1059         return ERROR_FUNCTION_FAILED;
1060
1061     TRACE("squished (%s)\n", debugstr_w(squished_pc));
1062
1063     sprintfW(keypath, szInstaller_LocalSystemComponent_fmt, squished_pc);
1064
1065     if (create)
1066         return RegCreateKeyW(HKEY_LOCAL_MACHINE, keypath, key);
1067
1068     return RegOpenKeyW(HKEY_LOCAL_MACHINE, keypath, key);
1069 }
1070
1071 UINT MSIREG_OpenLocalClassesProductKey(LPCWSTR szProductCode, HKEY *key, BOOL create)
1072 {
1073     WCHAR squished_pc[GUID_SIZE];
1074     WCHAR keypath[0x200];
1075
1076     TRACE("%s\n", debugstr_w(szProductCode));
1077
1078     if (!squash_guid(szProductCode, squished_pc))
1079         return ERROR_FUNCTION_FAILED;
1080
1081     TRACE("squished (%s)\n", debugstr_w(squished_pc));
1082
1083     sprintfW(keypath, szInstaller_LocalClassesProd_fmt, squished_pc);
1084
1085     if (create)
1086         return RegCreateKeyW(HKEY_LOCAL_MACHINE, keypath, key);
1087
1088     return RegOpenKeyW(HKEY_LOCAL_MACHINE, keypath, key);
1089 }
1090
1091 UINT MSIREG_DeleteLocalClassesProductKey(LPCWSTR szProductCode)
1092 {
1093     WCHAR squished_pc[GUID_SIZE];
1094     WCHAR keypath[0x200];
1095
1096     TRACE("%s\n", debugstr_w(szProductCode));
1097
1098     if (!squash_guid(szProductCode, squished_pc))
1099         return ERROR_FUNCTION_FAILED;
1100
1101     TRACE("squished (%s)\n", debugstr_w(squished_pc));
1102
1103     sprintfW(keypath, szInstaller_LocalClassesProd_fmt, squished_pc);
1104
1105     return RegDeleteTreeW(HKEY_LOCAL_MACHINE, keypath);
1106 }
1107
1108 UINT MSIREG_OpenLocalClassesFeaturesKey(LPCWSTR szProductCode, HKEY *key, BOOL create)
1109 {
1110     WCHAR squished_pc[GUID_SIZE];
1111     WCHAR keypath[0x200];
1112
1113     TRACE("%s\n", debugstr_w(szProductCode));
1114
1115     if (!squash_guid(szProductCode, squished_pc))
1116         return ERROR_FUNCTION_FAILED;
1117
1118     TRACE("squished (%s)\n", debugstr_w(squished_pc));
1119
1120     sprintfW(keypath, szInstaller_LocalClassesFeat_fmt, squished_pc);
1121
1122     if (create)
1123         return RegCreateKeyW(HKEY_LOCAL_MACHINE, keypath, key);
1124
1125     return RegOpenKeyW(HKEY_LOCAL_MACHINE, keypath, key);
1126 }
1127
1128 UINT MSIREG_DeleteLocalClassesFeaturesKey(LPCWSTR szProductCode)
1129 {
1130     WCHAR squished_pc[GUID_SIZE];
1131     WCHAR keypath[0x200];
1132
1133     TRACE("%s\n", debugstr_w(szProductCode));
1134
1135     if (!squash_guid(szProductCode, squished_pc))
1136         return ERROR_FUNCTION_FAILED;
1137
1138     TRACE("squished (%s)\n", debugstr_w(squished_pc));
1139
1140     sprintfW(keypath, szInstaller_LocalClassesFeat_fmt, squished_pc);
1141
1142     return RegDeleteTreeW(HKEY_LOCAL_MACHINE, keypath);
1143 }
1144
1145 UINT MSIREG_OpenLocalManagedProductKey(LPCWSTR szProductCode, HKEY *key, BOOL create)
1146 {
1147     WCHAR squished_pc[GUID_SIZE];
1148     WCHAR keypath[0x200];
1149     LPWSTR usersid;
1150     UINT r;
1151
1152     TRACE("%s\n", debugstr_w(szProductCode));
1153
1154     if (!squash_guid(szProductCode, squished_pc))
1155         return ERROR_FUNCTION_FAILED;
1156
1157     TRACE("squished (%s)\n", debugstr_w(squished_pc));
1158
1159     r = get_user_sid(&usersid);
1160     if (r != ERROR_SUCCESS || !usersid)
1161     {
1162         ERR("Failed to retrieve user SID: %d\n", r);
1163         return r;
1164     }
1165
1166     sprintfW(keypath, szInstaller_LocalManagedProd_fmt, usersid, squished_pc);
1167     LocalFree(usersid);
1168
1169     if (create)
1170         return RegCreateKeyW(HKEY_LOCAL_MACHINE, keypath, key);
1171
1172     return RegOpenKeyW(HKEY_LOCAL_MACHINE, keypath, key);
1173 }
1174
1175 UINT MSIREG_OpenManagedFeaturesKey(LPCWSTR szProductCode, HKEY *key, BOOL create)
1176 {
1177     WCHAR squished_pc[GUID_SIZE];
1178     WCHAR keypath[0x200];
1179     LPWSTR usersid;
1180     UINT r;
1181
1182     TRACE("%s\n", debugstr_w(szProductCode));
1183
1184     if (!squash_guid(szProductCode, squished_pc))
1185         return ERROR_FUNCTION_FAILED;
1186
1187     TRACE("squished (%s)\n", debugstr_w(squished_pc));
1188
1189     r = get_user_sid(&usersid);
1190     if (r != ERROR_SUCCESS || !usersid)
1191     {
1192         ERR("Failed to retrieve user SID: %d\n", r);
1193         return r;
1194     }
1195
1196     sprintfW(keypath, szInstaller_LocalManagedFeat_fmt, usersid, squished_pc);
1197     LocalFree(usersid);
1198
1199     if (create)
1200         return RegCreateKeyW(HKEY_LOCAL_MACHINE, keypath, key);
1201
1202     return RegOpenKeyW(HKEY_LOCAL_MACHINE, keypath, key);
1203 }
1204
1205 UINT MSIREG_OpenClassesUpgradeCodesKey(LPCWSTR szUpgradeCode, HKEY* key, BOOL create)
1206 {
1207     WCHAR squished_pc[GUID_SIZE];
1208     WCHAR keypath[0x200];
1209
1210     TRACE("%s\n", debugstr_w(szUpgradeCode));
1211     if (!squash_guid(szUpgradeCode, squished_pc))
1212         return ERROR_FUNCTION_FAILED;
1213     TRACE("squished (%s)\n", debugstr_w(squished_pc));
1214
1215     sprintfW(keypath, szInstaller_ClassesUpgrade_fmt, squished_pc);
1216
1217     if (create)
1218         return RegCreateKeyW(HKEY_CLASSES_ROOT, keypath, key);
1219
1220     return RegOpenKeyW(HKEY_CLASSES_ROOT, keypath, key);
1221 }
1222
1223 /*************************************************************************
1224  *  MsiDecomposeDescriptorW   [MSI.@]
1225  *
1226  * Decomposes an MSI descriptor into product, feature and component parts.
1227  * An MSI descriptor is a string of the form:
1228  *   [base 85 guid] [feature code] '>' [base 85 guid]
1229  *
1230  * PARAMS
1231  *   szDescriptor  [I]  the descriptor to decompose
1232  *   szProduct     [O]  buffer of MAX_FEATURE_CHARS+1 for the product guid
1233  *   szFeature     [O]  buffer of MAX_FEATURE_CHARS+1 for the feature code
1234  *   szComponent   [O]  buffer of MAX_FEATURE_CHARS+1 for the component guid
1235  *   pUsed         [O]  the length of the descriptor
1236  *
1237  * RETURNS
1238  *   ERROR_SUCCESS             if everything worked correctly
1239  *   ERROR_INVALID_PARAMETER   if the descriptor was invalid
1240  *
1241  */
1242 UINT WINAPI MsiDecomposeDescriptorW( LPCWSTR szDescriptor, LPWSTR szProduct,
1243                 LPWSTR szFeature, LPWSTR szComponent, LPDWORD pUsed )
1244 {
1245     UINT r, len;
1246     LPWSTR p;
1247     GUID product, component;
1248
1249     TRACE("%s %p %p %p %p\n", debugstr_w(szDescriptor), szProduct,
1250           szFeature, szComponent, pUsed);
1251
1252     r = decode_base85_guid( szDescriptor, &product );
1253     if( !r )
1254         return ERROR_INVALID_PARAMETER;
1255
1256     TRACE("product %s\n", debugstr_guid( &product ));
1257
1258     p = strchrW(&szDescriptor[20],'>');
1259     if( !p )
1260         return ERROR_INVALID_PARAMETER;
1261
1262     len = (p - &szDescriptor[20]);
1263     if( len > MAX_FEATURE_CHARS )
1264         return ERROR_INVALID_PARAMETER;
1265
1266     TRACE("feature %s\n", debugstr_wn( &szDescriptor[20], len ));
1267
1268     r = decode_base85_guid( p+1, &component );
1269     if( !r )
1270         return ERROR_INVALID_PARAMETER;
1271
1272     TRACE("component %s\n", debugstr_guid( &component ));
1273
1274     if (szProduct)
1275         StringFromGUID2( &product, szProduct, MAX_FEATURE_CHARS+1 );
1276     if (szComponent)
1277         StringFromGUID2( &component, szComponent, MAX_FEATURE_CHARS+1 );
1278     if (szFeature)
1279     {
1280         memcpy( szFeature, &szDescriptor[20], len*sizeof(WCHAR) );
1281         szFeature[len] = 0;
1282     }
1283     len = ( &p[21] - szDescriptor );
1284
1285     TRACE("length = %d\n", len);
1286     *pUsed = len;
1287
1288     return ERROR_SUCCESS;
1289 }
1290
1291 UINT WINAPI MsiDecomposeDescriptorA( LPCSTR szDescriptor, LPSTR szProduct,
1292                 LPSTR szFeature, LPSTR szComponent, LPDWORD pUsed )
1293 {
1294     WCHAR product[MAX_FEATURE_CHARS+1];
1295     WCHAR feature[MAX_FEATURE_CHARS+1];
1296     WCHAR component[MAX_FEATURE_CHARS+1];
1297     LPWSTR str = NULL, p = NULL, f = NULL, c = NULL;
1298     UINT r;
1299
1300     TRACE("%s %p %p %p %p\n", debugstr_a(szDescriptor), szProduct,
1301           szFeature, szComponent, pUsed);
1302
1303     str = strdupAtoW( szDescriptor );
1304     if( szDescriptor && !str )
1305         return ERROR_OUTOFMEMORY;
1306
1307     if (szProduct)
1308         p = product;
1309     if (szFeature)
1310         f = feature;
1311     if (szComponent)
1312         c = component;
1313
1314     r = MsiDecomposeDescriptorW( str, p, f, c, pUsed );
1315
1316     if (r == ERROR_SUCCESS)
1317     {
1318         WideCharToMultiByte( CP_ACP, 0, p, -1,
1319                              szProduct, MAX_FEATURE_CHARS+1, NULL, NULL );
1320         WideCharToMultiByte( CP_ACP, 0, f, -1,
1321                              szFeature, MAX_FEATURE_CHARS+1, NULL, NULL );
1322         WideCharToMultiByte( CP_ACP, 0, c, -1,
1323                              szComponent, MAX_FEATURE_CHARS+1, NULL, NULL );
1324     }
1325
1326     msi_free( str );
1327
1328     return r;
1329 }
1330
1331 UINT WINAPI MsiEnumProductsA(DWORD index, LPSTR lpguid)
1332 {
1333     DWORD r;
1334     WCHAR szwGuid[GUID_SIZE];
1335
1336     TRACE("%d %p\n", index, lpguid);
1337
1338     if (NULL == lpguid)
1339         return ERROR_INVALID_PARAMETER;
1340     r = MsiEnumProductsW(index, szwGuid);
1341     if( r == ERROR_SUCCESS )
1342         WideCharToMultiByte(CP_ACP, 0, szwGuid, -1, lpguid, GUID_SIZE, NULL, NULL);
1343
1344     return r;
1345 }
1346
1347 UINT WINAPI MsiEnumProductsW(DWORD index, LPWSTR lpguid)
1348 {
1349     HKEY hkeyProducts = 0;
1350     DWORD r;
1351     WCHAR szKeyName[SQUISH_GUID_SIZE];
1352
1353     TRACE("%d %p\n", index, lpguid);
1354
1355     if (NULL == lpguid)
1356         return ERROR_INVALID_PARAMETER;
1357
1358     r = RegCreateKeyW(HKEY_LOCAL_MACHINE, szInstaller_Products, &hkeyProducts);
1359     if( r != ERROR_SUCCESS )
1360         return ERROR_NO_MORE_ITEMS;
1361
1362     r = RegEnumKeyW(hkeyProducts, index, szKeyName, SQUISH_GUID_SIZE);
1363     if( r == ERROR_SUCCESS )
1364         unsquash_guid(szKeyName, lpguid);
1365     RegCloseKey(hkeyProducts);
1366
1367     return r;
1368 }
1369
1370 UINT WINAPI MsiEnumFeaturesA(LPCSTR szProduct, DWORD index, 
1371       LPSTR szFeature, LPSTR szParent)
1372 {
1373     DWORD r;
1374     WCHAR szwFeature[GUID_SIZE], szwParent[GUID_SIZE];
1375     LPWSTR szwProduct = NULL;
1376
1377     TRACE("%s %d %p %p\n", debugstr_a(szProduct), index, szFeature, szParent);
1378
1379     if( szProduct )
1380     {
1381         szwProduct = strdupAtoW( szProduct );
1382         if( !szwProduct )
1383             return ERROR_OUTOFMEMORY;
1384     }
1385
1386     r = MsiEnumFeaturesW(szwProduct, index, szwFeature, szwParent);
1387     if( r == ERROR_SUCCESS )
1388     {
1389         WideCharToMultiByte(CP_ACP, 0, szwFeature, -1,
1390                             szFeature, GUID_SIZE, NULL, NULL);
1391         WideCharToMultiByte(CP_ACP, 0, szwParent, -1,
1392                             szParent, GUID_SIZE, NULL, NULL);
1393     }
1394
1395     msi_free( szwProduct);
1396
1397     return r;
1398 }
1399
1400 UINT WINAPI MsiEnumFeaturesW(LPCWSTR szProduct, DWORD index, 
1401       LPWSTR szFeature, LPWSTR szParent)
1402 {
1403     HKEY hkeyProduct = 0;
1404     DWORD r, sz;
1405
1406     TRACE("%s %d %p %p\n", debugstr_w(szProduct), index, szFeature, szParent);
1407
1408     if( !szProduct )
1409         return ERROR_INVALID_PARAMETER;
1410
1411     r = MSIREG_OpenFeaturesKey(szProduct,&hkeyProduct,FALSE);
1412     if( r != ERROR_SUCCESS )
1413         return ERROR_NO_MORE_ITEMS;
1414
1415     sz = GUID_SIZE;
1416     r = RegEnumValueW(hkeyProduct, index, szFeature, &sz, NULL, NULL, NULL, NULL);
1417     RegCloseKey(hkeyProduct);
1418
1419     return r;
1420 }
1421
1422 UINT WINAPI MsiEnumComponentsA(DWORD index, LPSTR lpguid)
1423 {
1424     DWORD r;
1425     WCHAR szwGuid[GUID_SIZE];
1426
1427     TRACE("%d %p\n", index, lpguid);
1428
1429     r = MsiEnumComponentsW(index, szwGuid);
1430     if( r == ERROR_SUCCESS )
1431         WideCharToMultiByte(CP_ACP, 0, szwGuid, -1, lpguid, GUID_SIZE, NULL, NULL);
1432
1433     return r;
1434 }
1435
1436 UINT WINAPI MsiEnumComponentsW(DWORD index, LPWSTR lpguid)
1437 {
1438     HKEY hkeyComponents = 0;
1439     DWORD r;
1440     WCHAR szKeyName[SQUISH_GUID_SIZE];
1441
1442     TRACE("%d %p\n", index, lpguid);
1443
1444     r = RegCreateKeyW(HKEY_LOCAL_MACHINE, szInstaller_Components, &hkeyComponents);
1445     if( r != ERROR_SUCCESS )
1446         return ERROR_NO_MORE_ITEMS;
1447
1448     r = RegEnumKeyW(hkeyComponents, index, szKeyName, SQUISH_GUID_SIZE);
1449     if( r == ERROR_SUCCESS )
1450         unsquash_guid(szKeyName, lpguid);
1451     RegCloseKey(hkeyComponents);
1452
1453     return r;
1454 }
1455
1456 UINT WINAPI MsiEnumClientsA(LPCSTR szComponent, DWORD index, LPSTR szProduct)
1457 {
1458     DWORD r;
1459     WCHAR szwProduct[GUID_SIZE];
1460     LPWSTR szwComponent = NULL;
1461
1462     TRACE("%s %d %p\n", debugstr_a(szComponent), index, szProduct);
1463
1464     if ( !szProduct )
1465         return ERROR_INVALID_PARAMETER;
1466
1467     if( szComponent )
1468     {
1469         szwComponent = strdupAtoW( szComponent );
1470         if( !szwComponent )
1471             return ERROR_OUTOFMEMORY;
1472     }
1473
1474     r = MsiEnumClientsW(szComponent?szwComponent:NULL, index, szwProduct);
1475     if( r == ERROR_SUCCESS )
1476     {
1477         WideCharToMultiByte(CP_ACP, 0, szwProduct, -1,
1478                             szProduct, GUID_SIZE, NULL, NULL);
1479     }
1480
1481     msi_free( szwComponent);
1482
1483     return r;
1484 }
1485
1486 UINT WINAPI MsiEnumClientsW(LPCWSTR szComponent, DWORD index, LPWSTR szProduct)
1487 {
1488     HKEY hkeyComp = 0;
1489     DWORD r, sz;
1490     WCHAR szValName[SQUISH_GUID_SIZE];
1491
1492     TRACE("%s %d %p\n", debugstr_w(szComponent), index, szProduct);
1493
1494     if (!szComponent || !*szComponent || !szProduct)
1495         return ERROR_INVALID_PARAMETER;
1496
1497     if (MSIREG_OpenUserDataComponentKey(szComponent, &hkeyComp, FALSE) != ERROR_SUCCESS &&
1498         MSIREG_OpenLocalSystemComponentKey(szComponent, &hkeyComp, FALSE) != ERROR_SUCCESS)
1499         return ERROR_UNKNOWN_COMPONENT;
1500
1501     /* see if there are any products at all */
1502     sz = SQUISH_GUID_SIZE;
1503     r = RegEnumValueW(hkeyComp, 0, szValName, &sz, NULL, NULL, NULL, NULL);
1504     if (r != ERROR_SUCCESS)
1505     {
1506         RegCloseKey(hkeyComp);
1507
1508         if (index != 0)
1509             return ERROR_INVALID_PARAMETER;
1510
1511         return ERROR_UNKNOWN_COMPONENT;
1512     }
1513
1514     sz = SQUISH_GUID_SIZE;
1515     r = RegEnumValueW(hkeyComp, index, szValName, &sz, NULL, NULL, NULL, NULL);
1516     if( r == ERROR_SUCCESS )
1517         unsquash_guid(szValName, szProduct);
1518
1519     RegCloseKey(hkeyComp);
1520
1521     return r;
1522 }
1523
1524 static UINT WINAPI MSI_EnumComponentQualifiers( LPCWSTR szComponent, DWORD iIndex,
1525                 awstring *lpQualBuf, LPDWORD pcchQual,
1526                 awstring *lpAppBuf, LPDWORD pcchAppBuf )
1527 {
1528     DWORD name_sz, val_sz, name_max, val_max, type, ofs;
1529     LPWSTR name = NULL, val = NULL;
1530     UINT r, r2;
1531     HKEY key;
1532
1533     TRACE("%s %08x %p %p %p %p\n", debugstr_w(szComponent), iIndex,
1534           lpQualBuf, pcchQual, lpAppBuf, pcchAppBuf);
1535
1536     if (!szComponent)
1537         return ERROR_INVALID_PARAMETER;
1538
1539     r = MSIREG_OpenUserComponentsKey( szComponent, &key, FALSE );
1540     if (r != ERROR_SUCCESS)
1541         return ERROR_UNKNOWN_COMPONENT;
1542
1543     /* figure out how big the name is we want to return */
1544     name_max = 0x10;
1545     r = ERROR_OUTOFMEMORY;
1546     name = msi_alloc( name_max * sizeof(WCHAR) );
1547     if (!name)
1548         goto end;
1549
1550     val_max = 0x10;
1551     r = ERROR_OUTOFMEMORY;
1552     val = msi_alloc( val_max );
1553     if (!val)
1554         goto end;
1555
1556     /* loop until we allocate enough memory */
1557     while (1)
1558     {
1559         name_sz = name_max;
1560         val_sz = val_max;
1561         r = RegEnumValueW( key, iIndex, name, &name_sz,
1562                            NULL, &type, (LPBYTE)val, &val_sz );
1563         if (r == ERROR_SUCCESS)
1564             break;
1565         if (r != ERROR_MORE_DATA)
1566             goto end;
1567  
1568         if (type != REG_MULTI_SZ)
1569         {
1570             ERR("component data has wrong type (%d)\n", type);
1571             goto end;
1572         }
1573
1574         r = ERROR_OUTOFMEMORY;
1575         if ((name_sz+1) >= name_max)
1576         {
1577             name_max *= 2;
1578             msi_free( name );
1579             name = msi_alloc( name_max * sizeof (WCHAR) );
1580             if (!name)
1581                 goto end;
1582             continue;
1583         }
1584         if (val_sz > val_max)
1585         {
1586             val_max = val_sz + sizeof (WCHAR);
1587             msi_free( val );
1588             val = msi_alloc( val_max * sizeof (WCHAR) );
1589             if (!val)
1590                 goto end;
1591             continue;
1592         }
1593         ERR("should be enough data, but isn't %d %d\n", name_sz, val_sz );
1594         goto end;
1595     }
1596
1597     ofs = 0;
1598     r = MsiDecomposeDescriptorW( val, NULL, NULL, NULL, &ofs );
1599     if (r != ERROR_SUCCESS)
1600         goto end;
1601
1602     TRACE("Providing %s and %s\n", debugstr_w(name), debugstr_w(val+ofs));
1603
1604     r = msi_strcpy_to_awstring( name, lpQualBuf, pcchQual );
1605     r2 = msi_strcpy_to_awstring( val+ofs, lpAppBuf, pcchAppBuf );
1606
1607     if (r2 != ERROR_SUCCESS)
1608         r = r2;
1609
1610 end:
1611     msi_free(val);
1612     msi_free(name);
1613     RegCloseKey(key);
1614
1615     return r;
1616 }
1617
1618 /*************************************************************************
1619  *  MsiEnumComponentQualifiersA [MSI.@]
1620  */
1621 UINT WINAPI MsiEnumComponentQualifiersA( LPCSTR szComponent, DWORD iIndex,
1622                 LPSTR lpQualifierBuf, LPDWORD pcchQualifierBuf,
1623                 LPSTR lpApplicationDataBuf, LPDWORD pcchApplicationDataBuf )
1624 {
1625     awstring qual, appdata;
1626     LPWSTR comp;
1627     UINT r;
1628
1629     TRACE("%s %08x %p %p %p %p\n", debugstr_a(szComponent), iIndex,
1630           lpQualifierBuf, pcchQualifierBuf, lpApplicationDataBuf,
1631           pcchApplicationDataBuf);
1632
1633     comp = strdupAtoW( szComponent );
1634     if (szComponent && !comp)
1635         return ERROR_OUTOFMEMORY;
1636
1637     qual.unicode = FALSE;
1638     qual.str.a = lpQualifierBuf;
1639
1640     appdata.unicode = FALSE;
1641     appdata.str.a = lpApplicationDataBuf;
1642
1643     r = MSI_EnumComponentQualifiers( comp, iIndex,
1644               &qual, pcchQualifierBuf, &appdata, pcchApplicationDataBuf );
1645     msi_free( comp );
1646     return r;
1647 }
1648
1649 /*************************************************************************
1650  *  MsiEnumComponentQualifiersW [MSI.@]
1651  */
1652 UINT WINAPI MsiEnumComponentQualifiersW( LPCWSTR szComponent, DWORD iIndex,
1653                 LPWSTR lpQualifierBuf, LPDWORD pcchQualifierBuf,
1654                 LPWSTR lpApplicationDataBuf, LPDWORD pcchApplicationDataBuf )
1655 {
1656     awstring qual, appdata;
1657
1658     TRACE("%s %08x %p %p %p %p\n", debugstr_w(szComponent), iIndex,
1659           lpQualifierBuf, pcchQualifierBuf, lpApplicationDataBuf,
1660           pcchApplicationDataBuf);
1661
1662     qual.unicode = TRUE;
1663     qual.str.w = lpQualifierBuf;
1664
1665     appdata.unicode = TRUE;
1666     appdata.str.w = lpApplicationDataBuf;
1667
1668     return MSI_EnumComponentQualifiers( szComponent, iIndex,
1669                  &qual, pcchQualifierBuf, &appdata, pcchApplicationDataBuf );
1670 }
1671
1672 /*************************************************************************
1673  *  MsiEnumRelatedProductsW   [MSI.@]
1674  *
1675  */
1676 UINT WINAPI MsiEnumRelatedProductsW(LPCWSTR szUpgradeCode, DWORD dwReserved,
1677                                     DWORD iProductIndex, LPWSTR lpProductBuf)
1678 {
1679     UINT r;
1680     HKEY hkey;
1681     DWORD dwSize = SQUISH_GUID_SIZE;
1682     WCHAR szKeyName[SQUISH_GUID_SIZE];
1683
1684     TRACE("%s %u %u %p\n", debugstr_w(szUpgradeCode), dwReserved,
1685           iProductIndex, lpProductBuf);
1686
1687     if (NULL == szUpgradeCode)
1688         return ERROR_INVALID_PARAMETER;
1689     if (NULL == lpProductBuf)
1690         return ERROR_INVALID_PARAMETER;
1691
1692     r = MSIREG_OpenUpgradeCodesKey(szUpgradeCode, &hkey, FALSE);
1693     if (r != ERROR_SUCCESS)
1694         return ERROR_NO_MORE_ITEMS;
1695
1696     r = RegEnumValueW(hkey, iProductIndex, szKeyName, &dwSize, NULL, NULL, NULL, NULL);
1697     if( r == ERROR_SUCCESS )
1698         unsquash_guid(szKeyName, lpProductBuf);
1699     RegCloseKey(hkey);
1700
1701     return r;
1702 }
1703
1704 /*************************************************************************
1705  *  MsiEnumRelatedProductsA   [MSI.@]
1706  *
1707  */
1708 UINT WINAPI MsiEnumRelatedProductsA(LPCSTR szUpgradeCode, DWORD dwReserved,
1709                                     DWORD iProductIndex, LPSTR lpProductBuf)
1710 {
1711     LPWSTR szwUpgradeCode = NULL;
1712     WCHAR productW[GUID_SIZE];
1713     UINT r;
1714
1715     TRACE("%s %u %u %p\n", debugstr_a(szUpgradeCode), dwReserved,
1716           iProductIndex, lpProductBuf);
1717
1718     if (szUpgradeCode)
1719     {
1720         szwUpgradeCode = strdupAtoW( szUpgradeCode );
1721         if( !szwUpgradeCode )
1722             return ERROR_OUTOFMEMORY;
1723     }
1724
1725     r = MsiEnumRelatedProductsW( szwUpgradeCode, dwReserved,
1726                                  iProductIndex, productW );
1727     if (r == ERROR_SUCCESS)
1728     {
1729         WideCharToMultiByte( CP_ACP, 0, productW, GUID_SIZE,
1730                              lpProductBuf, GUID_SIZE, NULL, NULL );
1731     }
1732     msi_free( szwUpgradeCode);
1733     return r;
1734 }
1735
1736 /***********************************************************************
1737  * MsiEnumPatchesExA            [MSI.@]
1738  */
1739 UINT WINAPI MsiEnumPatchesExA(LPCSTR szProductCode, LPCSTR szUserSid,
1740         DWORD dwContext, DWORD dwFilter, DWORD dwIndex, LPSTR szPatchCode,
1741         LPSTR szTargetProductCode, MSIINSTALLCONTEXT *pdwTargetProductContext,
1742         LPSTR szTargetUserSid, LPSTR pcchTargetUserSid)
1743 {
1744     FIXME("(%s, %s, %d, %d, %d, %p, %p, %p, %p, %p) stub!\n",
1745           debugstr_a(szProductCode), debugstr_a(szUserSid), dwContext, dwFilter,
1746           dwIndex, szPatchCode, szTargetProductCode, pdwTargetProductContext,
1747           szTargetUserSid, pcchTargetUserSid);
1748     return ERROR_NO_MORE_ITEMS;
1749 }
1750
1751 /***********************************************************************
1752  * MsiEnumPatchesW            [MSI.@]
1753  */
1754 UINT WINAPI MsiEnumPatchesExW(LPCWSTR szProductCode, LPCWSTR szUserSid,
1755         DWORD dwContext, DWORD dwFilter, DWORD dwIndex, LPWSTR szPatchCode,
1756         LPWSTR szTargetProductCode, MSIINSTALLCONTEXT *pdwTargetProductContext,
1757         LPWSTR szTargetUserSid, LPWSTR pcchTargetUserSid)
1758 {
1759     FIXME("(%s, %s, %d, %d, %d, %p, %p, %p, %p, %p) stub!\n",
1760           debugstr_w(szProductCode), debugstr_w(szUserSid), dwContext, dwFilter,
1761           dwIndex, szPatchCode, szTargetProductCode, pdwTargetProductContext,
1762           szTargetUserSid, pcchTargetUserSid);
1763     return ERROR_NO_MORE_ITEMS;
1764 }
1765
1766 /***********************************************************************
1767  * MsiEnumPatchesA            [MSI.@]
1768  */
1769 UINT WINAPI MsiEnumPatchesA( LPCSTR szProduct, DWORD iPatchIndex,
1770         LPSTR lpPatchBuf, LPSTR lpTransformsBuf, LPDWORD pcchTransformsBuf)
1771 {
1772     FIXME("%s %d %p %p %p\n", debugstr_a(szProduct),
1773           iPatchIndex, lpPatchBuf, lpTransformsBuf, pcchTransformsBuf);
1774     return ERROR_NO_MORE_ITEMS;
1775 }
1776
1777 /***********************************************************************
1778  * MsiEnumPatchesW            [MSI.@]
1779  */
1780 UINT WINAPI MsiEnumPatchesW( LPCWSTR szProduct, DWORD iPatchIndex,
1781         LPWSTR lpPatchBuf, LPWSTR lpTransformsBuf, LPDWORD pcchTransformsBuf)
1782 {
1783     FIXME("%s %d %p %p %p\n", debugstr_w(szProduct),
1784           iPatchIndex, lpPatchBuf, lpTransformsBuf, pcchTransformsBuf);
1785     return ERROR_NO_MORE_ITEMS;
1786 }
1787
1788 UINT WINAPI MsiEnumProductsExA( LPCSTR szProductCode, LPCSTR szUserSid,
1789         DWORD dwContext, DWORD dwIndex, CHAR szInstalledProductCode[39],
1790         MSIINSTALLCONTEXT* pdwInstalledContext, LPSTR szSid, LPDWORD pcchSid)
1791 {
1792     FIXME("%s %s %d %d %p %p %p %p\n", debugstr_a(szProductCode), debugstr_a(szUserSid),
1793           dwContext, dwIndex, szInstalledProductCode, pdwInstalledContext,
1794           szSid, pcchSid);
1795     return ERROR_NO_MORE_ITEMS;
1796 }
1797
1798 UINT WINAPI MsiEnumProductsExW( LPCWSTR szProductCode, LPCWSTR szUserSid,
1799         DWORD dwContext, DWORD dwIndex, WCHAR szInstalledProductCode[39],
1800         MSIINSTALLCONTEXT* pdwInstalledContext, LPWSTR szSid, LPDWORD pcchSid)
1801 {
1802     FIXME("%s %s %d %d %p %p %p %p\n", debugstr_w(szProductCode), debugstr_w(szUserSid),
1803           dwContext, dwIndex, szInstalledProductCode, pdwInstalledContext,
1804           szSid, pcchSid);
1805     return ERROR_NO_MORE_ITEMS;
1806 }