Fixed some handle type mismatches and added a few casts in prevision
[wine] / memory / 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 <stdio.h>
31 #include <unistd.h>
32
33 #include "winbase.h"
34 #include "winreg.h"
35 #include "winerror.h"
36 #include "wine/winbase16.h"
37 #include "wine/unicode.h"
38 #include "wine/server.h"
39 #include "wine/debug.h"
40
41 WINE_DEFAULT_DEBUG_CHANNEL(reg);
42
43
44 /* check if value type needs string conversion (Ansi<->Unicode) */
45 static inline int is_string( DWORD type )
46 {
47     return (type == REG_SZ) || (type == REG_EXPAND_SZ) || (type == REG_MULTI_SZ);
48 }
49
50
51 /******************************************************************************
52  *           RegCreateKeyExA   [ADVAPI32.@]
53  */
54 DWORD WINAPI RegCreateKeyExA( HKEY hkey, LPCSTR name, DWORD reserved, LPSTR class,
55                               DWORD options, REGSAM access, SECURITY_ATTRIBUTES *sa,
56                               LPHKEY retkey, LPDWORD dispos )
57 {
58     OBJECT_ATTRIBUTES attr;
59     UNICODE_STRING nameW, classW;
60     ANSI_STRING nameA, classA;
61     NTSTATUS status;
62
63     if (reserved) return ERROR_INVALID_PARAMETER;
64     if (!(access & KEY_ALL_ACCESS) || (access & ~KEY_ALL_ACCESS)) return ERROR_ACCESS_DENIED;
65
66     attr.Length = sizeof(attr);
67     attr.RootDirectory = hkey;
68     attr.ObjectName = &nameW;
69     attr.Attributes = 0;
70     attr.SecurityDescriptor = NULL;
71     attr.SecurityQualityOfService = NULL;
72     RtlInitAnsiString( &nameA, name );
73     RtlInitAnsiString( &classA, class );
74
75     /* FIXME: should use Unicode buffer in TEB */
76     if (!(status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE )))
77     {
78         if (!(status = RtlAnsiStringToUnicodeString( &classW, &classA, TRUE )))
79         {
80             status = NtCreateKey( retkey, access, &attr, 0, &classW, options, dispos );
81             RtlFreeUnicodeString( &classW );
82         }
83         RtlFreeUnicodeString( &nameW );
84     }
85     return RtlNtStatusToDosError( status );
86 }
87
88
89 /******************************************************************************
90  *           RegCreateKeyA   [ADVAPI32.@]
91  */
92 DWORD WINAPI RegCreateKeyA( HKEY hkey, LPCSTR name, LPHKEY retkey )
93 {
94     return RegCreateKeyExA( hkey, name, 0, NULL, REG_OPTION_NON_VOLATILE,
95                             KEY_ALL_ACCESS, NULL, retkey, NULL );
96 }
97
98
99
100 /******************************************************************************
101  *           RegOpenKeyExA   [ADVAPI32.@]
102  */
103 DWORD WINAPI RegOpenKeyExA( HKEY hkey, LPCSTR name, DWORD reserved, REGSAM access, LPHKEY retkey )
104 {
105     OBJECT_ATTRIBUTES attr;
106     UNICODE_STRING nameW;
107     STRING nameA;
108     NTSTATUS status;
109
110     attr.Length = sizeof(attr);
111     attr.RootDirectory = hkey;
112     attr.ObjectName = &nameW;
113     attr.Attributes = 0;
114     attr.SecurityDescriptor = NULL;
115     attr.SecurityQualityOfService = NULL;
116
117     RtlInitAnsiString( &nameA, name );
118     /* FIXME: should use Unicode buffer in TEB */
119     if (!(status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE )))
120     {
121         status = NtOpenKey( retkey, access, &attr );
122         RtlFreeUnicodeString( &nameW );
123     }
124     return RtlNtStatusToDosError( status );
125 }
126
127
128 /******************************************************************************
129  *           RegOpenKeyA   [ADVAPI32.@]
130  */
131 DWORD WINAPI RegOpenKeyA( HKEY hkey, LPCSTR name, LPHKEY retkey )
132 {
133     return RegOpenKeyExA( hkey, name, 0, KEY_ALL_ACCESS, retkey );
134 }
135
136
137 /******************************************************************************
138  *           RegEnumKeyExA   [ADVAPI32.@]
139  */
140 DWORD WINAPI RegEnumKeyExA( HKEY hkey, DWORD index, LPSTR name, LPDWORD name_len,
141                             LPDWORD reserved, LPSTR class, LPDWORD class_len, FILETIME *ft )
142 {
143     NTSTATUS status;
144     char buffer[256], *buf_ptr = buffer;
145     KEY_NODE_INFORMATION *info = (KEY_NODE_INFORMATION *)buffer;
146     DWORD total_size;
147
148     TRACE( "(0x%x,%ld,%p,%p(%ld),%p,%p,%p,%p)\n", hkey, index, name, name_len,
149            name_len ? *name_len : -1, reserved, class, class_len, ft );
150
151     if (reserved) return ERROR_INVALID_PARAMETER;
152
153     status = NtEnumerateKey( hkey, index, KeyNodeInformation,
154                              buffer, sizeof(buffer), &total_size );
155
156     while (status == STATUS_BUFFER_OVERFLOW)
157     {
158         /* retry with a dynamically allocated buffer */
159         if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
160         if (!(buf_ptr = HeapAlloc( GetProcessHeap(), 0, total_size )))
161             return ERROR_NOT_ENOUGH_MEMORY;
162         info = (KEY_NODE_INFORMATION *)buf_ptr;
163         status = NtEnumerateKey( hkey, index, KeyNodeInformation,
164                                  buf_ptr, total_size, &total_size );
165     }
166
167     if (!status)
168     {
169         DWORD len, cls_len;
170
171         RtlUnicodeToMultiByteSize( &len, info->Name, info->NameLength );
172         RtlUnicodeToMultiByteSize( &cls_len, (WCHAR *)(buf_ptr + info->ClassOffset),
173                                    info->ClassLength );
174         if (ft) *ft = *(FILETIME *)&info->LastWriteTime;
175
176         if (len >= *name_len || (class_len && (cls_len >= *class_len)))
177             status = STATUS_BUFFER_OVERFLOW;
178         else
179         {
180             *name_len = len;
181             RtlUnicodeToMultiByteN( name, len, NULL, info->Name, info->NameLength );
182             name[len] = 0;
183             if (class_len)
184             {
185                 *class_len = cls_len;
186                 if (class)
187                 {
188                     RtlUnicodeToMultiByteN( class, cls_len, NULL,
189                                             (WCHAR *)(buf_ptr + info->ClassOffset),
190                                             info->ClassLength );
191                     class[cls_len] = 0;
192                 }
193             }
194         }
195     }
196
197     if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
198     return RtlNtStatusToDosError( status );
199 }
200
201
202 /******************************************************************************
203  *           RegEnumKeyA   [ADVAPI32.@]
204  */
205 DWORD WINAPI RegEnumKeyA( HKEY hkey, DWORD index, LPSTR name, DWORD name_len )
206 {
207     return RegEnumKeyExA( hkey, index, name, &name_len, NULL, NULL, NULL, NULL );
208 }
209
210
211 /******************************************************************************
212  *           RegQueryInfoKeyA   [ADVAPI32.@]
213  */
214 DWORD WINAPI RegQueryInfoKeyA( HKEY hkey, LPSTR class, LPDWORD class_len, LPDWORD reserved,
215                                LPDWORD subkeys, LPDWORD max_subkey, LPDWORD max_class,
216                                LPDWORD values, LPDWORD max_value, LPDWORD max_data,
217                                LPDWORD security, FILETIME *modif )
218 {
219     NTSTATUS status;
220     char buffer[256], *buf_ptr = buffer;
221     KEY_FULL_INFORMATION *info = (KEY_FULL_INFORMATION *)buffer;
222     DWORD total_size, len;
223
224     TRACE( "(0x%x,%p,%ld,%p,%p,%p,%p,%p,%p,%p,%p)\n", hkey, class, class_len ? *class_len : 0,
225            reserved, subkeys, max_subkey, values, max_value, max_data, security, modif );
226
227     if (class && !class_len && !(GetVersion() & 0x80000000 /*NT*/))
228         return ERROR_INVALID_PARAMETER;
229
230     status = NtQueryKey( hkey, KeyFullInformation, buffer, sizeof(buffer), &total_size );
231     if (status && status != STATUS_BUFFER_OVERFLOW) goto done;
232
233     if (class || class_len)
234     {
235         /* retry with a dynamically allocated buffer */
236         while (status == STATUS_BUFFER_OVERFLOW)
237         {
238             if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
239             if (!(buf_ptr = HeapAlloc( GetProcessHeap(), 0, total_size )))
240                 return ERROR_NOT_ENOUGH_MEMORY;
241             info = (KEY_FULL_INFORMATION *)buf_ptr;
242             status = NtQueryKey( hkey, KeyFullInformation, buf_ptr, total_size, &total_size );
243         }
244
245         if (status) goto done;
246
247         RtlUnicodeToMultiByteSize( &len, (WCHAR *)(buf_ptr + info->ClassOffset), info->ClassLength);
248         if (class_len)
249         {
250             if (len + 1 > *class_len) status = STATUS_BUFFER_OVERFLOW;
251             *class_len = len;
252         }
253         if (class && !status)
254         {
255             RtlUnicodeToMultiByteN( class, len, NULL, (WCHAR *)(buf_ptr + info->ClassOffset),
256                                     info->ClassLength );
257             class[len] = 0;
258         }
259     }
260     else status = STATUS_SUCCESS;
261
262     if (subkeys) *subkeys = info->SubKeys;
263     if (max_subkey) *max_subkey = info->MaxNameLen;
264     if (max_class) *max_class = info->MaxClassLen;
265     if (values) *values = info->Values;
266     if (max_value) *max_value = info->MaxValueNameLen;
267     if (max_data) *max_data = info->MaxValueDataLen;
268     if (modif) *modif = *(FILETIME *)&info->LastWriteTime;
269
270  done:
271     if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
272     return RtlNtStatusToDosError( status );
273 }
274
275
276 /******************************************************************************
277  *           RegCloseKey   [ADVAPI32.@]
278  *
279  * Releases the handle of the specified key
280  *
281  * PARAMS
282  *    hkey [I] Handle of key to close
283  *
284  * RETURNS
285  *    Success: ERROR_SUCCESS
286  *    Failure: Error code
287  */
288 DWORD WINAPI RegCloseKey( HKEY hkey )
289 {
290     if (!hkey || hkey >= (HKEY)0x80000000) return ERROR_SUCCESS;
291     return RtlNtStatusToDosError( NtClose( hkey ) );
292 }
293
294
295 /******************************************************************************
296  *           RegDeleteKeyA   [ADVAPI32.@]
297  */
298 DWORD WINAPI RegDeleteKeyA( HKEY hkey, LPCSTR name )
299 {
300     DWORD ret;
301     HKEY tmp;
302
303     if (!name || !*name) return NtDeleteKey( hkey );
304     if (!(ret = RegOpenKeyExA( hkey, name, 0, 0, &tmp )))
305     {
306         ret = RtlNtStatusToDosError( NtDeleteKey( tmp ) );
307         RegCloseKey( tmp );
308     }
309     return ret;
310 }
311
312
313
314 /******************************************************************************
315  *           RegSetValueExA   [ADVAPI32.@]
316  */
317 DWORD WINAPI RegSetValueExA( HKEY hkey, LPCSTR name, DWORD reserved, DWORD type,
318                              CONST BYTE *data, DWORD count )
319 {
320     UNICODE_STRING nameW;
321     ANSI_STRING nameA;
322     WCHAR *dataW = NULL;
323     NTSTATUS status;
324
325     if (count && is_string(type))
326     {
327         /* if user forgot to count terminating null, add it (yes NT does this) */
328         if (data[count-1] && !data[count]) count++;
329     }
330
331     if (is_string( type )) /* need to convert to Unicode */
332     {
333         DWORD lenW;
334         RtlMultiByteToUnicodeSize( &lenW, data, count );
335         if (!(dataW = HeapAlloc( GetProcessHeap(), 0, lenW ))) return ERROR_OUTOFMEMORY;
336         RtlMultiByteToUnicodeN( dataW, lenW, NULL, data, count );
337         count = lenW;
338         data = (BYTE *)dataW;
339     }
340
341     RtlInitAnsiString( &nameA, name );
342     /* FIXME: should use Unicode buffer in TEB */
343     if (!(status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE )))
344     {
345         status = NtSetValueKey( hkey, &nameW, 0, type, data, count );
346         RtlFreeUnicodeString( &nameW );
347     }
348     if (dataW) HeapFree( GetProcessHeap(), 0, dataW );
349     return RtlNtStatusToDosError( status );
350 }
351
352
353 /******************************************************************************
354  *           RegSetValueA   [ADVAPI32.@]
355  */
356 DWORD WINAPI RegSetValueA( HKEY hkey, LPCSTR name, DWORD type, LPCSTR data, DWORD count )
357 {
358     HKEY subkey = hkey;
359     DWORD ret;
360
361     TRACE("(0x%x,%s,%ld,%s,%ld)\n", hkey, debugstr_a(name), type, debugstr_a(data), count );
362
363     if (type != REG_SZ) return ERROR_INVALID_PARAMETER;
364
365     if (name && name[0])  /* need to create the subkey */
366     {
367         if ((ret = RegCreateKeyA( hkey, name, &subkey )) != ERROR_SUCCESS) return ret;
368     }
369     ret = RegSetValueExA( subkey, NULL, 0, REG_SZ, (LPBYTE)data, strlen(data)+1 );
370     if (subkey != hkey) RegCloseKey( subkey );
371     return ret;
372 }
373
374
375
376 /******************************************************************************
377  *           RegQueryValueExA   [ADVAPI32.@]
378  *
379  * NOTES:
380  * the documentation is wrong: if the buffer is too small it remains untouched
381  */
382 DWORD WINAPI RegQueryValueExA( HKEY hkey, LPCSTR name, LPDWORD reserved, LPDWORD type,
383                                LPBYTE data, LPDWORD count )
384 {
385     NTSTATUS status;
386     ANSI_STRING nameA;
387     UNICODE_STRING nameW;
388     DWORD total_size;
389     char buffer[256], *buf_ptr = buffer;
390     KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
391     static const int info_size = offsetof( KEY_VALUE_PARTIAL_INFORMATION, Data );
392
393     TRACE("(0x%x,%s,%p,%p,%p,%p=%ld)\n",
394           hkey, debugstr_a(name), reserved, type, data, count, count ? *count : 0 );
395
396     if ((data && !count) || reserved) return ERROR_INVALID_PARAMETER;
397
398     RtlInitAnsiString( &nameA, name );
399     /* FIXME: should use Unicode buffer in TEB */
400     if ((status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE )))
401         return RtlNtStatusToDosError(status);
402
403     status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation,
404                               buffer, sizeof(buffer), &total_size );
405     if (status && status != STATUS_BUFFER_OVERFLOW) goto done;
406
407     /* we need to fetch the contents for a string type even if not requested,
408      * because we need to compute the length of the ASCII string. */
409     if (data || is_string(info->Type))
410     {
411         /* retry with a dynamically allocated buffer */
412         while (status == STATUS_BUFFER_OVERFLOW)
413         {
414             if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
415             if (!(buf_ptr = HeapAlloc( GetProcessHeap(), 0, total_size )))
416             {
417                 status = STATUS_NO_MEMORY;
418                 goto done;
419             }
420             info = (KEY_VALUE_PARTIAL_INFORMATION *)buf_ptr;
421             status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation,
422                                       buf_ptr, total_size, &total_size );
423         }
424
425         if (!status)
426         {
427             if (is_string(info->Type))
428             {
429                 DWORD len = WideCharToMultiByte( CP_ACP, 0, (WCHAR *)(buf_ptr + info_size),
430                                                  (total_size - info_size) /sizeof(WCHAR),
431                                                  NULL, 0, NULL, NULL );
432                 if (data && len)
433                 {
434                     if (len > *count) status = STATUS_BUFFER_OVERFLOW;
435                     else
436                     {
437                         WideCharToMultiByte( CP_ACP, 0, (WCHAR *)(buf_ptr + info_size),
438                                              (total_size - info_size) /sizeof(WCHAR),
439                                              data, len, NULL, NULL );
440                         /* if the type is REG_SZ and data is not 0-terminated
441                          * and there is enough space in the buffer NT appends a \0 */
442                         if (len < *count && data[len-1]) data[len] = 0;
443                     }
444                 }
445                 total_size = len + info_size;
446             }
447             else if (data)
448             {
449                 if (total_size - info_size > *count) status = STATUS_BUFFER_OVERFLOW;
450                 else memcpy( data, buf_ptr + info_size, total_size - info_size );
451             }
452         }
453         else if (status != STATUS_BUFFER_OVERFLOW) goto done;
454     }
455
456     if (type) *type = info->Type;
457     if (count) *count = total_size - info_size;
458
459  done:
460     if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
461     RtlFreeUnicodeString( &nameW );
462     return RtlNtStatusToDosError(status);
463 }
464
465
466 /******************************************************************************
467  *           RegQueryValueA   [ADVAPI32.@]
468  */
469 DWORD WINAPI RegQueryValueA( HKEY hkey, LPCSTR name, LPSTR data, LPLONG count )
470 {
471     DWORD ret;
472     HKEY subkey = hkey;
473
474     TRACE("(%x,%s,%p,%ld)\n", hkey, debugstr_a(name), data, count ? *count : 0 );
475
476     if (name && name[0])
477     {
478         if ((ret = RegOpenKeyA( hkey, name, &subkey )) != ERROR_SUCCESS) return ret;
479     }
480     ret = RegQueryValueExA( subkey, NULL, NULL, NULL, (LPBYTE)data, count );
481     if (subkey != hkey) RegCloseKey( subkey );
482     if (ret == ERROR_FILE_NOT_FOUND)
483     {
484         /* return empty string if default value not found */
485         if (data) *data = 0;
486         if (count) *count = 1;
487         ret = ERROR_SUCCESS;
488     }
489     return ret;
490 }
491
492
493 /******************************************************************************
494  *           RegEnumValueA   [ADVAPI32.@]
495  */
496 DWORD WINAPI RegEnumValueA( HKEY hkey, DWORD index, LPSTR value, LPDWORD val_count,
497                             LPDWORD reserved, LPDWORD type, LPBYTE data, LPDWORD count )
498 {
499     NTSTATUS status;
500     DWORD total_size;
501     char buffer[256], *buf_ptr = buffer;
502     KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
503     static const int info_size = offsetof( KEY_VALUE_FULL_INFORMATION, Name );
504
505     TRACE("(%x,%ld,%p,%p,%p,%p,%p,%p)\n",
506           hkey, index, value, val_count, reserved, type, data, count );
507
508     /* NT only checks count, not val_count */
509     if ((data && !count) || reserved) return ERROR_INVALID_PARAMETER;
510
511     total_size = info_size + (MAX_PATH + 1) * sizeof(WCHAR);
512     if (data) total_size += *count;
513     total_size = min( sizeof(buffer), total_size );
514
515     status = NtEnumerateValueKey( hkey, index, KeyValueFullInformation,
516                                   buffer, total_size, &total_size );
517     if (status && status != STATUS_BUFFER_OVERFLOW) goto done;
518
519     /* we need to fetch the contents for a string type even if not requested,
520      * because we need to compute the length of the ASCII string. */
521     if (value || data || is_string(info->Type))
522     {
523         /* retry with a dynamically allocated buffer */
524         while (status == STATUS_BUFFER_OVERFLOW)
525         {
526             if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
527             if (!(buf_ptr = HeapAlloc( GetProcessHeap(), 0, total_size )))
528                 return ERROR_NOT_ENOUGH_MEMORY;
529             info = (KEY_VALUE_FULL_INFORMATION *)buf_ptr;
530             status = NtEnumerateValueKey( hkey, index, KeyValueFullInformation,
531                                           buf_ptr, total_size, &total_size );
532         }
533
534         if (status) goto done;
535
536         if (is_string(info->Type))
537         {
538             DWORD len;
539             RtlUnicodeToMultiByteSize( &len, (WCHAR *)(buf_ptr + info->DataOffset),
540                                        total_size - info->DataOffset );
541             if (data && len)
542             {
543                 if (len > *count) status = STATUS_BUFFER_OVERFLOW;
544                 else
545                 {
546                     RtlUnicodeToMultiByteN( data, len, NULL, (WCHAR *)(buf_ptr + info->DataOffset),
547                                             total_size - info->DataOffset );
548                     /* if the type is REG_SZ and data is not 0-terminated
549                      * and there is enough space in the buffer NT appends a \0 */
550                     if (len < *count && data[len-1]) data[len] = 0;
551                 }
552             }
553             info->DataLength = len;
554         }
555         else if (data)
556         {
557             if (total_size - info->DataOffset > *count) status = STATUS_BUFFER_OVERFLOW;
558             else memcpy( data, buf_ptr + info->DataOffset, total_size - info->DataOffset );
559         }
560
561         if (value && !status)
562         {
563             DWORD len;
564
565             RtlUnicodeToMultiByteSize( &len, info->Name, info->NameLength );
566             if (len >= *val_count)
567             {
568                 status = STATUS_BUFFER_OVERFLOW;
569                 if (*val_count)
570                 {
571                     len = *val_count - 1;
572                     RtlUnicodeToMultiByteN( value, len, NULL, info->Name, info->NameLength );
573                     value[len] = 0;
574                 }
575             }
576             else
577             {
578                 RtlUnicodeToMultiByteN( value, len, NULL, info->Name, info->NameLength );
579                 value[len] = 0;
580                 *val_count = len;
581             }
582         }
583     }
584     else status = STATUS_SUCCESS;
585
586     if (type) *type = info->Type;
587     if (count) *count = info->DataLength;
588
589  done:
590     if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
591     return RtlNtStatusToDosError(status);
592 }
593
594
595
596 /******************************************************************************
597  *           RegDeleteValueA   [ADVAPI32.@]
598  */
599 DWORD WINAPI RegDeleteValueA( HKEY hkey, LPCSTR name )
600 {
601     UNICODE_STRING nameW;
602     STRING nameA;
603     NTSTATUS status;
604
605     RtlInitAnsiString( &nameA, name );
606     /* FIXME: should use Unicode buffer in TEB */
607     if (!(status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE )))
608     {
609         status = NtDeleteValueKey( hkey, &nameW );
610         RtlFreeUnicodeString( &nameW );
611     }
612     return RtlNtStatusToDosError( status );
613 }
614
615
616 /******************************************************************************
617  *           RegLoadKeyA   [ADVAPI32.@]
618  */
619 LONG WINAPI RegLoadKeyA( HKEY hkey, LPCSTR subkey, LPCSTR filename )
620 {
621     HANDLE file;
622     WCHAR buffer[MAX_PATH];
623     DWORD ret, len, err = GetLastError();
624
625     TRACE( "(%x,%s,%s)\n", hkey, debugstr_a(subkey), debugstr_a(filename) );
626
627     if (!filename || !*filename) return ERROR_INVALID_PARAMETER;
628     if (!subkey || !*subkey) return ERROR_INVALID_PARAMETER;
629
630     if (!(len = MultiByteToWideChar( CP_ACP, 0, subkey, strlen(subkey), buffer, MAX_PATH )))
631         return ERROR_INVALID_PARAMETER;
632
633     if ((file = CreateFileA( filename, GENERIC_READ, 0, NULL, OPEN_EXISTING,
634                              FILE_ATTRIBUTE_NORMAL, 0 )) == INVALID_HANDLE_VALUE)
635     {
636         ret = GetLastError();
637         goto done;
638     }
639
640     SERVER_START_REQ( load_registry )
641     {
642         req->hkey  = hkey;
643         req->file  = file;
644         wine_server_add_data( req, buffer, len * sizeof(WCHAR) );
645         ret = RtlNtStatusToDosError( wine_server_call(req) );
646     }
647     SERVER_END_REQ;
648     CloseHandle( file );
649
650  done:
651     SetLastError( err );  /* restore the last error code */
652     return ret;
653 }
654
655
656 /******************************************************************************
657  *           RegSaveKeyA   [ADVAPI32.@]
658  *
659  * PARAMS
660  *    hkey   [I] Handle of key where save begins
661  *    lpFile [I] Address of filename to save to
662  *    sa     [I] Address of security structure
663  */
664 LONG WINAPI RegSaveKeyA( HKEY hkey, LPCSTR file, LPSECURITY_ATTRIBUTES sa )
665 {
666     char buffer[1024];
667     int count = 0;
668     LPSTR name;
669     DWORD ret, err;
670     HANDLE handle;
671
672     TRACE( "(%x,%s,%p)\n", hkey, debugstr_a(file), sa );
673
674     if (!file || !*file) return ERROR_INVALID_PARAMETER;
675
676     err = GetLastError();
677     GetFullPathNameA( file, sizeof(buffer), buffer, &name );
678     for (;;)
679     {
680         sprintf( name, "reg%04x.tmp", count++ );
681         handle = CreateFileA( buffer, GENERIC_WRITE, 0, NULL,
682                             CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
683         if (handle != INVALID_HANDLE_VALUE) break;
684         if ((ret = GetLastError()) != ERROR_ALREADY_EXISTS) goto done;
685
686         /* Something gone haywire ? Please report if this happens abnormally */
687         if (count >= 100)
688             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", buffer, count);
689     }
690
691     SERVER_START_REQ( save_registry )
692     {
693         req->hkey = hkey;
694         req->file = handle;
695         ret = RtlNtStatusToDosError( wine_server_call( req ) );
696     }
697     SERVER_END_REQ;
698
699     CloseHandle( handle );
700     if (!ret)
701     {
702         if (!MoveFileExA( buffer, file, MOVEFILE_REPLACE_EXISTING ))
703         {
704             ERR( "Failed to move %s to %s\n", buffer, file );
705             ret = GetLastError();
706         }
707     }
708     if (ret) DeleteFileA( buffer );
709
710 done:
711     SetLastError( err );  /* restore last error code */
712     return ret;
713 }