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