Added CSIDL_MYVIDEO|MYPICTURES|MYMUSIC to _SHRegisterUserShellFolders.
[wine] / dlls / advapi32 / registry.c
1 /*
2  * Registry management
3  *
4  * Copyright (C) 1999 Alexandre Julliard
5  *
6  * Based on misc/registry.c code
7  * Copyright (C) 1996 Marcus Meissner
8  * Copyright (C) 1998 Matthew Becker
9  * Copyright (C) 1999 Sylvain St-Germain
10  *
11  * This file is concerned about handle management and interaction with the Wine server.
12  * Registry file I/O is in misc/registry.c.
13  *
14  * This library is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU Lesser General Public
16  * License as published by the Free Software Foundation; either
17  * version 2.1 of the License, or (at your option) any later version.
18  *
19  * This library is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22  * Lesser General Public License for more details.
23  *
24  * You should have received a copy of the GNU Lesser General Public
25  * License along with this library; if not, write to the Free Software
26  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
27  */
28
29 #include <stdlib.h>
30 #include <stdarg.h>
31 #include <stdio.h>
32
33 #include "ntstatus.h"
34 #define WIN32_NO_STATUS
35 #include "windef.h"
36 #include "winbase.h"
37 #include "winreg.h"
38 #include "winerror.h"
39 #include "winternl.h"
40
41 #include "wine/unicode.h"
42 #include "wine/debug.h"
43
44 WINE_DEFAULT_DEBUG_CHANNEL(reg);
45
46 /* allowed bits for access mask */
47 #define KEY_ACCESS_MASK (KEY_ALL_ACCESS | MAXIMUM_ALLOWED)
48
49 #define HKEY_SPECIAL_ROOT_FIRST   HKEY_CLASSES_ROOT
50 #define HKEY_SPECIAL_ROOT_LAST    HKEY_DYN_DATA
51 #define NB_SPECIAL_ROOT_KEYS      ((UINT)HKEY_SPECIAL_ROOT_LAST - (UINT)HKEY_SPECIAL_ROOT_FIRST + 1)
52
53 static HKEY special_root_keys[NB_SPECIAL_ROOT_KEYS];
54
55 static const WCHAR name_CLASSES_ROOT[] =
56     {'M','a','c','h','i','n','e','\\',
57      'S','o','f','t','w','a','r','e','\\',
58      'C','l','a','s','s','e','s',0};
59 static const WCHAR name_LOCAL_MACHINE[] =
60     {'M','a','c','h','i','n','e',0};
61 static const WCHAR name_USERS[] =
62     {'U','s','e','r',0};
63 static const WCHAR name_PERFORMANCE_DATA[] =
64     {'P','e','r','f','D','a','t','a',0};
65 static const WCHAR name_CURRENT_CONFIG[] =
66     {'M','a','c','h','i','n','e','\\',
67      'S','y','s','t','e','m','\\',
68      'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
69      'H','a','r','d','w','a','r','e',' ','P','r','o','f','i','l','e','s','\\',
70      'C','u','r','r','e','n','t',0};
71 static const WCHAR name_DYN_DATA[] =
72     {'D','y','n','D','a','t','a',0};
73
74 #define DECL_STR(key) { sizeof(name_##key)-sizeof(WCHAR), sizeof(name_##key), (LPWSTR)name_##key }
75 static UNICODE_STRING root_key_names[NB_SPECIAL_ROOT_KEYS] =
76 {
77     DECL_STR(CLASSES_ROOT),
78     { 0, 0, NULL },         /* HKEY_CURRENT_USER is determined dynamically */
79     DECL_STR(LOCAL_MACHINE),
80     DECL_STR(USERS),
81     DECL_STR(PERFORMANCE_DATA),
82     DECL_STR(CURRENT_CONFIG),
83     DECL_STR(DYN_DATA)
84 };
85 #undef DECL_STR
86
87
88 /* check if value type needs string conversion (Ansi<->Unicode) */
89 inline static int is_string( DWORD type )
90 {
91     return (type == REG_SZ) || (type == REG_EXPAND_SZ) || (type == REG_MULTI_SZ);
92 }
93
94 /* check if current version is NT or Win95 */
95 inline static int is_version_nt(void)
96 {
97     return !(GetVersion() & 0x80000000);
98 }
99
100 /* create one of the HKEY_* special root keys */
101 static HKEY create_special_root_hkey( HANDLE hkey, DWORD access )
102 {
103     HKEY ret = 0;
104     int idx = (UINT_PTR)hkey - (UINT_PTR)HKEY_SPECIAL_ROOT_FIRST;
105
106     if (hkey == HKEY_CURRENT_USER)
107     {
108         if (RtlOpenCurrentUser( access, &hkey )) return 0;
109         TRACE( "HKEY_CURRENT_USER -> %p\n", hkey );
110     }
111     else
112     {
113         OBJECT_ATTRIBUTES attr;
114
115         attr.Length = sizeof(attr);
116         attr.RootDirectory = 0;
117         attr.ObjectName = &root_key_names[idx];
118         attr.Attributes = 0;
119         attr.SecurityDescriptor = NULL;
120         attr.SecurityQualityOfService = NULL;
121         if (NtCreateKey( &hkey, access, &attr, 0, NULL, 0, NULL )) return 0;
122         TRACE( "%s -> %p\n", debugstr_w(attr.ObjectName->Buffer), hkey );
123     }
124
125     if (!(ret = InterlockedCompareExchangePointer( (void **)&special_root_keys[idx], hkey, 0 )))
126         ret = hkey;
127     else
128         NtClose( hkey );  /* somebody beat us to it */
129     return ret;
130 }
131
132 /* map the hkey from special root to normal key if necessary */
133 inline static HKEY get_special_root_hkey( HKEY hkey )
134 {
135     HKEY ret = hkey;
136
137     if ((hkey >= HKEY_SPECIAL_ROOT_FIRST) && (hkey <= HKEY_SPECIAL_ROOT_LAST))
138     {
139         if (!(ret = special_root_keys[(UINT_PTR)hkey - (UINT_PTR)HKEY_SPECIAL_ROOT_FIRST]))
140             ret = create_special_root_hkey( hkey, KEY_ALL_ACCESS );
141     }
142     return ret;
143 }
144
145
146 /******************************************************************************
147  * RegCreateKeyExW   [ADVAPI32.@]
148  *
149  * See RegCreateKeyExA.
150  */
151 DWORD WINAPI RegCreateKeyExW( HKEY hkey, LPCWSTR name, DWORD reserved, LPWSTR class,
152                               DWORD options, REGSAM access, SECURITY_ATTRIBUTES *sa,
153                               PHKEY retkey, LPDWORD dispos )
154 {
155     OBJECT_ATTRIBUTES attr;
156     UNICODE_STRING nameW, classW;
157
158     if (reserved) return ERROR_INVALID_PARAMETER;
159     if (!(access & KEY_ACCESS_MASK) || (access & ~KEY_ACCESS_MASK)) return ERROR_ACCESS_DENIED;
160     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
161
162     attr.Length = sizeof(attr);
163     attr.RootDirectory = hkey;
164     attr.ObjectName = &nameW;
165     attr.Attributes = 0;
166     attr.SecurityDescriptor = NULL;
167     attr.SecurityQualityOfService = NULL;
168     RtlInitUnicodeString( &nameW, name );
169     RtlInitUnicodeString( &classW, class );
170
171     return RtlNtStatusToDosError( NtCreateKey( (PHANDLE)retkey, access, &attr, 0,
172                                                &classW, options, dispos ) );
173 }
174
175
176 /******************************************************************************
177  * RegCreateKeyExA   [ADVAPI32.@]
178  *
179  * Open a registry key, creating it if it doesn't exist.
180  *
181  * PARAMS
182  *  hkey       [I] Handle of the parent registry key
183  *  name       [I] Name of the new key to open or create
184  *  reserved   [I] Reserved, pass 0
185  *  class      [I] The object type of the new key
186  *  options    [I] Flags controlling the key creation (REG_OPTION_* flags from "winnt.h")
187  *  access     [I] Access level desired
188  *  sa         [I] Security attributes for the key
189  *  retkey     [O] Destination for the resulting handle
190  *  dispos     [O] Receives REG_CREATED_NEW_KEY or REG_OPENED_EXISTING_KEY
191  *
192  * RETURNS
193  *  Success: ERROR_SUCCESS.
194  *  Failure: A standard Win32 error code. retkey remains untouched.
195  *
196  * FIXME
197  *  MAXIMUM_ALLOWED in access mask not supported by server
198  */
199 DWORD WINAPI RegCreateKeyExA( HKEY hkey, LPCSTR name, DWORD reserved, LPSTR class,
200                               DWORD options, REGSAM access, SECURITY_ATTRIBUTES *sa,
201                               PHKEY retkey, LPDWORD dispos )
202 {
203     OBJECT_ATTRIBUTES attr;
204     UNICODE_STRING classW;
205     ANSI_STRING nameA, classA;
206     NTSTATUS status;
207
208     if (reserved) return ERROR_INVALID_PARAMETER;
209     if (!is_version_nt()) access = KEY_ALL_ACCESS;  /* Win95 ignores the access mask */
210     else if (!(access & KEY_ACCESS_MASK) || (access & ~KEY_ACCESS_MASK)) return ERROR_ACCESS_DENIED;
211     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
212
213     attr.Length = sizeof(attr);
214     attr.RootDirectory = hkey;
215     attr.ObjectName = &NtCurrentTeb()->StaticUnicodeString;
216     attr.Attributes = 0;
217     attr.SecurityDescriptor = NULL;
218     attr.SecurityQualityOfService = NULL;
219     RtlInitAnsiString( &nameA, name );
220     RtlInitAnsiString( &classA, class );
221
222     if (!(status = RtlAnsiStringToUnicodeString( &NtCurrentTeb()->StaticUnicodeString,
223                                                  &nameA, FALSE )))
224     {
225         if (!(status = RtlAnsiStringToUnicodeString( &classW, &classA, TRUE )))
226         {
227             status = NtCreateKey( (PHANDLE)retkey, access, &attr, 0, &classW, options, dispos );
228             RtlFreeUnicodeString( &classW );
229         }
230     }
231     return RtlNtStatusToDosError( status );
232 }
233
234
235 /******************************************************************************
236  * RegCreateKeyW   [ADVAPI32.@]
237  *
238  * Creates the specified reg key.
239  *
240  * PARAMS
241  *  hKey      [I] Handle to an open key.
242  *  lpSubKey  [I] Name of a key that will be opened or created.
243  *  phkResult [O] Receives a handle to the opened or created key.
244  *
245  * RETURNS
246  *  Success: ERROR_SUCCESS
247  *  Failure: nonzero error code defined in Winerror.h
248  */
249 DWORD WINAPI RegCreateKeyW( HKEY hkey, LPCWSTR lpSubKey, PHKEY phkResult )
250 {
251     /* FIXME: previous implementation converted ERROR_INVALID_HANDLE to ERROR_BADKEY, */
252     /* but at least my version of NT (4.0 SP5) doesn't do this.  -- AJ */
253     return RegCreateKeyExW( hkey, lpSubKey, 0, NULL, REG_OPTION_NON_VOLATILE,
254                             KEY_ALL_ACCESS, NULL, phkResult, NULL );
255 }
256
257
258 /******************************************************************************
259  * RegCreateKeyA   [ADVAPI32.@]
260  *
261  * See RegCreateKeyW.
262  */
263 DWORD WINAPI RegCreateKeyA( HKEY hkey, LPCSTR lpSubKey, PHKEY phkResult )
264 {
265     return RegCreateKeyExA( hkey, lpSubKey, 0, NULL, REG_OPTION_NON_VOLATILE,
266                             KEY_ALL_ACCESS, NULL, phkResult, NULL );
267 }
268
269
270
271 /******************************************************************************
272  * RegOpenKeyExW   [ADVAPI32.@]
273  * 
274  * See RegOpenKeyExA.
275  */
276 DWORD WINAPI RegOpenKeyExW( HKEY hkey, LPCWSTR name, DWORD reserved, REGSAM access, PHKEY retkey )
277 {
278     OBJECT_ATTRIBUTES attr;
279     UNICODE_STRING nameW;
280
281     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
282
283     attr.Length = sizeof(attr);
284     attr.RootDirectory = hkey;
285     attr.ObjectName = &nameW;
286     attr.Attributes = 0;
287     attr.SecurityDescriptor = NULL;
288     attr.SecurityQualityOfService = NULL;
289     RtlInitUnicodeString( &nameW, name );
290     return RtlNtStatusToDosError( NtOpenKey( (PHANDLE)retkey, access, &attr ) );
291 }
292
293
294 /******************************************************************************
295  * RegOpenKeyExA   [ADVAPI32.@]
296  *
297  * Open a registry key.
298  *
299  * PARAMS
300  *  hkey       [I] Handle of open key
301  *  name       [I] Name of subkey to open
302  *  reserved   [I] Reserved - must be zero
303  *  access     [I] Security access mask
304  *  retkey     [O] Handle to open key
305  *
306  * RETURNS
307  *  Success: ERROR_SUCCESS
308  *  Failure: A standard Win32 error code. retkey is set to 0.
309  *
310  * NOTES
311  *  Unlike RegCreateKeyExA(), this function will not create the key if it
312  *  does not exist.
313  */
314 DWORD WINAPI RegOpenKeyExA( HKEY hkey, LPCSTR name, DWORD reserved, REGSAM access, PHKEY retkey )
315 {
316     OBJECT_ATTRIBUTES attr;
317     STRING nameA;
318     NTSTATUS status;
319
320     if (!is_version_nt()) access = KEY_ALL_ACCESS;  /* Win95 ignores the access mask */
321
322     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
323
324     attr.Length = sizeof(attr);
325     attr.RootDirectory = hkey;
326     attr.ObjectName = &NtCurrentTeb()->StaticUnicodeString;
327     attr.Attributes = 0;
328     attr.SecurityDescriptor = NULL;
329     attr.SecurityQualityOfService = NULL;
330
331     RtlInitAnsiString( &nameA, name );
332     if (!(status = RtlAnsiStringToUnicodeString( &NtCurrentTeb()->StaticUnicodeString,
333                                                  &nameA, FALSE )))
334     {
335         status = NtOpenKey( (PHANDLE)retkey, access, &attr );
336     }
337     return RtlNtStatusToDosError( status );
338 }
339
340
341 /******************************************************************************
342  * RegOpenKeyW   [ADVAPI32.@]
343  *
344  * See RegOpenKeyA.
345  */
346 DWORD WINAPI RegOpenKeyW( HKEY hkey, LPCWSTR name, PHKEY retkey )
347 {
348     if (!name || !*name)
349     {
350         *retkey = hkey;
351         return ERROR_SUCCESS;
352     }
353     return RegOpenKeyExW( hkey, name, 0, KEY_ALL_ACCESS, retkey );
354 }
355
356
357 /******************************************************************************
358  * RegOpenKeyA   [ADVAPI32.@]
359  *           
360  * Open a registry key.
361  *
362  * PARAMS
363  *  hkey    [I] Handle of parent key to open the new key under
364  *  name    [I] Name of the key under hkey to open
365  *  retkey  [O] Destination for the resulting Handle
366  *
367  * RETURNS
368  *  Success: ERROR_SUCCESS
369  *  Failure: A standard Win32 error code. retkey is set to 0.
370  */
371 DWORD WINAPI RegOpenKeyA( HKEY hkey, LPCSTR name, PHKEY retkey )
372 {
373     if (!name || !*name)
374     {
375         *retkey = hkey;
376         return ERROR_SUCCESS;
377     }
378     return RegOpenKeyExA( hkey, name, 0, KEY_ALL_ACCESS, retkey );
379 }
380
381
382 /******************************************************************************
383  * RegOpenCurrentUser   [ADVAPI32.@]
384  *
385  * Get a handle to the HKEY_CURRENT_USER key for the user 
386  * the current thread is impersonating.
387  *
388  * PARAMS
389  *  access [I] Desired access rights to the key
390  *  retkey [O] Handle to the opened key
391  *
392  * RETURNS
393  *  Success: ERROR_SUCCESS
394  *  Failure: nonzero error code from Winerror.h
395  *
396  * FIXME
397  *  This function is supposed to retrieve a handle to the
398  *  HKEY_CURRENT_USER for the user the current thread is impersonating.
399  *  Since Wine does not currently allow threads to impersonate other users,
400  *  this stub should work fine.
401  */
402 DWORD WINAPI RegOpenCurrentUser( REGSAM access, PHKEY retkey )
403 {
404     return RegOpenKeyExA( HKEY_CURRENT_USER, "", 0, access, retkey );
405 }
406
407
408
409 /******************************************************************************
410  * RegEnumKeyExW   [ADVAPI32.@]
411  *
412  * Enumerate subkeys of the specified open registry key.
413  *
414  * PARAMS
415  *  hkey         [I] Handle to key to enumerate
416  *  index        [I] Index of subkey to enumerate
417  *  name         [O] Buffer for subkey name
418  *  name_len     [O] Size of subkey buffer
419  *  reserved     [I] Reserved
420  *  class        [O] Buffer for class string
421  *  class_len    [O] Size of class buffer
422  *  ft           [O] Time key last written to
423  *
424  * RETURNS
425  *  Success: ERROR_SUCCESS
426  *  Failure: System error code. If there are no more subkeys available, the
427  *           function returns ERROR_NO_MORE_ITEMS.
428  */
429 DWORD WINAPI RegEnumKeyExW( HKEY hkey, DWORD index, LPWSTR name, LPDWORD name_len,
430                             LPDWORD reserved, LPWSTR class, LPDWORD class_len, FILETIME *ft )
431 {
432     NTSTATUS status;
433     char buffer[256], *buf_ptr = buffer;
434     KEY_NODE_INFORMATION *info = (KEY_NODE_INFORMATION *)buffer;
435     DWORD total_size;
436
437     TRACE( "(%p,%ld,%p,%p(%ld),%p,%p,%p,%p)\n", hkey, index, name, name_len,
438            name_len ? *name_len : -1, reserved, class, class_len, ft );
439
440     if (reserved) return ERROR_INVALID_PARAMETER;
441     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
442
443     status = NtEnumerateKey( hkey, index, KeyNodeInformation,
444                              buffer, sizeof(buffer), &total_size );
445
446     while (status == STATUS_BUFFER_OVERFLOW)
447     {
448         /* retry with a dynamically allocated buffer */
449         if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
450         if (!(buf_ptr = HeapAlloc( GetProcessHeap(), 0, total_size )))
451             return ERROR_NOT_ENOUGH_MEMORY;
452         info = (KEY_NODE_INFORMATION *)buf_ptr;
453         status = NtEnumerateKey( hkey, index, KeyNodeInformation,
454                                  buf_ptr, total_size, &total_size );
455     }
456
457     if (!status)
458     {
459         DWORD len = info->NameLength / sizeof(WCHAR);
460         DWORD cls_len = info->ClassLength / sizeof(WCHAR);
461
462         if (ft) *ft = *(FILETIME *)&info->LastWriteTime;
463
464         if (len >= *name_len || (class && class_len && (cls_len >= *class_len)))
465             status = STATUS_BUFFER_OVERFLOW;
466         else
467         {
468             *name_len = len;
469             memcpy( name, info->Name, info->NameLength );
470             name[len] = 0;
471             if (class_len)
472             {
473                 *class_len = cls_len;
474                 if (class)
475                 {
476                     memcpy( class, buf_ptr + info->ClassOffset, info->ClassLength );
477                     class[cls_len] = 0;
478                 }
479             }
480         }
481     }
482
483     if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
484     return RtlNtStatusToDosError( status );
485 }
486
487
488 /******************************************************************************
489  * RegEnumKeyExA   [ADVAPI32.@]
490  *
491  * See RegEnumKeyExW.
492  */
493 DWORD WINAPI RegEnumKeyExA( HKEY hkey, DWORD index, LPSTR name, LPDWORD name_len,
494                             LPDWORD reserved, LPSTR class, LPDWORD class_len, FILETIME *ft )
495 {
496     NTSTATUS status;
497     char buffer[256], *buf_ptr = buffer;
498     KEY_NODE_INFORMATION *info = (KEY_NODE_INFORMATION *)buffer;
499     DWORD total_size;
500
501     TRACE( "(%p,%ld,%p,%p(%ld),%p,%p,%p,%p)\n", hkey, index, name, name_len,
502            name_len ? *name_len : -1, reserved, class, class_len, ft );
503
504     if (reserved) return ERROR_INVALID_PARAMETER;
505     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
506
507     status = NtEnumerateKey( hkey, index, KeyNodeInformation,
508                              buffer, sizeof(buffer), &total_size );
509
510     while (status == STATUS_BUFFER_OVERFLOW)
511     {
512         /* retry with a dynamically allocated buffer */
513         if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
514         if (!(buf_ptr = HeapAlloc( GetProcessHeap(), 0, total_size )))
515             return ERROR_NOT_ENOUGH_MEMORY;
516         info = (KEY_NODE_INFORMATION *)buf_ptr;
517         status = NtEnumerateKey( hkey, index, KeyNodeInformation,
518                                  buf_ptr, total_size, &total_size );
519     }
520
521     if (!status)
522     {
523         DWORD len, cls_len;
524
525         RtlUnicodeToMultiByteSize( &len, info->Name, info->NameLength );
526         RtlUnicodeToMultiByteSize( &cls_len, (WCHAR *)(buf_ptr + info->ClassOffset),
527                                    info->ClassLength );
528         if (ft) *ft = *(FILETIME *)&info->LastWriteTime;
529
530         if (len >= *name_len || (class && class_len && (cls_len >= *class_len)))
531             status = STATUS_BUFFER_OVERFLOW;
532         else
533         {
534             *name_len = len;
535             RtlUnicodeToMultiByteN( name, len, NULL, info->Name, info->NameLength );
536             name[len] = 0;
537             if (class_len)
538             {
539                 *class_len = cls_len;
540                 if (class)
541                 {
542                     RtlUnicodeToMultiByteN( class, cls_len, NULL,
543                                             (WCHAR *)(buf_ptr + info->ClassOffset),
544                                             info->ClassLength );
545                     class[cls_len] = 0;
546                 }
547             }
548         }
549     }
550
551     if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
552     return RtlNtStatusToDosError( status );
553 }
554
555
556 /******************************************************************************
557  * RegEnumKeyW   [ADVAPI32.@]
558  *
559  * Enumerates subkyes of the specified open reg key.
560  *
561  * PARAMS
562  *  hKey    [I] Handle to an open key.
563  *  dwIndex [I] Index of the subkey of hKey to retrieve.
564  *  lpName  [O] Name of the subkey.
565  *  cchName [I] Size of lpName in TCHARS.
566  *
567  * RETURNS
568  *  Success: ERROR_SUCCESS
569  *  Failure: system error code. If there are no more subkeys available, the
570  *           function returns ERROR_NO_MORE_ITEMS.
571  */
572 DWORD WINAPI RegEnumKeyW( HKEY hkey, DWORD index, LPWSTR name, DWORD name_len )
573 {
574     return RegEnumKeyExW( hkey, index, name, &name_len, NULL, NULL, NULL, NULL );
575 }
576
577
578 /******************************************************************************
579  * RegEnumKeyA   [ADVAPI32.@]
580  *
581  * See RegEnumKeyW.
582  */
583 DWORD WINAPI RegEnumKeyA( HKEY hkey, DWORD index, LPSTR name, DWORD name_len )
584 {
585     return RegEnumKeyExA( hkey, index, name, &name_len, NULL, NULL, NULL, NULL );
586 }
587
588
589 /******************************************************************************
590  * RegQueryInfoKeyW   [ADVAPI32.@]
591  *
592  * Retrieves information about the specified registry key.
593  *
594  * PARAMS
595  *    hkey       [I] Handle to key to query
596  *    class      [O] Buffer for class string
597  *    class_len  [O] Size of class string buffer
598  *    reserved   [I] Reserved
599  *    subkeys    [O] Buffer for number of subkeys
600  *    max_subkey [O] Buffer for longest subkey name length
601  *    max_class  [O] Buffer for longest class string length
602  *    values     [O] Buffer for number of value entries
603  *    max_value  [O] Buffer for longest value name length
604  *    max_data   [O] Buffer for longest value data length
605  *    security   [O] Buffer for security descriptor length
606  *    modif      [O] Modification time
607  *
608  * RETURNS
609  *  Success: ERROR_SUCCESS
610  *  Failure: system error code.
611  *
612  * NOTES
613  *  - win95 allows class to be valid and class_len to be NULL
614  *  - winnt returns ERROR_INVALID_PARAMETER if class is valid and class_len is NULL
615  *  - both allow class to be NULL and class_len to be NULL
616  *    (it's hard to test validity, so test !NULL instead)
617  */
618 DWORD WINAPI RegQueryInfoKeyW( HKEY hkey, LPWSTR class, LPDWORD class_len, LPDWORD reserved,
619                                LPDWORD subkeys, LPDWORD max_subkey, LPDWORD max_class,
620                                LPDWORD values, LPDWORD max_value, LPDWORD max_data,
621                                LPDWORD security, FILETIME *modif )
622 {
623     NTSTATUS status;
624     char buffer[256], *buf_ptr = buffer;
625     KEY_FULL_INFORMATION *info = (KEY_FULL_INFORMATION *)buffer;
626     DWORD total_size;
627
628     TRACE( "(%p,%p,%ld,%p,%p,%p,%p,%p,%p,%p,%p)\n", hkey, class, class_len ? *class_len : 0,
629            reserved, subkeys, max_subkey, values, max_value, max_data, security, modif );
630
631     if (class && !class_len && is_version_nt()) return ERROR_INVALID_PARAMETER;
632     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
633
634     status = NtQueryKey( hkey, KeyFullInformation, buffer, sizeof(buffer), &total_size );
635     if (status && status != STATUS_BUFFER_OVERFLOW) goto done;
636
637     if (class)
638     {
639         /* retry with a dynamically allocated buffer */
640         while (status == STATUS_BUFFER_OVERFLOW)
641         {
642             if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
643             if (!(buf_ptr = HeapAlloc( GetProcessHeap(), 0, total_size )))
644                 return ERROR_NOT_ENOUGH_MEMORY;
645             info = (KEY_FULL_INFORMATION *)buf_ptr;
646             status = NtQueryKey( hkey, KeyFullInformation, buf_ptr, total_size, &total_size );
647         }
648
649         if (status) goto done;
650
651         if (class_len && (info->ClassLength/sizeof(WCHAR) + 1 > *class_len))
652         {
653             status = STATUS_BUFFER_OVERFLOW;
654         }
655         else
656         {
657             memcpy( class, buf_ptr + info->ClassOffset, info->ClassLength );
658             class[info->ClassLength/sizeof(WCHAR)] = 0;
659         }
660     }
661     else status = STATUS_SUCCESS;
662
663     if (class_len) *class_len = info->ClassLength / sizeof(WCHAR);
664     if (subkeys) *subkeys = info->SubKeys;
665     if (max_subkey) *max_subkey = info->MaxNameLen;
666     if (max_class) *max_class = info->MaxClassLen;
667     if (values) *values = info->Values;
668     if (max_value) *max_value = info->MaxValueNameLen;
669     if (max_data) *max_data = info->MaxValueDataLen;
670     if (modif) *modif = *(FILETIME *)&info->LastWriteTime;
671
672  done:
673     if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
674     return RtlNtStatusToDosError( status );
675 }
676
677
678 /******************************************************************************
679  * RegQueryMultipleValuesA   [ADVAPI32.@]
680  *
681  * Retrieves the type and data for a list of value names associated with a key.
682  *
683  * PARAMS
684  *  hKey       [I] Handle to an open key.
685  *  val_list   [O] Array of VALENT structures that describes the entries.
686  *  num_vals   [I] Number of elements in val_list.
687  *  lpValueBuf [O] Pointer to a buffer that receives the data for each value.
688  *  ldwTotsize [I/O] Size of lpValueBuf.
689  *
690  * RETURNS
691  *  Success: ERROR_SUCCESS. ldwTotsize contains num bytes copied.
692  *  Failure: nonzero error code from Winerror.h ldwTotsize contains num needed
693  *           bytes.
694  */
695 DWORD WINAPI RegQueryMultipleValuesA(HKEY hkey, PVALENTA val_list, DWORD num_vals,
696                                      LPSTR lpValueBuf, LPDWORD ldwTotsize)
697 {
698     unsigned int i;
699     DWORD maxBytes = *ldwTotsize;
700     HRESULT status;
701     LPSTR bufptr = lpValueBuf;
702     *ldwTotsize = 0;
703
704     TRACE("(%p,%p,%ld,%p,%p=%ld)\n", hkey, val_list, num_vals, lpValueBuf, ldwTotsize, *ldwTotsize);
705
706     for(i=0; i < num_vals; ++i)
707     {
708
709         val_list[i].ve_valuelen=0;
710         status = RegQueryValueExA(hkey, val_list[i].ve_valuename, NULL, NULL, NULL, &val_list[i].ve_valuelen);
711         if(status != ERROR_SUCCESS)
712         {
713             return status;
714         }
715
716         if(lpValueBuf != NULL && *ldwTotsize + val_list[i].ve_valuelen <= maxBytes)
717         {
718             status = RegQueryValueExA(hkey, val_list[i].ve_valuename, NULL, &val_list[i].ve_type,
719                                       (LPBYTE)bufptr, &val_list[i].ve_valuelen);
720             if(status != ERROR_SUCCESS)
721             {
722                 return status;
723             }
724
725             val_list[i].ve_valueptr = (DWORD_PTR)bufptr;
726
727             bufptr += val_list[i].ve_valuelen;
728         }
729
730         *ldwTotsize += val_list[i].ve_valuelen;
731     }
732     return lpValueBuf != NULL && *ldwTotsize <= maxBytes ? ERROR_SUCCESS : ERROR_MORE_DATA;
733 }
734
735
736 /******************************************************************************
737  * RegQueryMultipleValuesW   [ADVAPI32.@]
738  *
739  * See RegQueryMultipleValuesA.
740  */
741 DWORD WINAPI RegQueryMultipleValuesW(HKEY hkey, PVALENTW val_list, DWORD num_vals,
742                                      LPWSTR lpValueBuf, LPDWORD ldwTotsize)
743 {
744     unsigned int i;
745     DWORD maxBytes = *ldwTotsize;
746     HRESULT status;
747     LPSTR bufptr = (LPSTR)lpValueBuf;
748     *ldwTotsize = 0;
749
750     TRACE("(%p,%p,%ld,%p,%p=%ld)\n", hkey, val_list, num_vals, lpValueBuf, ldwTotsize, *ldwTotsize);
751
752     for(i=0; i < num_vals; ++i)
753     {
754         val_list[i].ve_valuelen=0;
755         status = RegQueryValueExW(hkey, val_list[i].ve_valuename, NULL, NULL, NULL, &val_list[i].ve_valuelen);
756         if(status != ERROR_SUCCESS)
757         {
758             return status;
759         }
760
761         if(lpValueBuf != NULL && *ldwTotsize + val_list[i].ve_valuelen <= maxBytes)
762         {
763             status = RegQueryValueExW(hkey, val_list[i].ve_valuename, NULL, &val_list[i].ve_type,
764                                       (LPBYTE)bufptr, &val_list[i].ve_valuelen);
765             if(status != ERROR_SUCCESS)
766             {
767                 return status;
768             }
769
770             val_list[i].ve_valueptr = (DWORD_PTR)bufptr;
771
772             bufptr += val_list[i].ve_valuelen;
773         }
774
775         *ldwTotsize += val_list[i].ve_valuelen;
776     }
777     return lpValueBuf != NULL && *ldwTotsize <= maxBytes ? ERROR_SUCCESS : ERROR_MORE_DATA;
778 }
779
780 /******************************************************************************
781  * RegQueryInfoKeyA   [ADVAPI32.@]
782  *
783  * Retrieves information about a registry key.
784  *
785  * PARAMS
786  *  hKey                   [I] Handle to an open key.
787  *  lpClass                [O] Class string of the key.
788  *  lpcClass               [I/O] size of lpClass.
789  *  lpReserved             [I] Reserved; must be NULL.
790  *  lpcSubKeys             [O] Number of subkeys contained by the key.
791  *  lpcMaxSubKeyLen        [O] Size of the key's subkey with the longest name.
792  *  lpcMaxClassLen         [O] Size of the longest string specifying a subkey
793  *                             class in TCHARS.
794  *  lpcValues              [O] Number of values associated with the key.
795  *  lpcMaxValueNameLen     [O] Size of the key's longest value name in TCHARS.
796  *  lpcMaxValueLen         [O] Longest data component among the key's values
797  *  lpcbSecurityDescriptor [O] Size of the key's security descriptor.
798  *  lpftLastWriteTime      [O] FILETIME strucutre that is the last write time.
799  *
800  *  RETURNS
801  *   Success: ERROR_SUCCESS
802  *   Failure: nonzero error code from Winerror.h
803  */
804 DWORD WINAPI RegQueryInfoKeyA( HKEY hkey, LPSTR class, LPDWORD class_len, LPDWORD reserved,
805                                LPDWORD subkeys, LPDWORD max_subkey, LPDWORD max_class,
806                                LPDWORD values, LPDWORD max_value, LPDWORD max_data,
807                                LPDWORD security, FILETIME *modif )
808 {
809     NTSTATUS status;
810     char buffer[256], *buf_ptr = buffer;
811     KEY_FULL_INFORMATION *info = (KEY_FULL_INFORMATION *)buffer;
812     DWORD total_size, len;
813
814     TRACE( "(%p,%p,%ld,%p,%p,%p,%p,%p,%p,%p,%p)\n", hkey, class, class_len ? *class_len : 0,
815            reserved, subkeys, max_subkey, values, max_value, max_data, security, modif );
816
817     if (class && !class_len && is_version_nt()) return ERROR_INVALID_PARAMETER;
818     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
819
820     status = NtQueryKey( hkey, KeyFullInformation, buffer, sizeof(buffer), &total_size );
821     if (status && status != STATUS_BUFFER_OVERFLOW) goto done;
822
823     if (class || class_len)
824     {
825         /* retry with a dynamically allocated buffer */
826         while (status == STATUS_BUFFER_OVERFLOW)
827         {
828             if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
829             if (!(buf_ptr = HeapAlloc( GetProcessHeap(), 0, total_size )))
830                 return ERROR_NOT_ENOUGH_MEMORY;
831             info = (KEY_FULL_INFORMATION *)buf_ptr;
832             status = NtQueryKey( hkey, KeyFullInformation, buf_ptr, total_size, &total_size );
833         }
834
835         if (status) goto done;
836
837         RtlUnicodeToMultiByteSize( &len, (WCHAR *)(buf_ptr + info->ClassOffset), info->ClassLength);
838         if (class_len)
839         {
840             if (len + 1 > *class_len) status = STATUS_BUFFER_OVERFLOW;
841             *class_len = len;
842         }
843         if (class && !status)
844         {
845             RtlUnicodeToMultiByteN( class, len, NULL, (WCHAR *)(buf_ptr + info->ClassOffset),
846                                     info->ClassLength );
847             class[len] = 0;
848         }
849     }
850     else status = STATUS_SUCCESS;
851
852     if (subkeys) *subkeys = info->SubKeys;
853     if (max_subkey) *max_subkey = info->MaxNameLen;
854     if (max_class) *max_class = info->MaxClassLen;
855     if (values) *values = info->Values;
856     if (max_value) *max_value = info->MaxValueNameLen;
857     if (max_data) *max_data = info->MaxValueDataLen;
858     if (modif) *modif = *(FILETIME *)&info->LastWriteTime;
859
860  done:
861     if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
862     return RtlNtStatusToDosError( status );
863 }
864
865
866 /******************************************************************************
867  * RegCloseKey   [ADVAPI32.@]
868  *
869  * Close an open registry key.
870  *
871  * PARAMS
872  *  hkey [I] Handle of key to close
873  *
874  * RETURNS
875  *  Success: ERROR_SUCCESS
876  *  Failure: Error code
877  */
878 DWORD WINAPI RegCloseKey( HKEY hkey )
879 {
880     if (!hkey) return ERROR_INVALID_HANDLE;
881     if (hkey >= (HKEY)0x80000000) return ERROR_SUCCESS;
882     return RtlNtStatusToDosError( NtClose( hkey ) );
883 }
884
885
886 /******************************************************************************
887  * RegDeleteKeyW   [ADVAPI32.@]
888  *
889  * See RegDeleteKeyA.
890  */
891 DWORD WINAPI RegDeleteKeyW( HKEY hkey, LPCWSTR name )
892 {
893     DWORD ret;
894     HKEY tmp;
895
896     if (!name) return ERROR_INVALID_PARAMETER;
897
898     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
899
900     if (!(ret = RegOpenKeyExW( hkey, name, 0, DELETE, &tmp )))
901     {
902         ret = RtlNtStatusToDosError( NtDeleteKey( tmp ) );
903         RegCloseKey( tmp );
904     }
905     TRACE("%s ret=%08lx\n", debugstr_w(name), ret);
906     return ret;
907 }
908
909
910 /******************************************************************************
911  * RegDeleteKeyA   [ADVAPI32.@]
912  *
913  * Delete a registry key.
914  *
915  * PARAMS
916  *  hkey   [I] Handle to parent key containing the key to delete
917  *  name   [I] Name of the key user hkey to delete
918  *
919  * NOTES
920  *
921  * MSDN is wrong when it says that hkey must be opened with the DELETE access
922  * right. In reality, it opens a new handle with DELETE access.
923  *
924  * RETURNS
925  *  Success: ERROR_SUCCESS
926  *  Failure: Error code
927  */
928 DWORD WINAPI RegDeleteKeyA( HKEY hkey, LPCSTR name )
929 {
930     DWORD ret;
931     HKEY tmp;
932
933     if (!name) return ERROR_INVALID_PARAMETER;
934
935     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
936
937     if (!(ret = RegOpenKeyExA( hkey, name, 0, DELETE, &tmp )))
938     {
939         if (!is_version_nt()) /* win95 does recursive key deletes */
940         {
941             CHAR name[MAX_PATH];
942
943             while(!RegEnumKeyA(tmp, 0, name, sizeof(name)))
944             {
945                 if(RegDeleteKeyA(tmp, name))  /* recurse */
946                     break;
947             }
948         }
949         ret = RtlNtStatusToDosError( NtDeleteKey( tmp ) );
950         RegCloseKey( tmp );
951     }
952     TRACE("%s ret=%08lx\n", debugstr_a(name), ret);
953     return ret;
954 }
955
956
957
958 /******************************************************************************
959  * RegSetValueExW   [ADVAPI32.@]
960  *
961  * Set the data and contents of a registry value.
962  *
963  * PARAMS
964  *  hkey       [I] Handle of key to set value for
965  *  name       [I] Name of value to set
966  *  reserved   [I] Reserved, must be zero
967  *  type       [I] Type of the value being set
968  *  data       [I] The new contents of the value to set
969  *  count      [I] Size of data
970  *
971  * RETURNS
972  *  Success: ERROR_SUCCESS
973  *  Failure: Error code
974  */
975 DWORD WINAPI RegSetValueExW( HKEY hkey, LPCWSTR name, DWORD reserved,
976                              DWORD type, CONST BYTE *data, DWORD count )
977 {
978     UNICODE_STRING nameW;
979
980     /* no need for version check, not implemented on win9x anyway */
981     if (count && is_string(type))
982     {
983         LPCWSTR str = (LPCWSTR)data;
984         /* if user forgot to count terminating null, add it (yes NT does this) */
985         if (str[count / sizeof(WCHAR) - 1] && !str[count / sizeof(WCHAR)])
986             count += sizeof(WCHAR);
987     }
988     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
989
990     RtlInitUnicodeString( &nameW, name );
991     return RtlNtStatusToDosError( NtSetValueKey( hkey, &nameW, 0, type, data, count ) );
992 }
993
994
995 /******************************************************************************
996  * RegSetValueExA   [ADVAPI32.@]
997  *
998  * See RegSetValueExW.
999  *
1000  * NOTES
1001  *  win95 does not care about count for REG_SZ and finds out the len by itself (js)
1002  *  NT does definitely care (aj)
1003  */
1004 DWORD WINAPI RegSetValueExA( HKEY hkey, LPCSTR name, DWORD reserved, DWORD type,
1005                              CONST BYTE *data, DWORD count )
1006 {
1007     ANSI_STRING nameA;
1008     WCHAR *dataW = NULL;
1009     NTSTATUS status;
1010
1011     if (!is_version_nt())  /* win95 */
1012     {
1013         if (type == REG_SZ)
1014         {
1015             if (!data) return ERROR_INVALID_PARAMETER;
1016             count = strlen((const char *)data) + 1;
1017         }
1018     }
1019     else if (count && is_string(type))
1020     {
1021         /* if user forgot to count terminating null, add it (yes NT does this) */
1022         if (data[count-1] && !data[count]) count++;
1023     }
1024
1025     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
1026
1027     if (is_string( type )) /* need to convert to Unicode */
1028     {
1029         DWORD lenW;
1030         RtlMultiByteToUnicodeSize( &lenW, (const char *)data, count );
1031         if (!(dataW = HeapAlloc( GetProcessHeap(), 0, lenW ))) return ERROR_OUTOFMEMORY;
1032         RtlMultiByteToUnicodeN( dataW, lenW, NULL, (const char *)data, count );
1033         count = lenW;
1034         data = (BYTE *)dataW;
1035     }
1036
1037     RtlInitAnsiString( &nameA, name );
1038     if (!(status = RtlAnsiStringToUnicodeString( &NtCurrentTeb()->StaticUnicodeString,
1039                                                  &nameA, FALSE )))
1040     {
1041         status = NtSetValueKey( hkey, &NtCurrentTeb()->StaticUnicodeString, 0, type, data, count );
1042     }
1043     HeapFree( GetProcessHeap(), 0, dataW );
1044     return RtlNtStatusToDosError( status );
1045 }
1046
1047
1048 /******************************************************************************
1049  * RegSetValueW   [ADVAPI32.@]
1050  *
1051  * Sets the data for the default or unnamed value of a reg key.
1052  *
1053  * PARAMS
1054  *  hKey     [I] Handle to an open key.
1055  *  lpSubKey [I] Name of a subkey of hKey.
1056  *  dwType   [I] Type of information to store.
1057  *  lpData   [I] String that contains the data to set for the default value.
1058  *  cbData   [I] Size of lpData.
1059  *
1060  * RETURNS
1061  *  Success: ERROR_SUCCESS
1062  *  Failure: nonzero error code from Winerror.h
1063  */
1064 DWORD WINAPI RegSetValueW( HKEY hkey, LPCWSTR name, DWORD type, LPCWSTR data, DWORD count )
1065 {
1066     HKEY subkey = hkey;
1067     DWORD ret;
1068
1069     TRACE("(%p,%s,%ld,%s,%ld)\n", hkey, debugstr_w(name), type, debugstr_w(data), count );
1070
1071     if (type != REG_SZ) return ERROR_INVALID_PARAMETER;
1072
1073     if (name && name[0])  /* need to create the subkey */
1074     {
1075         if ((ret = RegCreateKeyW( hkey, name, &subkey )) != ERROR_SUCCESS) return ret;
1076     }
1077
1078     ret = RegSetValueExW( subkey, NULL, 0, REG_SZ, (const BYTE*)data,
1079                           (strlenW( data ) + 1) * sizeof(WCHAR) );
1080     if (subkey != hkey) RegCloseKey( subkey );
1081     return ret;
1082 }
1083
1084
1085 /******************************************************************************
1086  * RegSetValueA   [ADVAPI32.@]
1087  *
1088  * See RegSetValueW.
1089  */
1090 DWORD WINAPI RegSetValueA( HKEY hkey, LPCSTR name, DWORD type, LPCSTR data, DWORD count )
1091 {
1092     HKEY subkey = hkey;
1093     DWORD ret;
1094
1095     TRACE("(%p,%s,%ld,%s,%ld)\n", hkey, debugstr_a(name), type, debugstr_a(data), count );
1096
1097     if (type != REG_SZ) return ERROR_INVALID_PARAMETER;
1098
1099     if (name && name[0])  /* need to create the subkey */
1100     {
1101         if ((ret = RegCreateKeyA( hkey, name, &subkey )) != ERROR_SUCCESS) return ret;
1102     }
1103     ret = RegSetValueExA( subkey, NULL, 0, REG_SZ, (const BYTE*)data, strlen(data)+1 );
1104     if (subkey != hkey) RegCloseKey( subkey );
1105     return ret;
1106 }
1107
1108
1109
1110 /******************************************************************************
1111  * RegQueryValueExW   [ADVAPI32.@]
1112  *
1113  * See RegQueryValueExA.
1114  */
1115 DWORD WINAPI RegQueryValueExW( HKEY hkey, LPCWSTR name, LPDWORD reserved, LPDWORD type,
1116                                LPBYTE data, LPDWORD count )
1117 {
1118     NTSTATUS status;
1119     UNICODE_STRING name_str;
1120     DWORD total_size;
1121     char buffer[256], *buf_ptr = buffer;
1122     KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
1123     static const int info_size = offsetof( KEY_VALUE_PARTIAL_INFORMATION, Data );
1124
1125     TRACE("(%p,%s,%p,%p,%p,%p=%ld)\n",
1126           hkey, debugstr_w(name), reserved, type, data, count,
1127           (count && data) ? *count : 0 );
1128
1129     if ((data && !count) || reserved) return ERROR_INVALID_PARAMETER;
1130     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
1131
1132     RtlInitUnicodeString( &name_str, name );
1133
1134     if (data) total_size = min( sizeof(buffer), *count + info_size );
1135     else total_size = info_size;
1136
1137     status = NtQueryValueKey( hkey, &name_str, KeyValuePartialInformation,
1138                               buffer, total_size, &total_size );
1139     if (status && status != STATUS_BUFFER_OVERFLOW) goto done;
1140
1141     if (data)
1142     {
1143         /* retry with a dynamically allocated buffer */
1144         while (status == STATUS_BUFFER_OVERFLOW && total_size - info_size <= *count)
1145         {
1146             if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
1147             if (!(buf_ptr = HeapAlloc( GetProcessHeap(), 0, total_size )))
1148                 return ERROR_NOT_ENOUGH_MEMORY;
1149             info = (KEY_VALUE_PARTIAL_INFORMATION *)buf_ptr;
1150             status = NtQueryValueKey( hkey, &name_str, KeyValuePartialInformation,
1151                                       buf_ptr, total_size, &total_size );
1152         }
1153
1154         if (!status)
1155         {
1156             memcpy( data, buf_ptr + info_size, total_size - info_size );
1157             /* if the type is REG_SZ and data is not 0-terminated
1158              * and there is enough space in the buffer NT appends a \0 */
1159             if (total_size - info_size <= *count-sizeof(WCHAR) && is_string(info->Type))
1160             {
1161                 WCHAR *ptr = (WCHAR *)(data + total_size - info_size);
1162                 if (ptr > (WCHAR *)data && ptr[-1]) *ptr = 0;
1163             }
1164         }
1165         else if (status != STATUS_BUFFER_OVERFLOW) goto done;
1166     }
1167     else status = STATUS_SUCCESS;
1168
1169     if (type) *type = info->Type;
1170     if (count) *count = total_size - info_size;
1171
1172  done:
1173     if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
1174     return RtlNtStatusToDosError(status);
1175 }
1176
1177
1178 /******************************************************************************
1179  * RegQueryValueExA   [ADVAPI32.@]
1180  *
1181  * Get the type and contents of a specified value under with a key.
1182  *
1183  * PARAMS
1184  *  hkey      [I]   Handle of the key to query
1185  *  name      [I]   Name of value under hkey to query
1186  *  reserved  [I]   Reserved - must be NULL
1187  *  type      [O]   Destination for the value type, or NULL if not required
1188  *  data      [O]   Destination for the values contents, or NULL if not required
1189  *  count     [I/O] Size of data, updated with the number of bytes returned
1190  *
1191  * RETURNS
1192  *  Success: ERROR_SUCCESS. *count is updated with the number of bytes copied to data.
1193  *  Failure: ERROR_INVALID_HANDLE, if hkey is invalid.
1194  *           ERROR_INVALID_PARAMETER, if any other parameter is invalid.
1195  *           ERROR_MORE_DATA, if on input *count is too small to hold the contents.
1196  *                     
1197  * NOTES
1198  *   MSDN states that if data is too small it is partially filled. In reality 
1199  *   it remains untouched.
1200  */
1201 DWORD WINAPI RegQueryValueExA( HKEY hkey, LPCSTR name, LPDWORD reserved, LPDWORD type,
1202                                LPBYTE data, LPDWORD count )
1203 {
1204     NTSTATUS status;
1205     ANSI_STRING nameA;
1206     DWORD total_size;
1207     char buffer[256], *buf_ptr = buffer;
1208     KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
1209     static const int info_size = offsetof( KEY_VALUE_PARTIAL_INFORMATION, Data );
1210
1211     TRACE("(%p,%s,%p,%p,%p,%p=%ld)\n",
1212           hkey, debugstr_a(name), reserved, type, data, count, count ? *count : 0 );
1213
1214     if ((data && !count) || reserved) return ERROR_INVALID_PARAMETER;
1215     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
1216
1217     RtlInitAnsiString( &nameA, name );
1218     if ((status = RtlAnsiStringToUnicodeString( &NtCurrentTeb()->StaticUnicodeString,
1219                                                 &nameA, FALSE )))
1220         return RtlNtStatusToDosError(status);
1221
1222     status = NtQueryValueKey( hkey, &NtCurrentTeb()->StaticUnicodeString,
1223                               KeyValuePartialInformation, buffer, sizeof(buffer), &total_size );
1224     if (status && status != STATUS_BUFFER_OVERFLOW) goto done;
1225
1226     /* we need to fetch the contents for a string type even if not requested,
1227      * because we need to compute the length of the ASCII string. */
1228     if (data || is_string(info->Type))
1229     {
1230         /* retry with a dynamically allocated buffer */
1231         while (status == STATUS_BUFFER_OVERFLOW)
1232         {
1233             if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
1234             if (!(buf_ptr = HeapAlloc( GetProcessHeap(), 0, total_size )))
1235             {
1236                 status = STATUS_NO_MEMORY;
1237                 goto done;
1238             }
1239             info = (KEY_VALUE_PARTIAL_INFORMATION *)buf_ptr;
1240             status = NtQueryValueKey( hkey, &NtCurrentTeb()->StaticUnicodeString,
1241                                     KeyValuePartialInformation, buf_ptr, total_size, &total_size );
1242         }
1243
1244         if (status) goto done;
1245
1246         if (is_string(info->Type))
1247         {
1248             DWORD len;
1249
1250             RtlUnicodeToMultiByteSize( &len, (WCHAR *)(buf_ptr + info_size),
1251                                        total_size - info_size );
1252             if (data && len)
1253             {
1254                 if (len > *count) status = STATUS_BUFFER_OVERFLOW;
1255                 else
1256                 {
1257                     RtlUnicodeToMultiByteN( (char*)data, len, NULL, (WCHAR *)(buf_ptr + info_size),
1258                                             total_size - info_size );
1259                     /* if the type is REG_SZ and data is not 0-terminated
1260                      * and there is enough space in the buffer NT appends a \0 */
1261                     if (len < *count && data[len-1]) data[len] = 0;
1262                 }
1263             }
1264             total_size = len + info_size;
1265         }
1266         else if (data)
1267         {
1268             if (total_size - info_size > *count) status = STATUS_BUFFER_OVERFLOW;
1269             else memcpy( data, buf_ptr + info_size, total_size - info_size );
1270         }
1271     }
1272     else status = STATUS_SUCCESS;
1273
1274     if (type) *type = info->Type;
1275     if (count) *count = total_size - info_size;
1276
1277  done:
1278     if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
1279     return RtlNtStatusToDosError(status);
1280 }
1281
1282
1283 /******************************************************************************
1284  * RegQueryValueW   [ADVAPI32.@]
1285  *
1286  * Retrieves the data associated with the default or unnamed value of a key.
1287  *
1288  * PARAMS
1289  *  hkey      [I] Handle to an open key.
1290  *  name      [I] Name of the subkey of hKey.
1291  *  data      [O] Receives the string associated with the default value
1292  *                of the key.
1293  *  count     [I/O] Size of lpValue in bytes.
1294  *
1295  *  RETURNS
1296  *   Success: ERROR_SUCCESS
1297  *   Failure: nonzero error code from Winerror.h
1298  */
1299 DWORD WINAPI RegQueryValueW( HKEY hkey, LPCWSTR name, LPWSTR data, LPLONG count )
1300 {
1301     DWORD ret;
1302     HKEY subkey = hkey;
1303
1304     TRACE("(%p,%s,%p,%ld)\n", hkey, debugstr_w(name), data, count ? *count : 0 );
1305
1306     if (name && name[0])
1307     {
1308         if ((ret = RegOpenKeyW( hkey, name, &subkey )) != ERROR_SUCCESS) return ret;
1309     }
1310     ret = RegQueryValueExW( subkey, NULL, NULL, NULL, (LPBYTE)data, (LPDWORD)count );
1311     if (subkey != hkey) RegCloseKey( subkey );
1312     if (ret == ERROR_FILE_NOT_FOUND)
1313     {
1314         /* return empty string if default value not found */
1315         if (data) *data = 0;
1316         if (count) *count = sizeof(WCHAR);
1317         ret = ERROR_SUCCESS;
1318     }
1319     return ret;
1320 }
1321
1322
1323 /******************************************************************************
1324  * RegQueryValueA   [ADVAPI32.@]
1325  *
1326  * See RegQueryValueW.
1327  */
1328 DWORD WINAPI RegQueryValueA( HKEY hkey, LPCSTR name, LPSTR data, LPLONG count )
1329 {
1330     DWORD ret;
1331     HKEY subkey = hkey;
1332
1333     TRACE("(%p,%s,%p,%ld)\n", hkey, debugstr_a(name), data, count ? *count : 0 );
1334
1335     if (name && name[0])
1336     {
1337         if ((ret = RegOpenKeyA( hkey, name, &subkey )) != ERROR_SUCCESS) return ret;
1338     }
1339     ret = RegQueryValueExA( subkey, NULL, NULL, NULL, (LPBYTE)data, (LPDWORD)count );
1340     if (subkey != hkey) RegCloseKey( subkey );
1341     if (ret == ERROR_FILE_NOT_FOUND)
1342     {
1343         /* return empty string if default value not found */
1344         if (data) *data = 0;
1345         if (count) *count = 1;
1346         ret = ERROR_SUCCESS;
1347     }
1348     return ret;
1349 }
1350
1351
1352 /******************************************************************************
1353  * ADVAPI_ApplyRestrictions   [internal]
1354  *
1355  * Helper function for RegGetValueA/W.
1356  */
1357 VOID ADVAPI_ApplyRestrictions( DWORD dwFlags, DWORD dwType, DWORD cbData,
1358                                PLONG ret )
1359 {
1360     /* Check if the type is restricted by the passed flags */
1361     if (*ret == ERROR_SUCCESS || *ret == ERROR_MORE_DATA)
1362     {
1363         DWORD dwMask = 0;
1364
1365         switch (dwType)
1366         {
1367         case REG_NONE: dwMask = RRF_RT_REG_NONE; break;
1368         case REG_SZ: dwMask = RRF_RT_REG_SZ; break;
1369         case REG_EXPAND_SZ: dwMask = RRF_RT_REG_EXPAND_SZ; break;
1370         case REG_MULTI_SZ: dwMask = RRF_RT_REG_MULTI_SZ; break;
1371         case REG_BINARY: dwMask = RRF_RT_REG_BINARY; break;
1372         case REG_DWORD: dwMask = RRF_RT_REG_DWORD; break;
1373         case REG_QWORD: dwMask = RRF_RT_REG_QWORD; break;
1374         }
1375
1376         if (dwFlags & dwMask)
1377         {
1378             /* Type is not restricted, check for size mismatch */
1379             if (dwType == REG_BINARY)
1380             {
1381                 DWORD cbExpect = 0;
1382
1383                 if ((dwFlags & RRF_RT_DWORD) == RRF_RT_DWORD)
1384                     cbExpect = 4;
1385                 else if ((dwFlags & RRF_RT_DWORD) == RRF_RT_QWORD)
1386                     cbExpect = 8;
1387
1388                 if (cbExpect && cbData != cbExpect)
1389                     *ret = ERROR_DATATYPE_MISMATCH;
1390             }
1391         }
1392         else *ret = ERROR_UNSUPPORTED_TYPE;
1393     }
1394 }
1395
1396
1397 /******************************************************************************
1398  * RegGetValueW   [ADVAPI32.@]
1399  *
1400  * Retrieves the type and data for a value name associated with a key 
1401  * optionally expanding it's content and restricting it's type.
1402  *
1403  * PARAMS
1404  *  hKey      [I] Handle to an open key.
1405  *  pszSubKey [I] Name of the subkey of hKey.
1406  *  pszValue  [I] Name of value under hKey/szSubKey to query.
1407  *  dwFlags   [I] Flags restricting the value type to retrieve.
1408  *  pdwType   [O] Destination for the values type, may be NULL.
1409  *  pvData    [O] Destination for the values content, may be NULL.
1410  *  pcbData   [I/O] Size of pvData, updated with the size required to 
1411  *                  retrieve the whole content.
1412  *
1413  * RETURNS
1414  *  Success: ERROR_SUCCESS
1415  *  Failure: nonzero error code from Winerror.h
1416  *
1417  * NOTES
1418  *  - Unless RRF_NOEXPAND is specified REG_EXPAND_SZ is automatically expanded
1419  *    and REG_SZ is retrieved instead.
1420  *  - Restrictions are applied after expanding, using RRF_RT_REG_EXPAND_SZ 
1421  *    without RRF_NOEXPAND is thus not allowed.
1422  */
1423 LONG WINAPI RegGetValueW( HKEY hKey, LPCWSTR pszSubKey, LPCWSTR pszValue, 
1424                           DWORD dwFlags, LPDWORD pdwType, PVOID pvData,
1425                           LPDWORD pcbData )
1426 {
1427     DWORD dwType, cbData = pcbData ? *pcbData : 0;
1428     PVOID pvBuf = NULL;
1429     LONG ret;
1430
1431     TRACE("(%p,%s,%s,%ld,%p,%p,%p=%ld)\n", 
1432           hKey, debugstr_w(pszSubKey), debugstr_w(pszValue), dwFlags, pdwType,
1433           pvData, pcbData, cbData);
1434
1435     if ((dwFlags & RRF_RT_REG_EXPAND_SZ) && !(dwFlags & RRF_NOEXPAND))
1436         return ERROR_INVALID_PARAMETER;
1437
1438     if (pszSubKey && pszSubKey[0])
1439     {
1440         ret = RegOpenKeyExW(hKey, pszSubKey, 0, KEY_QUERY_VALUE, &hKey);
1441         if (ret != ERROR_SUCCESS) return ret;
1442     }
1443
1444     ret = RegQueryValueExW(hKey, pszValue, NULL, &dwType, pvData, &cbData);
1445     
1446     /* If we are going to expand we need to read in the whole the value even
1447      * if the passed buffer was too small as the expanded string might be
1448      * smaller than the unexpanded one and could fit into cbData bytes. */
1449     if ((ret == ERROR_SUCCESS || ret == ERROR_MORE_DATA) &&
1450         (dwType == REG_EXPAND_SZ && !(dwFlags & RRF_NOEXPAND)))
1451     {
1452         do {
1453             if (pvBuf) HeapFree(GetProcessHeap(), 0, pvBuf);
1454             
1455             pvBuf = HeapAlloc(GetProcessHeap(), 0, cbData);
1456             if (!pvBuf)
1457             {
1458                 ret = ERROR_NOT_ENOUGH_MEMORY;
1459                 break;
1460             }
1461
1462             if (ret == ERROR_MORE_DATA)
1463                 ret = RegQueryValueExW(hKey, pszValue, NULL, 
1464                                        &dwType, pvBuf, &cbData);
1465             else
1466             {
1467                 /* Even if cbData was large enough we have to copy the 
1468                  * string since ExpandEnvironmentStrings can't handle
1469                  * overlapping buffers. */
1470                 CopyMemory(pvBuf, pvData, cbData);
1471             }
1472
1473             /* Both the type or the value itself could have been modified in
1474              * between so we have to keep retrying until the buffer is large
1475              * enough or we no longer have to expand the value. */
1476         } while (dwType == REG_EXPAND_SZ && ret == ERROR_MORE_DATA);
1477
1478         if (ret == ERROR_SUCCESS)
1479         {
1480             if (dwType == REG_EXPAND_SZ)
1481             {
1482                 cbData = ExpandEnvironmentStringsW(pvBuf, pvData,
1483                                                    pcbData ? *pcbData : 0);
1484                 dwType = REG_SZ;
1485                 if(pcbData && cbData > *pcbData)
1486                     ret = ERROR_MORE_DATA;
1487             }
1488             else if (pcbData)
1489                 CopyMemory(pvData, pvBuf, *pcbData);
1490         }
1491
1492         if (pvBuf) HeapFree(GetProcessHeap(), 0, pvBuf);
1493     }
1494
1495     if (pszSubKey && pszSubKey[0])
1496         RegCloseKey(hKey);
1497
1498     ADVAPI_ApplyRestrictions(dwFlags, dwType, cbData, &ret);
1499
1500     if (pcbData && ret != ERROR_SUCCESS && (dwFlags & RRF_ZEROONFAILURE))
1501         ZeroMemory(pvData, *pcbData);
1502
1503     if (pdwType) *pdwType = dwType;
1504     if (pcbData) *pcbData = cbData;
1505
1506     return ret;
1507 }
1508
1509
1510 /******************************************************************************
1511  * RegGetValueA   [ADVAPI32.@]
1512  *
1513  * See RegGetValueW.
1514  */
1515 LONG WINAPI RegGetValueA( HKEY hKey, LPCSTR pszSubKey, LPCSTR pszValue, 
1516                           DWORD dwFlags, LPDWORD pdwType, PVOID pvData, 
1517                           LPDWORD pcbData )
1518 {
1519     DWORD dwType, cbData = pcbData ? *pcbData : 0;
1520     PVOID pvBuf = NULL;
1521     LONG ret;
1522
1523     TRACE("(%p,%s,%s,%ld,%p,%p,%p=%ld)\n", 
1524           hKey, pszSubKey, pszValue, dwFlags, pdwType, pvData, pcbData,
1525           cbData);
1526
1527     if ((dwFlags & RRF_RT_REG_EXPAND_SZ) && !(dwFlags & RRF_NOEXPAND))
1528         return ERROR_INVALID_PARAMETER;
1529
1530     if (pszSubKey && pszSubKey[0])
1531     {
1532         ret = RegOpenKeyExA(hKey, pszSubKey, 0, KEY_QUERY_VALUE, &hKey);
1533         if (ret != ERROR_SUCCESS) return ret;
1534     }
1535
1536     ret = RegQueryValueExA(hKey, pszValue, NULL, &dwType, pvData, &cbData);
1537
1538     /* If we are going to expand we need to read in the whole the value even
1539      * if the passed buffer was too small as the expanded string might be
1540      * smaller than the unexpanded one and could fit into cbData bytes. */
1541     if ((ret == ERROR_SUCCESS || ret == ERROR_MORE_DATA) &&
1542         (dwType == REG_EXPAND_SZ && !(dwFlags & RRF_NOEXPAND)))
1543     {
1544         do {
1545             if (pvBuf) HeapFree(GetProcessHeap(), 0, pvBuf);
1546
1547             pvBuf = HeapAlloc(GetProcessHeap(), 0, cbData);
1548             if (!pvBuf)
1549             {
1550                 ret = ERROR_NOT_ENOUGH_MEMORY;
1551                 break;
1552             }
1553
1554             if (ret == ERROR_MORE_DATA)
1555                 ret = RegQueryValueExA(hKey, pszValue, NULL, 
1556                                        &dwType, pvBuf, &cbData);
1557             else
1558             {
1559                 /* Even if cbData was large enough we have to copy the 
1560                  * string since ExpandEnvironmentStrings can't handle
1561                  * overlapping buffers. */
1562                 CopyMemory(pvBuf, pvData, cbData);
1563             }
1564
1565             /* Both the type or the value itself could have been modified in
1566              * between so we have to keep retrying until the buffer is large
1567              * enough or we no longer have to expand the value. */
1568         } while (dwType == REG_EXPAND_SZ && ret == ERROR_MORE_DATA);
1569
1570         if (ret == ERROR_SUCCESS)
1571         {
1572             if (dwType == REG_EXPAND_SZ)
1573             {
1574                 cbData = ExpandEnvironmentStringsA(pvBuf, pvData,
1575                                                    pcbData ? *pcbData : 0);
1576                 dwType = REG_SZ;
1577                 if(pcbData && cbData > *pcbData)
1578                     ret = ERROR_MORE_DATA;
1579             }
1580             else if (pcbData)
1581                 CopyMemory(pvData, pvBuf, *pcbData);
1582         }
1583
1584         if (pvBuf) HeapFree(GetProcessHeap(), 0, pvBuf);
1585     }
1586
1587     if (pszSubKey && pszSubKey[0])
1588         RegCloseKey(hKey);
1589
1590     ADVAPI_ApplyRestrictions(dwFlags, dwType, cbData, &ret);
1591
1592     if (pcbData && ret != ERROR_SUCCESS && (dwFlags & RRF_ZEROONFAILURE))
1593         ZeroMemory(pvData, *pcbData);
1594
1595     if (pdwType) *pdwType = dwType;
1596     if (pcbData) *pcbData = cbData;
1597
1598     return ret;
1599 }
1600
1601
1602 /******************************************************************************
1603  * RegEnumValueW   [ADVAPI32.@]
1604  *
1605  * Enumerates the values for the specified open registry key.
1606  *
1607  * PARAMS
1608  *  hkey       [I] Handle to key to query
1609  *  index      [I] Index of value to query
1610  *  value      [O] Value string
1611  *  val_count  [I/O] Size of value buffer (in wchars)
1612  *  reserved   [I] Reserved
1613  *  type       [O] Type code
1614  *  data       [O] Value data
1615  *  count      [I/O] Size of data buffer (in bytes)
1616  *
1617  * RETURNS
1618  *  Success: ERROR_SUCCESS
1619  *  Failure: nonzero error code from Winerror.h
1620  */
1621
1622 DWORD WINAPI RegEnumValueW( HKEY hkey, DWORD index, LPWSTR value, LPDWORD val_count,
1623                             LPDWORD reserved, LPDWORD type, LPBYTE data, LPDWORD count )
1624 {
1625     NTSTATUS status;
1626     DWORD total_size;
1627     char buffer[256], *buf_ptr = buffer;
1628     KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
1629     static const int info_size = offsetof( KEY_VALUE_FULL_INFORMATION, Name );
1630
1631     TRACE("(%p,%ld,%p,%p,%p,%p,%p,%p)\n",
1632           hkey, index, value, val_count, reserved, type, data, count );
1633
1634     /* NT only checks count, not val_count */
1635     if ((data && !count) || reserved) return ERROR_INVALID_PARAMETER;
1636     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
1637
1638     total_size = info_size + (MAX_PATH + 1) * sizeof(WCHAR);
1639     if (data) total_size += *count;
1640     total_size = min( sizeof(buffer), total_size );
1641
1642     status = NtEnumerateValueKey( hkey, index, KeyValueFullInformation,
1643                                   buffer, total_size, &total_size );
1644     if (status && status != STATUS_BUFFER_OVERFLOW) goto done;
1645
1646     if (value || data)
1647     {
1648         /* retry with a dynamically allocated buffer */
1649         while (status == STATUS_BUFFER_OVERFLOW)
1650         {
1651             if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
1652             if (!(buf_ptr = HeapAlloc( GetProcessHeap(), 0, total_size )))
1653                 return ERROR_NOT_ENOUGH_MEMORY;
1654             info = (KEY_VALUE_FULL_INFORMATION *)buf_ptr;
1655             status = NtEnumerateValueKey( hkey, index, KeyValueFullInformation,
1656                                           buf_ptr, total_size, &total_size );
1657         }
1658
1659         if (status) goto done;
1660
1661         if (value)
1662         {
1663             if (info->NameLength/sizeof(WCHAR) >= *val_count)
1664             {
1665                 status = STATUS_BUFFER_OVERFLOW;
1666                 goto overflow;
1667             }
1668             memcpy( value, info->Name, info->NameLength );
1669             *val_count = info->NameLength / sizeof(WCHAR);
1670             value[*val_count] = 0;
1671         }
1672
1673         if (data)
1674         {
1675             if (total_size - info->DataOffset > *count)
1676             {
1677                 status = STATUS_BUFFER_OVERFLOW;
1678                 goto overflow;
1679             }
1680             memcpy( data, buf_ptr + info->DataOffset, total_size - info->DataOffset );
1681             if (total_size - info->DataOffset <= *count-sizeof(WCHAR) && is_string(info->Type))
1682             {
1683                 /* if the type is REG_SZ and data is not 0-terminated
1684                  * and there is enough space in the buffer NT appends a \0 */
1685                 WCHAR *ptr = (WCHAR *)(data + total_size - info->DataOffset);
1686                 if (ptr > (WCHAR *)data && ptr[-1]) *ptr = 0;
1687             }
1688         }
1689     }
1690     else status = STATUS_SUCCESS;
1691
1692  overflow:
1693     if (type) *type = info->Type;
1694     if (count) *count = info->DataLength;
1695
1696  done:
1697     if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
1698     return RtlNtStatusToDosError(status);
1699 }
1700
1701
1702 /******************************************************************************
1703  * RegEnumValueA   [ADVAPI32.@]
1704  *
1705  * See RegEnumValueW.
1706  */
1707 DWORD WINAPI RegEnumValueA( HKEY hkey, DWORD index, LPSTR value, LPDWORD val_count,
1708                             LPDWORD reserved, LPDWORD type, LPBYTE data, LPDWORD count )
1709 {
1710     NTSTATUS status;
1711     DWORD total_size;
1712     char buffer[256], *buf_ptr = buffer;
1713     KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
1714     static const int info_size = offsetof( KEY_VALUE_FULL_INFORMATION, Name );
1715
1716     TRACE("(%p,%ld,%p,%p,%p,%p,%p,%p)\n",
1717           hkey, index, value, val_count, reserved, type, data, count );
1718
1719     /* NT only checks count, not val_count */
1720     if ((data && !count) || reserved) return ERROR_INVALID_PARAMETER;
1721     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
1722
1723     total_size = info_size + (MAX_PATH + 1) * sizeof(WCHAR);
1724     if (data) total_size += *count;
1725     total_size = min( sizeof(buffer), total_size );
1726
1727     status = NtEnumerateValueKey( hkey, index, KeyValueFullInformation,
1728                                   buffer, total_size, &total_size );
1729     if (status && status != STATUS_BUFFER_OVERFLOW) goto done;
1730
1731     /* we need to fetch the contents for a string type even if not requested,
1732      * because we need to compute the length of the ASCII string. */
1733     if (value || data || is_string(info->Type))
1734     {
1735         /* retry with a dynamically allocated buffer */
1736         while (status == STATUS_BUFFER_OVERFLOW)
1737         {
1738             if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
1739             if (!(buf_ptr = HeapAlloc( GetProcessHeap(), 0, total_size )))
1740                 return ERROR_NOT_ENOUGH_MEMORY;
1741             info = (KEY_VALUE_FULL_INFORMATION *)buf_ptr;
1742             status = NtEnumerateValueKey( hkey, index, KeyValueFullInformation,
1743                                           buf_ptr, total_size, &total_size );
1744         }
1745
1746         if (status) goto done;
1747
1748         if (is_string(info->Type))
1749         {
1750             DWORD len;
1751             RtlUnicodeToMultiByteSize( &len, (WCHAR *)(buf_ptr + info->DataOffset),
1752                                        total_size - info->DataOffset );
1753             if (data && len)
1754             {
1755                 if (len > *count) status = STATUS_BUFFER_OVERFLOW;
1756                 else
1757                 {
1758                     RtlUnicodeToMultiByteN( (char*)data, len, NULL, (WCHAR *)(buf_ptr + info->DataOffset),
1759                                             total_size - info->DataOffset );
1760                     /* if the type is REG_SZ and data is not 0-terminated
1761                      * and there is enough space in the buffer NT appends a \0 */
1762                     if (len < *count && data[len-1]) data[len] = 0;
1763                 }
1764             }
1765             info->DataLength = len;
1766         }
1767         else if (data)
1768         {
1769             if (total_size - info->DataOffset > *count) status = STATUS_BUFFER_OVERFLOW;
1770             else memcpy( data, buf_ptr + info->DataOffset, total_size - info->DataOffset );
1771         }
1772
1773         if (value && !status)
1774         {
1775             DWORD len;
1776
1777             RtlUnicodeToMultiByteSize( &len, info->Name, info->NameLength );
1778             if (len >= *val_count)
1779             {
1780                 status = STATUS_BUFFER_OVERFLOW;
1781                 if (*val_count)
1782                 {
1783                     len = *val_count - 1;
1784                     RtlUnicodeToMultiByteN( value, len, NULL, info->Name, info->NameLength );
1785                     value[len] = 0;
1786                 }
1787             }
1788             else
1789             {
1790                 RtlUnicodeToMultiByteN( value, len, NULL, info->Name, info->NameLength );
1791                 value[len] = 0;
1792                 *val_count = len;
1793             }
1794         }
1795     }
1796     else status = STATUS_SUCCESS;
1797
1798     if (type) *type = info->Type;
1799     if (count) *count = info->DataLength;
1800
1801  done:
1802     if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
1803     return RtlNtStatusToDosError(status);
1804 }
1805
1806
1807
1808 /******************************************************************************
1809  * RegDeleteValueW   [ADVAPI32.@]
1810  *
1811  * See RegDeleteValueA.
1812  */
1813 DWORD WINAPI RegDeleteValueW( HKEY hkey, LPCWSTR name )
1814 {
1815     UNICODE_STRING nameW;
1816
1817     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
1818
1819     RtlInitUnicodeString( &nameW, name );
1820     return RtlNtStatusToDosError( NtDeleteValueKey( hkey, &nameW ) );
1821 }
1822
1823
1824 /******************************************************************************
1825  * RegDeleteValueA   [ADVAPI32.@]
1826  *
1827  * Delete a value from the registry.
1828  *
1829  * PARAMS
1830  *  hkey [I] Registry handle of the key holding the value
1831  *  name [I] Name of the value under hkey to delete
1832  *
1833  * RETURNS
1834  *  Success: ERROR_SUCCESS
1835  *  Failure: nonzero error code from Winerror.h
1836  */
1837 DWORD WINAPI RegDeleteValueA( HKEY hkey, LPCSTR name )
1838 {
1839     STRING nameA;
1840     NTSTATUS status;
1841
1842     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
1843
1844     RtlInitAnsiString( &nameA, name );
1845     if (!(status = RtlAnsiStringToUnicodeString( &NtCurrentTeb()->StaticUnicodeString,
1846                                                  &nameA, FALSE )))
1847         status = NtDeleteValueKey( hkey, &NtCurrentTeb()->StaticUnicodeString );
1848     return RtlNtStatusToDosError( status );
1849 }
1850
1851
1852 /******************************************************************************
1853  * RegLoadKeyW   [ADVAPI32.@]
1854  *
1855  * Create a subkey under HKEY_USERS or HKEY_LOCAL_MACHINE and store
1856  * registration information from a specified file into that subkey.
1857  *
1858  * PARAMS
1859  *  hkey      [I] Handle of open key
1860  *  subkey    [I] Address of name of subkey
1861  *  filename  [I] Address of filename for registry information
1862  *
1863  * RETURNS
1864  *  Success: ERROR_SUCCES
1865  *  Failure: nonzero error code from Winerror.h
1866  */
1867 LONG WINAPI RegLoadKeyW( HKEY hkey, LPCWSTR subkey, LPCWSTR filename )
1868 {
1869     OBJECT_ATTRIBUTES destkey, file;
1870     UNICODE_STRING subkeyW, filenameW;
1871
1872     if (!(hkey = get_special_root_hkey(hkey))) return ERROR_INVALID_HANDLE;
1873
1874     destkey.Length = sizeof(destkey);
1875     destkey.RootDirectory = hkey;               /* root key: HKLM or HKU */
1876     destkey.ObjectName = &subkeyW;              /* name of the key */
1877     destkey.Attributes = 0;
1878     destkey.SecurityDescriptor = NULL;
1879     destkey.SecurityQualityOfService = NULL;
1880     RtlInitUnicodeString(&subkeyW, subkey);
1881
1882     file.Length = sizeof(file);
1883     file.RootDirectory = NULL;
1884     file.ObjectName = &filenameW;               /* file containing the hive */
1885     file.Attributes = OBJ_CASE_INSENSITIVE;
1886     file.SecurityDescriptor = NULL;
1887     file.SecurityQualityOfService = NULL;
1888     RtlDosPathNameToNtPathName_U(filename, &filenameW, NULL, NULL);
1889
1890     return RtlNtStatusToDosError( NtLoadKey(&destkey, &file) );
1891 }
1892
1893
1894 /******************************************************************************
1895  * RegLoadKeyA   [ADVAPI32.@]
1896  *
1897  * See RegLoadKeyW.
1898  */
1899 LONG WINAPI RegLoadKeyA( HKEY hkey, LPCSTR subkey, LPCSTR filename )
1900 {
1901     UNICODE_STRING subkeyW, filenameW;
1902     STRING subkeyA, filenameA;
1903     NTSTATUS status;
1904
1905     RtlInitAnsiString(&subkeyA, subkey);
1906     RtlInitAnsiString(&filenameA, filename);
1907
1908     if ((status = RtlAnsiStringToUnicodeString(&subkeyW, &subkeyA, TRUE)))
1909         return RtlNtStatusToDosError(status);
1910
1911     if ((status = RtlAnsiStringToUnicodeString(&filenameW, &filenameA, TRUE)))
1912         return RtlNtStatusToDosError(status);
1913
1914     return RegLoadKeyW(hkey, subkeyW.Buffer, filenameW.Buffer);
1915 }
1916
1917
1918 /******************************************************************************
1919  * RegSaveKeyW   [ADVAPI32.@]
1920  *
1921  * Save a key and all of its subkeys and values to a new file in the standard format.
1922  *
1923  * PARAMS
1924  *  hkey   [I] Handle of key where save begins
1925  *  lpFile [I] Address of filename to save to
1926  *  sa     [I] Address of security structure
1927  *
1928  * RETURNS
1929  *  Success: ERROR_SUCCESS
1930  *  Failure: nonzero error code from Winerror.h
1931  */
1932 LONG WINAPI RegSaveKeyW( HKEY hkey, LPCWSTR file, LPSECURITY_ATTRIBUTES sa )
1933 {
1934     static const WCHAR format[] =
1935         {'r','e','g','%','0','4','x','.','t','m','p',0};
1936     WCHAR buffer[MAX_PATH];
1937     int count = 0;
1938     LPWSTR nameW;
1939     DWORD ret, err;
1940     HANDLE handle;
1941
1942     TRACE( "(%p,%s,%p)\n", hkey, debugstr_w(file), sa );
1943
1944     if (!file || !*file) return ERROR_INVALID_PARAMETER;
1945     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
1946
1947     err = GetLastError();
1948     GetFullPathNameW( file, sizeof(buffer)/sizeof(WCHAR), buffer, &nameW );
1949
1950     for (;;)
1951     {
1952         snprintfW( nameW, 16, format, count++ );
1953         handle = CreateFileW( buffer, GENERIC_WRITE, 0, NULL,
1954                             CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
1955         if (handle != INVALID_HANDLE_VALUE) break;
1956         if ((ret = GetLastError()) != ERROR_ALREADY_EXISTS) goto done;
1957
1958         /* Something gone haywire ? Please report if this happens abnormally */
1959         if (count >= 100)
1960             MESSAGE("Wow, we are already fiddling with a temp file %s with an ordinal as high as %d !\nYou might want to delete all corresponding temp files in that directory.\n", debugstr_w(buffer), count);
1961     }
1962
1963     ret = RtlNtStatusToDosError(NtSaveKey(hkey, handle));
1964
1965     CloseHandle( handle );
1966     if (!ret)
1967     {
1968         if (!MoveFileExW( buffer, file, MOVEFILE_REPLACE_EXISTING ))
1969         {
1970             ERR( "Failed to move %s to %s\n", debugstr_w(buffer),
1971                 debugstr_w(file) );
1972             ret = GetLastError();
1973         }
1974     }
1975     if (ret) DeleteFileW( buffer );
1976
1977 done:
1978     SetLastError( err );  /* restore last error code */
1979     return ret;
1980 }
1981
1982
1983 /******************************************************************************
1984  * RegSaveKeyA  [ADVAPI32.@]
1985  *
1986  * See RegSaveKeyW.
1987  */
1988 LONG WINAPI RegSaveKeyA( HKEY hkey, LPCSTR file, LPSECURITY_ATTRIBUTES sa )
1989 {
1990     UNICODE_STRING *fileW = &NtCurrentTeb()->StaticUnicodeString;
1991     NTSTATUS status;
1992     STRING fileA;
1993
1994     RtlInitAnsiString(&fileA, file);
1995     if ((status = RtlAnsiStringToUnicodeString(fileW, &fileA, FALSE)))
1996         return RtlNtStatusToDosError( status );
1997     return RegSaveKeyW(hkey, fileW->Buffer, sa);
1998 }
1999
2000
2001 /******************************************************************************
2002  * RegRestoreKeyW [ADVAPI32.@]
2003  *
2004  * Read the registry information from a file and copy it over a key.
2005  *
2006  * PARAMS
2007  *  hkey    [I] Handle of key where restore begins
2008  *  lpFile  [I] Address of filename containing saved tree
2009  *  dwFlags [I] Optional flags
2010  *
2011  * RETURNS
2012  *  Success: ERROR_SUCCESS
2013  *  Failure: nonzero error code from Winerror.h
2014  */
2015 LONG WINAPI RegRestoreKeyW( HKEY hkey, LPCWSTR lpFile, DWORD dwFlags )
2016 {
2017     TRACE("(%p,%s,%ld)\n",hkey,debugstr_w(lpFile),dwFlags);
2018
2019     /* It seems to do this check before the hkey check */
2020     if (!lpFile || !*lpFile)
2021         return ERROR_INVALID_PARAMETER;
2022
2023     FIXME("(%p,%s,%ld): stub\n",hkey,debugstr_w(lpFile),dwFlags);
2024
2025     /* Check for file existence */
2026
2027     return ERROR_SUCCESS;
2028 }
2029
2030
2031 /******************************************************************************
2032  * RegRestoreKeyA [ADVAPI32.@]
2033  *
2034  * See RegRestoreKeyW.
2035  */
2036 LONG WINAPI RegRestoreKeyA( HKEY hkey, LPCSTR lpFile, DWORD dwFlags )
2037 {
2038     UNICODE_STRING lpFileW;
2039     LONG ret;
2040
2041     RtlCreateUnicodeStringFromAsciiz( &lpFileW, lpFile );
2042     ret = RegRestoreKeyW( hkey, lpFileW.Buffer, dwFlags );
2043     RtlFreeUnicodeString( &lpFileW );
2044     return ret;
2045 }
2046
2047
2048 /******************************************************************************
2049  * RegUnLoadKeyW [ADVAPI32.@]
2050  *
2051  * Unload a registry key and its subkeys from the registry.
2052  *
2053  * PARAMS
2054  *  hkey     [I] Handle of open key
2055  *  lpSubKey [I] Address of name of subkey to unload
2056  *
2057  * RETURNS
2058  *  Success: ERROR_SUCCESS
2059  *  Failure: nonzero error code from Winerror.h
2060  */
2061 LONG WINAPI RegUnLoadKeyW( HKEY hkey, LPCWSTR lpSubKey )
2062 {
2063     DWORD ret;
2064     HKEY shkey;
2065
2066     TRACE("(%p,%s)\n",hkey, debugstr_w(lpSubKey));
2067
2068     ret = RegOpenKeyW(hkey,lpSubKey,&shkey);
2069     if( ret )
2070         return ERROR_INVALID_PARAMETER;
2071
2072     ret = RtlNtStatusToDosError(NtUnloadKey(shkey));
2073
2074     RegCloseKey(shkey);
2075
2076     return ret;
2077 }
2078
2079
2080 /******************************************************************************
2081  * RegUnLoadKeyA [ADVAPI32.@]
2082  *
2083  * See RegUnLoadKeyW.
2084  */
2085 LONG WINAPI RegUnLoadKeyA( HKEY hkey, LPCSTR lpSubKey )
2086 {
2087     UNICODE_STRING lpSubKeyW;
2088     LONG ret;
2089
2090     RtlCreateUnicodeStringFromAsciiz( &lpSubKeyW, lpSubKey );
2091     ret = RegUnLoadKeyW( hkey, lpSubKeyW.Buffer );
2092     RtlFreeUnicodeString( &lpSubKeyW );
2093     return ret;
2094 }
2095
2096
2097 /******************************************************************************
2098  * RegReplaceKeyW [ADVAPI32.@]
2099  *
2100  * Replace the file backing a registry key and all its subkeys with another file.
2101  *
2102  * PARAMS
2103  *  hkey      [I] Handle of open key
2104  *  lpSubKey  [I] Address of name of subkey
2105  *  lpNewFile [I] Address of filename for file with new data
2106  *  lpOldFile [I] Address of filename for backup file
2107  *
2108  * RETURNS
2109  *  Success: ERROR_SUCCESS
2110  *  Failure: nonzero error code from Winerror.h
2111  */
2112 LONG WINAPI RegReplaceKeyW( HKEY hkey, LPCWSTR lpSubKey, LPCWSTR lpNewFile,
2113                               LPCWSTR lpOldFile )
2114 {
2115     FIXME("(%p,%s,%s,%s): stub\n", hkey, debugstr_w(lpSubKey),
2116           debugstr_w(lpNewFile),debugstr_w(lpOldFile));
2117     return ERROR_SUCCESS;
2118 }
2119
2120
2121 /******************************************************************************
2122  * RegReplaceKeyA [ADVAPI32.@]
2123  *
2124  * See RegReplaceKeyW.
2125  */
2126 LONG WINAPI RegReplaceKeyA( HKEY hkey, LPCSTR lpSubKey, LPCSTR lpNewFile,
2127                               LPCSTR lpOldFile )
2128 {
2129     UNICODE_STRING lpSubKeyW;
2130     UNICODE_STRING lpNewFileW;
2131     UNICODE_STRING lpOldFileW;
2132     LONG ret;
2133
2134     RtlCreateUnicodeStringFromAsciiz( &lpSubKeyW, lpSubKey );
2135     RtlCreateUnicodeStringFromAsciiz( &lpOldFileW, lpOldFile );
2136     RtlCreateUnicodeStringFromAsciiz( &lpNewFileW, lpNewFile );
2137     ret = RegReplaceKeyW( hkey, lpSubKeyW.Buffer, lpNewFileW.Buffer, lpOldFileW.Buffer );
2138     RtlFreeUnicodeString( &lpOldFileW );
2139     RtlFreeUnicodeString( &lpNewFileW );
2140     RtlFreeUnicodeString( &lpSubKeyW );
2141     return ret;
2142 }
2143
2144
2145 /******************************************************************************
2146  * RegSetKeySecurity [ADVAPI32.@]
2147  *
2148  * Set the security of an open registry key.
2149  *
2150  * PARAMS
2151  *  hkey          [I] Open handle of key to set
2152  *  SecurityInfo  [I] Descriptor contents
2153  *  pSecurityDesc [I] Address of descriptor for key
2154  *
2155  * RETURNS
2156  *  Success: ERROR_SUCCESS
2157  *  Failure: nonzero error code from Winerror.h
2158  */
2159 LONG WINAPI RegSetKeySecurity( HKEY hkey, SECURITY_INFORMATION SecurityInfo,
2160                                PSECURITY_DESCRIPTOR pSecurityDesc )
2161 {
2162     TRACE("(%p,%ld,%p)\n",hkey,SecurityInfo,pSecurityDesc);
2163
2164     /* It seems to perform this check before the hkey check */
2165     if ((SecurityInfo & OWNER_SECURITY_INFORMATION) ||
2166         (SecurityInfo & GROUP_SECURITY_INFORMATION) ||
2167         (SecurityInfo & DACL_SECURITY_INFORMATION) ||
2168         (SecurityInfo & SACL_SECURITY_INFORMATION)) {
2169         /* Param OK */
2170     } else
2171         return ERROR_INVALID_PARAMETER;
2172
2173     if (!pSecurityDesc)
2174         return ERROR_INVALID_PARAMETER;
2175
2176     FIXME(":(%p,%ld,%p): stub\n",hkey,SecurityInfo,pSecurityDesc);
2177
2178     return ERROR_SUCCESS;
2179 }
2180
2181
2182 /******************************************************************************
2183  * RegGetKeySecurity [ADVAPI32.@]
2184  *
2185  * Get a copy of the security descriptor for a given registry key.
2186  *
2187  * PARAMS
2188  *  hkey                   [I]   Open handle of key to set
2189  *  SecurityInformation    [I]   Descriptor contents
2190  *  pSecurityDescriptor    [O]   Address of descriptor for key
2191  *  lpcbSecurityDescriptor [I/O] Address of size of buffer and description
2192  *
2193  * RETURNS
2194  *  Success: ERROR_SUCCESS
2195  *  Failure: Error code
2196  */
2197 LONG WINAPI RegGetKeySecurity( HKEY hkey, SECURITY_INFORMATION SecurityInformation,
2198                                PSECURITY_DESCRIPTOR pSecurityDescriptor,
2199                                LPDWORD lpcbSecurityDescriptor )
2200 {
2201     TRACE("(%p,%ld,%p,%ld)\n",hkey,SecurityInformation,pSecurityDescriptor,
2202           lpcbSecurityDescriptor?*lpcbSecurityDescriptor:0);
2203
2204     /* FIXME: Check for valid SecurityInformation values */
2205
2206     if (*lpcbSecurityDescriptor < sizeof(SECURITY_DESCRIPTOR))
2207         return ERROR_INSUFFICIENT_BUFFER;
2208
2209     FIXME("(%p,%ld,%p,%ld): stub\n",hkey,SecurityInformation,
2210           pSecurityDescriptor,lpcbSecurityDescriptor?*lpcbSecurityDescriptor:0);
2211
2212     /* Do not leave security descriptor filled with garbage */
2213     RtlCreateSecurityDescriptor(pSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION);
2214
2215     return ERROR_SUCCESS;
2216 }
2217
2218
2219 /******************************************************************************
2220  * RegFlushKey [ADVAPI32.@]
2221  * 
2222  * Immediately write a registry key to registry.
2223  *
2224  * PARAMS
2225  *  hkey [I] Handle of key to write
2226  *
2227  * RETURNS
2228  *  Success: ERROR_SUCCESS
2229  *  Failure: Error code
2230  */
2231 DWORD WINAPI RegFlushKey( HKEY hkey )
2232 {
2233     hkey = get_special_root_hkey( hkey );
2234     if (!hkey) return ERROR_INVALID_HANDLE;
2235
2236     return RtlNtStatusToDosError( NtFlushKey( hkey ) );
2237 }
2238
2239
2240 /******************************************************************************
2241  * RegConnectRegistryW [ADVAPI32.@]
2242  *
2243  * Establishe a connection to a predefined registry key on another computer.
2244  *
2245  * PARAMS
2246  *  lpMachineName [I] Address of name of remote computer
2247  *  hHey          [I] Predefined registry handle
2248  *  phkResult     [I] Address of buffer for remote registry handle
2249  *
2250  * RETURNS
2251  *  Success: ERROR_SUCCESS
2252  *  Failure: nonzero error code from Winerror.h
2253  */
2254 LONG WINAPI RegConnectRegistryW( LPCWSTR lpMachineName, HKEY hKey,
2255                                    PHKEY phkResult )
2256 {
2257     LONG ret;
2258
2259     TRACE("(%s,%p,%p): stub\n",debugstr_w(lpMachineName),hKey,phkResult);
2260
2261     if (!lpMachineName || !*lpMachineName) {
2262         /* Use the local machine name */
2263         ret = RegOpenKeyW( hKey, NULL, phkResult );
2264     }
2265     else {
2266         WCHAR compName[MAX_COMPUTERNAME_LENGTH + 1];
2267         DWORD len = sizeof(compName) / sizeof(WCHAR);
2268
2269         /* MSDN says lpMachineName must start with \\ : not so */
2270         if( lpMachineName[0] == '\\' &&  lpMachineName[1] == '\\')
2271             lpMachineName += 2;
2272         if (GetComputerNameW(compName, &len))
2273         {
2274             if (!strcmpiW(lpMachineName, compName))
2275                 ret = RegOpenKeyW(hKey, NULL, phkResult);
2276             else
2277             {
2278                 FIXME("Connect to %s is not supported.\n",debugstr_w(lpMachineName));
2279                 ret = ERROR_BAD_NETPATH;
2280             }
2281         }
2282         else
2283             ret = GetLastError();
2284     }
2285     return ret;
2286 }
2287
2288
2289 /******************************************************************************
2290  * RegConnectRegistryA [ADVAPI32.@]
2291  *
2292  * See RegConnectRegistryW.
2293  */
2294 LONG WINAPI RegConnectRegistryA( LPCSTR machine, HKEY hkey, PHKEY reskey )
2295 {
2296     UNICODE_STRING machineW;
2297     LONG ret;
2298
2299     RtlCreateUnicodeStringFromAsciiz( &machineW, machine );
2300     ret = RegConnectRegistryW( machineW.Buffer, hkey, reskey );
2301     RtlFreeUnicodeString( &machineW );
2302     return ret;
2303 }
2304
2305
2306 /******************************************************************************
2307  * RegNotifyChangeKeyValue [ADVAPI32.@]
2308  *
2309  * Notify the caller about changes to the attributes or contents of a registry key.
2310  *
2311  * PARAMS
2312  *  hkey            [I] Handle of key to watch
2313  *  fWatchSubTree   [I] Flag for subkey notification
2314  *  fdwNotifyFilter [I] Changes to be reported
2315  *  hEvent          [I] Handle of signaled event
2316  *  fAsync          [I] Flag for asynchronous reporting
2317  *
2318  * RETURNS
2319  *  Success: ERROR_SUCCESS
2320  *  Failure: nonzero error code from Winerror.h
2321  */
2322 LONG WINAPI RegNotifyChangeKeyValue( HKEY hkey, BOOL fWatchSubTree,
2323                                      DWORD fdwNotifyFilter, HANDLE hEvent,
2324                                      BOOL fAsync )
2325 {
2326     NTSTATUS status;
2327     IO_STATUS_BLOCK iosb;
2328
2329     hkey = get_special_root_hkey( hkey );
2330     if (!hkey) return ERROR_INVALID_HANDLE;
2331
2332     TRACE("(%p,%i,%ld,%p,%i)\n", hkey, fWatchSubTree, fdwNotifyFilter,
2333           hEvent, fAsync);
2334
2335     status = NtNotifyChangeKey( hkey, hEvent, NULL, NULL, &iosb,
2336                                 fdwNotifyFilter, fWatchSubTree, NULL, 0,
2337                                 fAsync );
2338
2339     if (status && status != STATUS_TIMEOUT)
2340         return RtlNtStatusToDosError( status );
2341
2342     return ERROR_SUCCESS;
2343 }
2344
2345 /******************************************************************************
2346  * RegOpenUserClassesRoot [ADVAPI32.@]
2347  *
2348  * Open the HKEY_CLASSES_ROOT key for a user.
2349  *
2350  * PARAMS
2351  *  hToken     [I] Handle of token representing the user
2352  *  dwOptions  [I] Reserved, nust be 0
2353  *  samDesired [I] Desired access rights
2354  *  phkResult  [O] Destination for the resulting key handle
2355  *
2356  * RETURNS
2357  *  Success: ERROR_SUCCESS
2358  *  Failure: nonzero error code from Winerror.h
2359  * 
2360  * NOTES
2361  *  On Windows 2000 and upwards the HKEY_CLASSES_ROOT key is a view of the
2362  *  "HKEY_LOCAL_MACHINE\Software\Classes" and the
2363  *  "HKEY_CURRENT_USER\Software\Classes" keys merged together.
2364  */
2365 LONG WINAPI RegOpenUserClassesRoot(
2366     HANDLE hToken,
2367     DWORD dwOptions,
2368     REGSAM samDesired,
2369     PHKEY phkResult
2370 )
2371 {
2372     FIXME("(%p, 0x%lx, 0x%lx, %p) semi-stub\n", hToken, dwOptions, samDesired, phkResult);
2373
2374     *phkResult = HKEY_CLASSES_ROOT;
2375     return ERROR_SUCCESS;
2376 }