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