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