Make Rtl*Registry* functions case insensitive.
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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
40 WINE_DEFAULT_DEBUG_CHANNEL(msi);
41
42
43 /* 
44  * This module will be all the helper functions for registry access by the
45  * installer bits. 
46  */
47 static const WCHAR szUserFeatures_fmt[] = {
48 'S','o','f','t','w','a','r','e','\\',
49 'M','i','c','r','o','s','o','f','t','\\',
50 'I','n','s','t','a','l','l','e','r','\\',
51 'F','e','a','t','u','r','e','s','\\',
52 '%','s',0};
53
54 static const WCHAR szInstaller_Features[] = {
55 'S','o','f','t','w','a','r','e','\\',
56 'M','i','c','r','o','s','o','f','t','\\',
57 'W','i','n','d','o','w','s','\\',
58 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
59 'I','n','s','t','a','l','l','e','r','\\',
60 'F','e','a','t','u','r','e','s',0 };
61
62 static const WCHAR szInstaller_Features_fmt[] = {
63 'S','o','f','t','w','a','r','e','\\',
64 'M','i','c','r','o','s','o','f','t','\\',
65 'W','i','n','d','o','w','s','\\',
66 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
67 'I','n','s','t','a','l','l','e','r','\\',
68 'F','e','a','t','u','r','e','s','\\',
69 '%','s',0};
70
71 static const WCHAR szInstaller_Components[] = {
72 'S','o','f','t','w','a','r','e','\\',
73 'M','i','c','r','o','s','o','f','t','\\',
74 'W','i','n','d','o','w','s','\\',
75 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
76 'I','n','s','t','a','l','l','e','r','\\',
77 'C','o','m','p','o','n','e','n','t','s',0 };
78
79 static const WCHAR szInstaller_Components_fmt[] = {
80 'S','o','f','t','w','a','r','e','\\',
81 'M','i','c','r','o','s','o','f','t','\\',
82 'W','i','n','d','o','w','s','\\',
83 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
84 'I','n','s','t','a','l','l','e','r','\\',
85 'C','o','m','p','o','n','e','n','t','s','\\',
86 '%','s',0};
87
88 static const WCHAR szUninstall_fmt[] = {
89 'S','o','f','t','w','a','r','e','\\',
90 'M','i','c','r','o','s','o','f','t','\\',
91 'W','i','n','d','o','w','s','\\',
92 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
93 'U','n','i','n','s','t','a','l','l','\\',
94 '%','s',0 };
95
96 static const WCHAR szUserProduct_fmt[] = {
97 'S','o','f','t','w','a','r','e','\\',
98 'M','i','c','r','o','s','o','f','t','\\',
99 'I','n','s','t','a','l','l','e','r','\\',
100 'P','r','o','d','u','c','t','s','\\',
101 '%','s',0};
102
103 static const WCHAR szInstaller_Products[] = {
104 'S','o','f','t','w','a','r','e','\\',
105 'M','i','c','r','o','s','o','f','t','\\',
106 'W','i','n','d','o','w','s','\\',
107 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
108 'I','n','s','t','a','l','l','e','r','\\',
109 'P','r','o','d','u','c','t','s',0};
110
111 static const WCHAR szInstaller_Products_fmt[] = {
112 'S','o','f','t','w','a','r','e','\\',
113 'M','i','c','r','o','s','o','f','t','\\',
114 'W','i','n','d','o','w','s','\\',
115 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
116 'I','n','s','t','a','l','l','e','r','\\',
117 'P','r','o','d','u','c','t','s','\\',
118 '%','s',0};
119
120 static const WCHAR szInstaller_UpgradeCodes[] = {
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 'U','p','g','r','a','d','e','C','o','d','e','s',0};
127
128 static const WCHAR szInstaller_UpgradeCodes_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 'U','p','g','r','a','d','e','C','o','d','e','s','\\',
135 '%','s',0};
136
137 BOOL unsquash_guid(LPCWSTR in, LPWSTR out)
138 {
139     DWORD i,n=0;
140
141     out[n++]='{';
142     for(i=0; i<8; i++)
143         out[n++] = in[7-i];
144     out[n++]='-';
145     for(i=0; i<4; i++)
146         out[n++] = in[11-i];
147     out[n++]='-';
148     for(i=0; i<4; i++)
149         out[n++] = in[15-i];
150     out[n++]='-';
151     for(i=0; i<2; i++)
152     {
153         out[n++] = in[17+i*2];
154         out[n++] = in[16+i*2];
155     }
156     out[n++]='-';
157     for( ; i<8; i++)
158     {
159         out[n++] = in[17+i*2];
160         out[n++] = in[16+i*2];
161     }
162     out[n++]='}';
163     out[n]=0;
164     return TRUE;
165 }
166
167 BOOL squash_guid(LPCWSTR in, LPWSTR out)
168 {
169     DWORD i,n=0;
170
171     if(in[n++] != '{')
172         return FALSE;
173     for(i=0; i<8; i++)
174         out[7-i] = in[n++];
175     if(in[n++] != '-')
176         return FALSE;
177     for(i=0; i<4; i++)
178         out[11-i] = in[n++];
179     if(in[n++] != '-')
180         return FALSE;
181     for(i=0; i<4; i++)
182         out[15-i] = in[n++];
183     if(in[n++] != '-')
184         return FALSE;
185     for(i=0; i<2; i++)
186     {
187         out[17+i*2] = in[n++];
188         out[16+i*2] = in[n++];
189     }
190     if(in[n++] != '-')
191         return FALSE;
192     for( ; i<8; i++)
193     {
194         out[17+i*2] = in[n++];
195         out[16+i*2] = in[n++];
196     }
197     out[32]=0;
198     if(in[n++] != '}')
199         return FALSE;
200     if(in[n])
201         return FALSE;
202     return TRUE;
203 }
204
205
206 /* tables for encoding and decoding base85 */
207 static const unsigned char table_dec85[0x80] = {
208 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
209 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
210 0xff,0x00,0xff,0xff,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0xff,
211 0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0xff,0xff,0xff,0x16,0xff,0x17,
212 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,
213 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x32,0x33,0xff,0x34,0x35,0x36,
214 0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x40,0x41,0x42,0x43,0x44,0x45,0x46,
215 0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x50,0x51,0x52,0xff,0x53,0x54,0xff,
216 };
217
218 static const char table_enc85[] =
219 "!$%&'()*+,-.0123456789=?@ABCDEFGHIJKLMNO"
220 "PQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwx"
221 "yz{}~";
222
223 /*
224  *  Converts a base85 encoded guid into a GUID pointer
225  *  Base85 encoded GUIDs should be 20 characters long.
226  *
227  *  returns TRUE if successful, FALSE if not
228  */
229 BOOL decode_base85_guid( LPCWSTR str, GUID *guid )
230 {
231     DWORD i, val = 0, base = 1, *p;
232
233     p = (DWORD*) guid;
234     for( i=0; i<20; i++ )
235     {
236         if( (i%5) == 0 )
237         {
238             val = 0;
239             base = 1;
240         }
241         val += table_dec85[str[i]] * base;
242         if( str[i] >= 0x80 )
243             return FALSE;
244         if( table_dec85[str[i]] == 0xff )
245             return FALSE;
246         if( (i%5) == 4 )
247             p[i/5] = val;
248         base *= 85;
249     }
250     return TRUE;
251 }
252
253 /*
254  *  Encodes a base85 guid given a GUID pointer
255  *  Caller should provide a 21 character buffer for the encoded string.
256  *
257  *  returns TRUE if successful, FALSE if not
258  */
259 BOOL encode_base85_guid( GUID *guid, LPWSTR str )
260 {
261     unsigned int x, *p, i;
262
263     p = (unsigned int*) guid;
264     for( i=0; i<4; i++ )
265     {
266         x = p[i];
267         *str++ = table_enc85[x%85];
268         x = x/85;
269         *str++ = table_enc85[x%85];
270         x = x/85;
271         *str++ = table_enc85[x%85];
272         x = x/85;
273         *str++ = table_enc85[x%85];
274         x = x/85;
275         *str++ = table_enc85[x%85];
276     }
277     *str = 0;
278
279     return TRUE;
280 }
281
282
283 UINT MSIREG_OpenUninstallKey(LPCWSTR szProduct, HKEY* key, BOOL create)
284 {
285     UINT rc;
286     WCHAR keypath[0x200];
287     TRACE("%s\n",debugstr_w(szProduct));
288
289     sprintfW(keypath,szUninstall_fmt,szProduct);
290
291     if (create)
292         rc = RegCreateKeyW(HKEY_LOCAL_MACHINE, keypath, key);
293     else
294         rc = RegOpenKeyW(HKEY_LOCAL_MACHINE, keypath, key);
295
296     return rc;
297 }
298
299 UINT MSIREG_OpenUserProductsKey(LPCWSTR szProduct, HKEY* key, BOOL create)
300 {
301     UINT rc;
302     WCHAR squished_pc[GUID_SIZE];
303     WCHAR keypath[0x200];
304
305     TRACE("%s\n",debugstr_w(szProduct));
306     squash_guid(szProduct,squished_pc);
307     TRACE("squished (%s)\n", debugstr_w(squished_pc));
308
309     sprintfW(keypath,szUserProduct_fmt,squished_pc);
310
311     if (create)
312         rc = RegCreateKeyW(HKEY_CURRENT_USER,keypath,key);
313     else
314         rc = RegOpenKeyW(HKEY_CURRENT_USER,keypath,key);
315
316     return rc;
317 }
318
319 UINT MSIREG_OpenUserFeaturesKey(LPCWSTR szProduct, HKEY* key, BOOL create)
320 {
321     UINT rc;
322     WCHAR squished_pc[GUID_SIZE];
323     WCHAR keypath[0x200];
324
325     TRACE("%s\n",debugstr_w(szProduct));
326     squash_guid(szProduct,squished_pc);
327     TRACE("squished (%s)\n", debugstr_w(squished_pc));
328
329     sprintfW(keypath,szUserFeatures_fmt,squished_pc);
330
331     if (create)
332         rc = RegCreateKeyW(HKEY_CURRENT_USER,keypath,key);
333     else
334         rc = RegOpenKeyW(HKEY_CURRENT_USER,keypath,key);
335
336     return rc;
337 }
338
339 UINT MSIREG_OpenFeatures(HKEY* key)
340 {
341     return RegCreateKeyW(HKEY_LOCAL_MACHINE,szInstaller_Features,key);
342 }
343
344 UINT MSIREG_OpenFeaturesKey(LPCWSTR szProduct, HKEY* key, BOOL create)
345 {
346     UINT rc;
347     WCHAR squished_pc[GUID_SIZE];
348     WCHAR keypath[0x200];
349
350     TRACE("%s\n",debugstr_w(szProduct));
351     squash_guid(szProduct,squished_pc);
352     TRACE("squished (%s)\n", debugstr_w(squished_pc));
353
354     sprintfW(keypath,szInstaller_Features_fmt,squished_pc);
355
356     if (create)
357         rc = RegCreateKeyW(HKEY_LOCAL_MACHINE,keypath,key);
358     else
359         rc = RegOpenKeyW(HKEY_LOCAL_MACHINE,keypath,key);
360
361     return rc;
362 }
363
364 UINT MSIREG_OpenComponents(HKEY* key)
365 {
366     return RegCreateKeyW(HKEY_LOCAL_MACHINE,szInstaller_Components,key);
367 }
368
369 UINT MSIREG_OpenComponentsKey(LPCWSTR szComponent, HKEY* key, BOOL create)
370 {
371     UINT rc;
372     WCHAR squished_cc[GUID_SIZE];
373     WCHAR keypath[0x200];
374
375     TRACE("%s\n",debugstr_w(szComponent));
376     squash_guid(szComponent,squished_cc);
377     TRACE("squished (%s)\n", debugstr_w(squished_cc));
378
379     sprintfW(keypath,szInstaller_Components_fmt,squished_cc);
380
381     if (create)
382         rc = RegCreateKeyW(HKEY_LOCAL_MACHINE,keypath,key);
383     else
384         rc = RegOpenKeyW(HKEY_LOCAL_MACHINE,keypath,key);
385
386     return rc;
387 }
388
389 UINT MSIREG_OpenProductsKey(LPCWSTR szProduct, HKEY* key, BOOL create)
390 {
391     UINT rc;
392     WCHAR squished_pc[GUID_SIZE];
393     WCHAR keypath[0x200];
394
395     TRACE("%s\n",debugstr_w(szProduct));
396     squash_guid(szProduct,squished_pc);
397     TRACE("squished (%s)\n", debugstr_w(squished_pc));
398
399     sprintfW(keypath,szInstaller_Products_fmt,squished_pc);
400
401     if (create)
402         rc = RegCreateKeyW(HKEY_LOCAL_MACHINE,keypath,key);
403     else
404         rc = RegOpenKeyW(HKEY_LOCAL_MACHINE,keypath,key);
405
406     return rc;
407 }
408
409 UINT MSIREG_OpenUpgradeCodesKey(LPCWSTR szUpgradeCode, HKEY* key, BOOL create)
410 {
411     UINT rc;
412     WCHAR squished_pc[GUID_SIZE];
413     WCHAR keypath[0x200];
414
415     TRACE("%s\n",debugstr_w(szUpgradeCode));
416     squash_guid(szUpgradeCode,squished_pc);
417     TRACE("squished (%s)\n", debugstr_w(squished_pc));
418
419     sprintfW(keypath,szInstaller_UpgradeCodes_fmt,squished_pc);
420
421     if (create)
422         rc = RegCreateKeyW(HKEY_LOCAL_MACHINE,keypath,key);
423     else
424         rc = RegOpenKeyW(HKEY_LOCAL_MACHINE,keypath,key);
425
426     return rc;
427 }
428
429 /*************************************************************************
430  *  MsiDecomposeDescriptorW   [MSI.@]
431  *
432  * Decomposes an MSI descriptor into product, feature and component parts.
433  * An MSI descriptor is a string of the form:
434  *   [base 85 guid] [feature code] '>' [base 85 guid]
435  *
436  * PARAMS
437  *   szDescriptor  [I]  the descriptor to decompose
438  *   szProduct     [O]  buffer of MAX_FEATURE_CHARS for the product guid
439  *   szFeature     [O]  buffer of MAX_FEATURE_CHARS for the feature code
440  *   szComponent   [O]  buffer of MAX_FEATURE_CHARS for the component guid
441  *   pUsed         [O]  the length of the descriptor
442  *
443  * RETURNS
444  *   ERROR_SUCCESS             if everything worked correctly
445  *   ERROR_INVALID_PARAMETER   if the descriptor was invalid
446  *
447  */
448 UINT WINAPI MsiDecomposeDescriptorW( LPCWSTR szDescriptor, LPWSTR szProduct,
449                 LPWSTR szFeature, LPWSTR szComponent, DWORD *pUsed )
450 {
451     UINT r, len;
452     LPWSTR p;
453     GUID product, component;
454
455     TRACE("%s %p %p %p %p\n", debugstr_w(szDescriptor), szProduct,
456           szFeature, szComponent, pUsed);
457
458     r = decode_base85_guid( szDescriptor, &product );
459     if( !r )
460         return ERROR_INVALID_PARAMETER;
461
462     TRACE("product %s\n", debugstr_guid( &product ));
463
464     p = strchrW(&szDescriptor[20],'>');
465     if( !p )
466         return ERROR_INVALID_PARAMETER;
467
468     len = (p - &szDescriptor[20]);
469     if( len > MAX_FEATURE_CHARS )
470         return ERROR_INVALID_PARAMETER;
471     memcpy( szFeature, &szDescriptor[20], len*sizeof(WCHAR) );
472     szFeature[len] = 0;
473
474     TRACE("feature %s\n", debugstr_w( szFeature ));
475
476     r = decode_base85_guid( p+1, &component );
477     if( !r )
478         return ERROR_INVALID_PARAMETER;
479
480     TRACE("component %s\n", debugstr_guid( &component ));
481
482     StringFromGUID2( &product, szProduct, MAX_FEATURE_CHARS+1 );
483     StringFromGUID2( &component, szComponent, MAX_FEATURE_CHARS+1 );
484     len = ( &p[21] - szDescriptor );
485
486     TRACE("length = %d\n", len);
487     *pUsed = len;
488
489     return ERROR_SUCCESS;
490 }
491
492 UINT WINAPI MsiDecomposeDescriptorA( LPCSTR szDescriptor, LPSTR szProduct,
493                 LPSTR szFeature, LPSTR szComponent, DWORD *pUsed )
494 {
495     WCHAR product[MAX_FEATURE_CHARS+1];
496     WCHAR feature[MAX_FEATURE_CHARS+1];
497     WCHAR component[MAX_FEATURE_CHARS+1];
498     LPWSTR str = NULL;
499     UINT r, len;
500
501     TRACE("%s %p %p %p %p\n", debugstr_a(szDescriptor), szProduct,
502           szFeature, szComponent, pUsed);
503
504     if( szDescriptor )
505     {
506         len = MultiByteToWideChar( CP_ACP, 0, szDescriptor, -1, NULL, 0 );
507         str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
508         MultiByteToWideChar( CP_ACP, 0, szDescriptor, -1, str, len );
509     }
510
511     r = MsiDecomposeDescriptorW( str, product, feature, component, pUsed );
512
513     WideCharToMultiByte( CP_ACP, 0, product, MAX_FEATURE_CHARS+1,
514                          szProduct, MAX_FEATURE_CHARS+1, NULL, NULL );
515     WideCharToMultiByte( CP_ACP, 0, feature, MAX_FEATURE_CHARS+1,
516                          szFeature, MAX_FEATURE_CHARS+1, NULL, NULL );
517     WideCharToMultiByte( CP_ACP, 0, component, MAX_FEATURE_CHARS+1,
518                          szComponent, MAX_FEATURE_CHARS+1, NULL, NULL );
519
520     HeapFree( GetProcessHeap(), 0, str );
521
522     return r;
523 }
524
525 UINT WINAPI MsiEnumProductsA(DWORD index, LPSTR lpguid)
526 {
527     DWORD r;
528     WCHAR szwGuid[GUID_SIZE];
529
530     TRACE("%ld %p\n",index,lpguid);
531     
532     if (NULL == lpguid)
533         return ERROR_INVALID_PARAMETER;
534     r = MsiEnumProductsW(index, szwGuid);
535     if( r == ERROR_SUCCESS )
536         WideCharToMultiByte(CP_ACP, 0, szwGuid, -1, lpguid, GUID_SIZE, NULL, NULL);
537
538     return r;
539 }
540
541 UINT WINAPI MsiEnumProductsW(DWORD index, LPWSTR lpguid)
542 {
543     HKEY hkeyFeatures = 0;
544     DWORD r;
545     WCHAR szKeyName[33];
546
547     TRACE("%ld %p\n",index,lpguid);
548
549     if (NULL == lpguid)
550         return ERROR_INVALID_PARAMETER;
551
552     r = MSIREG_OpenFeatures(&hkeyFeatures);
553     if( r != ERROR_SUCCESS )
554         goto end;
555
556     r = RegEnumKeyW(hkeyFeatures, index, szKeyName, GUID_SIZE);
557
558     unsquash_guid(szKeyName, lpguid);
559
560 end:
561
562     if( hkeyFeatures )
563         RegCloseKey(hkeyFeatures);
564
565     return r;
566 }
567
568 UINT WINAPI MsiEnumFeaturesA(LPCSTR szProduct, DWORD index, 
569       LPSTR szFeature, LPSTR szParent)
570 {
571     DWORD r;
572     WCHAR szwFeature[GUID_SIZE], szwParent[GUID_SIZE];
573     LPWSTR szwProduct = NULL;
574
575     TRACE("%s %ld %p %p\n",debugstr_a(szProduct),index,szFeature,szParent);
576
577     if( szProduct )
578     {
579         UINT len = MultiByteToWideChar( CP_ACP, 0, szProduct, -1, NULL, 0 );
580         szwProduct = HeapAlloc( GetProcessHeap(), 0, len * sizeof (WCHAR) );
581         if( szwProduct )
582             MultiByteToWideChar( CP_ACP, 0, szProduct, -1, szwProduct, len );
583         else
584             return ERROR_FUNCTION_FAILED;
585     }
586
587     r = MsiEnumFeaturesW(szwProduct, index, szwFeature, szwParent);
588     if( r == ERROR_SUCCESS )
589     {
590         WideCharToMultiByte(CP_ACP, 0, szwFeature, -1,
591                             szFeature, GUID_SIZE, NULL, NULL);
592         WideCharToMultiByte(CP_ACP, 0, szwParent, -1,
593                             szParent, GUID_SIZE, NULL, NULL);
594     }
595
596     HeapFree( GetProcessHeap(), 0, szwProduct);
597
598     return r;
599 }
600
601 UINT WINAPI MsiEnumFeaturesW(LPCWSTR szProduct, DWORD index, 
602       LPWSTR szFeature, LPWSTR szParent)
603 {
604     HKEY hkeyProduct = 0;
605     DWORD r, sz;
606
607     TRACE("%s %ld %p %p\n",debugstr_w(szProduct),index,szFeature,szParent);
608
609     r = MSIREG_OpenFeaturesKey(szProduct,&hkeyProduct,FALSE);
610     if( r != ERROR_SUCCESS )
611         goto end;
612
613     sz = GUID_SIZE;
614     r = RegEnumValueW(hkeyProduct, index, szFeature, &sz, NULL, NULL, NULL, NULL);
615
616 end:
617     if( hkeyProduct )
618         RegCloseKey(hkeyProduct);
619
620     return r;
621 }
622
623 UINT WINAPI MsiEnumComponentsA(DWORD index, LPSTR lpguid)
624 {
625     DWORD r;
626     WCHAR szwGuid[GUID_SIZE];
627
628     TRACE("%ld %p\n",index,lpguid);
629
630     r = MsiEnumComponentsW(index, szwGuid);
631     if( r == ERROR_SUCCESS )
632         WideCharToMultiByte(CP_ACP, 0, szwGuid, -1, lpguid, GUID_SIZE, NULL, NULL);
633
634     return r;
635 }
636
637 UINT WINAPI MsiEnumComponentsW(DWORD index, LPWSTR lpguid)
638 {
639     HKEY hkeyComponents = 0;
640     DWORD r;
641     WCHAR szKeyName[33];
642
643     TRACE("%ld %p\n",index,lpguid);
644
645     r = MSIREG_OpenComponents(&hkeyComponents);
646     if( r != ERROR_SUCCESS )
647         goto end;
648
649     r = RegEnumKeyW(hkeyComponents, index, szKeyName, GUID_SIZE);
650
651     unsquash_guid(szKeyName, lpguid);
652
653 end:
654
655     if( hkeyComponents )
656         RegCloseKey(hkeyComponents);
657
658     return r;
659 }
660
661 UINT WINAPI MsiEnumClientsA(LPCSTR szComponent, DWORD index, LPSTR szProduct)
662 {
663     DWORD r;
664     WCHAR szwProduct[GUID_SIZE];
665     LPWSTR szwComponent = NULL;
666
667     TRACE("%s %ld %p\n",debugstr_a(szComponent),index,szProduct);
668
669     if( szComponent )
670     {
671         UINT len = MultiByteToWideChar( CP_ACP, 0, szComponent, -1, NULL, 0 );
672         szwComponent = HeapAlloc( GetProcessHeap(), 0, len * sizeof (WCHAR) );
673         if( szwComponent )
674             MultiByteToWideChar( CP_ACP, 0, szComponent, -1, szwComponent, len );
675         else
676             return ERROR_FUNCTION_FAILED;
677     }
678
679     r = MsiEnumClientsW(szComponent?szwComponent:NULL, index, szwProduct);
680     if( r == ERROR_SUCCESS )
681     {
682         WideCharToMultiByte(CP_ACP, 0, szwProduct, -1,
683                             szProduct, GUID_SIZE, NULL, NULL);
684     }
685
686     HeapFree( GetProcessHeap(), 0, szwComponent);
687
688     return r;
689 }
690
691 UINT WINAPI MsiEnumClientsW(LPCWSTR szComponent, DWORD index, LPWSTR szProduct)
692 {
693     HKEY hkeyComp = 0;
694     DWORD r, sz;
695     WCHAR szValName[GUID_SIZE];
696
697     TRACE("%s %ld %p\n",debugstr_w(szComponent),index,szProduct);
698
699     r = MSIREG_OpenComponentsKey(szComponent,&hkeyComp,FALSE);
700     if( r != ERROR_SUCCESS )
701         goto end;
702
703     sz = GUID_SIZE;
704     r = RegEnumValueW(hkeyComp, index, szValName, &sz, NULL, NULL, NULL, NULL);
705     if( r != ERROR_SUCCESS )
706         goto end;
707
708     unsquash_guid(szValName, szProduct);
709
710 end:
711     if( hkeyComp )
712         RegCloseKey(hkeyComp);
713
714     return r;
715 }
716
717 UINT WINAPI MsiEnumComponentQualifiersA( LPSTR szComponent, DWORD iIndex,
718                 LPSTR lpQualifierBuf, DWORD* pcchQualifierBuf,
719                 LPSTR lpApplicationDataBuf, DWORD* pcchApplicationDataBuf)
720 {
721     FIXME("%s %08lx %p %p %p %p\n", debugstr_a(szComponent), iIndex,
722           lpQualifierBuf, pcchQualifierBuf, lpApplicationDataBuf,
723           pcchApplicationDataBuf);
724     return ERROR_CALL_NOT_IMPLEMENTED;
725 }
726
727 UINT WINAPI MsiEnumComponentQualifiersW( LPWSTR szComponent, DWORD iIndex,
728                 LPWSTR lpQualifierBuf, DWORD* pcchQualifierBuf,
729                 LPWSTR lpApplicationDataBuf, DWORD* pcchApplicationDataBuf )
730 {
731     FIXME("%s %08lx %p %p %p %p\n", debugstr_w(szComponent), iIndex,
732           lpQualifierBuf, pcchQualifierBuf, lpApplicationDataBuf,
733           pcchApplicationDataBuf);
734     return ERROR_CALL_NOT_IMPLEMENTED;
735 }
736
737 UINT WINAPI MsiEnumRelatedProductsW(LPCWSTR szUpgradeCode, DWORD dwReserved,
738                                     DWORD iProductIndex, LPWSTR lpProductBuf)
739 {
740     UINT rc;
741     HKEY hkey;
742     WCHAR szKeyName[33];
743
744     TRACE("%s %lu %lu %p\n", debugstr_w(szUpgradeCode), dwReserved,
745           iProductIndex, lpProductBuf);
746
747     if (NULL == szUpgradeCode)
748         return ERROR_INVALID_PARAMETER;
749     if (NULL == lpProductBuf)
750         return ERROR_INVALID_PARAMETER;
751     rc = MSIREG_OpenUpgradeCodesKey(szUpgradeCode, &hkey, FALSE);
752     if (rc != ERROR_SUCCESS)
753     {
754         rc = ERROR_NO_MORE_ITEMS;
755         goto end;
756     }
757
758     rc = RegEnumKeyW(hkey, iProductIndex, szKeyName,
759      sizeof(szKeyName) / sizeof(szKeyName[0]));
760
761     unsquash_guid(szKeyName, lpProductBuf);
762     RegCloseKey(hkey);
763
764 end:
765     return rc;
766 }
767
768 UINT WINAPI MsiEnumRelatedProductsA(LPCSTR szUpgradeCode, DWORD dwReserved,
769                                     DWORD iProductIndex, LPSTR lpProductBuf)
770 {
771     UINT rc;
772     int len;
773     LPWSTR szUpgradeCodeW = NULL;
774
775     TRACE("%s %lu %lu %p\n", debugstr_a(szUpgradeCode), dwReserved,
776           iProductIndex, lpProductBuf);
777     if (!szUpgradeCode)
778         return ERROR_INVALID_PARAMETER;
779     len = MultiByteToWideChar(CP_ACP, 0, szUpgradeCode, -1, NULL, 0);
780     szUpgradeCodeW = (LPWSTR)HeapAlloc(GetProcessHeap(), 0,
781      len * sizeof(WCHAR));
782     if (szUpgradeCodeW)
783     {
784         WCHAR productW[39];
785
786         MultiByteToWideChar(CP_ACP, 0, szUpgradeCode, -1, szUpgradeCodeW, len);
787         rc = MsiEnumRelatedProductsW(szUpgradeCodeW, dwReserved,
788          iProductIndex, productW);
789         if (rc == ERROR_SUCCESS)
790         {
791             LPWSTR ptr;
792
793             for (ptr = productW; *ptr; )
794                 *lpProductBuf++ = *ptr++;
795         }
796         HeapFree(GetProcessHeap(), 0, szUpgradeCodeW);
797     }
798     else
799         rc = ERROR_OUTOFMEMORY;
800     return rc;
801 }