Added some tests.
[wine/multimedia.git] / memory / registry.c
blob9bd7024713fb3fdd189e10775bdd7deb5451f911
1 /*
2 * Registry management
4 * Copyright (C) 1999 Alexandre Julliard
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
11 * This file is concerned about handle management and interaction with the Wine server.
12 * Registry file I/O is in misc/registry.c.
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.
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.
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
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <unistd.h>
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"
41 WINE_DEFAULT_DEBUG_CHANNEL(reg);
44 /* check if value type needs string conversion (Ansi<->Unicode) */
45 static inline int is_string( DWORD type )
47 return (type == REG_SZ) || (type == REG_EXPAND_SZ) || (type == REG_MULTI_SZ);
51 /******************************************************************************
52 * RegCreateKeyExA [ADVAPI32.@]
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 )
58 OBJECT_ATTRIBUTES attr;
59 UNICODE_STRING nameW, classW;
60 ANSI_STRING nameA, classA;
61 NTSTATUS status;
63 if (reserved) return ERROR_INVALID_PARAMETER;
64 if (!(access & KEY_ALL_ACCESS) || (access & ~KEY_ALL_ACCESS)) return ERROR_ACCESS_DENIED;
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 );
75 /* FIXME: should use Unicode buffer in TEB */
76 if (!(status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE )))
78 if (!(status = RtlAnsiStringToUnicodeString( &classW, &classA, TRUE )))
80 status = NtCreateKey( retkey, access, &attr, 0, &classW, options, dispos );
81 RtlFreeUnicodeString( &classW );
83 RtlFreeUnicodeString( &nameW );
85 return RtlNtStatusToDosError( status );
89 /******************************************************************************
90 * RegCreateKeyA [ADVAPI32.@]
92 DWORD WINAPI RegCreateKeyA( HKEY hkey, LPCSTR name, LPHKEY retkey )
94 return RegCreateKeyExA( hkey, name, 0, NULL, REG_OPTION_NON_VOLATILE,
95 KEY_ALL_ACCESS, NULL, retkey, NULL );
100 /******************************************************************************
101 * RegOpenKeyExA [ADVAPI32.@]
103 DWORD WINAPI RegOpenKeyExA( HKEY hkey, LPCSTR name, DWORD reserved, REGSAM access, LPHKEY retkey )
105 OBJECT_ATTRIBUTES attr;
106 UNICODE_STRING nameW;
107 STRING nameA;
108 NTSTATUS status;
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;
117 RtlInitAnsiString( &nameA, name );
118 /* FIXME: should use Unicode buffer in TEB */
119 if (!(status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE )))
121 status = NtOpenKey( retkey, access, &attr );
122 RtlFreeUnicodeString( &nameW );
124 return RtlNtStatusToDosError( status );
128 /******************************************************************************
129 * RegOpenKeyA [ADVAPI32.@]
131 DWORD WINAPI RegOpenKeyA( HKEY hkey, LPCSTR name, LPHKEY retkey )
133 return RegOpenKeyExA( hkey, name, 0, KEY_ALL_ACCESS, retkey );
137 /******************************************************************************
138 * RegEnumKeyExA [ADVAPI32.@]
140 DWORD WINAPI RegEnumKeyExA( HKEY hkey, DWORD index, LPSTR name, LPDWORD name_len,
141 LPDWORD reserved, LPSTR class, LPDWORD class_len, FILETIME *ft )
143 NTSTATUS status;
144 char buffer[256], *buf_ptr = buffer;
145 KEY_NODE_INFORMATION *info = (KEY_NODE_INFORMATION *)buffer;
146 DWORD total_size;
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 );
151 if (reserved) return ERROR_INVALID_PARAMETER;
153 status = NtEnumerateKey( hkey, index, KeyNodeInformation,
154 buffer, sizeof(buffer), &total_size );
156 while (status == STATUS_BUFFER_OVERFLOW)
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 );
167 if (!status)
169 DWORD len, cls_len;
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;
176 if (len >= *name_len || (class_len && (cls_len >= *class_len)))
177 status = STATUS_BUFFER_OVERFLOW;
178 else
180 *name_len = len;
181 RtlUnicodeToMultiByteN( name, len, NULL, info->Name, info->NameLength );
182 name[len] = 0;
183 if (class_len)
185 *class_len = cls_len;
186 if (class)
188 RtlUnicodeToMultiByteN( class, cls_len, NULL,
189 (WCHAR *)(buf_ptr + info->ClassOffset),
190 info->ClassLength );
191 class[cls_len] = 0;
197 if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
198 return RtlNtStatusToDosError( status );
202 /******************************************************************************
203 * RegEnumKeyA [ADVAPI32.@]
205 DWORD WINAPI RegEnumKeyA( HKEY hkey, DWORD index, LPSTR name, DWORD name_len )
207 return RegEnumKeyExA( hkey, index, name, &name_len, NULL, NULL, NULL, NULL );
211 /******************************************************************************
212 * RegQueryInfoKeyA [ADVAPI32.@]
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 )
219 NTSTATUS status;
220 char buffer[256], *buf_ptr = buffer;
221 KEY_FULL_INFORMATION *info = (KEY_FULL_INFORMATION *)buffer;
222 DWORD total_size, len;
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 );
227 if (class && !class_len && !(GetVersion() & 0x80000000 /*NT*/))
228 return ERROR_INVALID_PARAMETER;
230 status = NtQueryKey( hkey, KeyFullInformation, buffer, sizeof(buffer), &total_size );
231 if (status && status != STATUS_BUFFER_OVERFLOW) goto done;
233 if (class || class_len)
235 /* retry with a dynamically allocated buffer */
236 while (status == STATUS_BUFFER_OVERFLOW)
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 );
245 if (status) goto done;
247 RtlUnicodeToMultiByteSize( &len, (WCHAR *)(buf_ptr + info->ClassOffset), info->ClassLength);
248 if (class_len)
250 if (len + 1 > *class_len) status = STATUS_BUFFER_OVERFLOW;
251 *class_len = len;
253 if (class && !status)
255 RtlUnicodeToMultiByteN( class, len, NULL, (WCHAR *)(buf_ptr + info->ClassOffset),
256 info->ClassLength );
257 class[len] = 0;
260 else status = STATUS_SUCCESS;
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;
270 done:
271 if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
272 return RtlNtStatusToDosError( status );
276 /******************************************************************************
277 * RegCloseKey [ADVAPI32.@]
279 * Releases the handle of the specified key
281 * PARAMS
282 * hkey [I] Handle of key to close
284 * RETURNS
285 * Success: ERROR_SUCCESS
286 * Failure: Error code
288 DWORD WINAPI RegCloseKey( HKEY hkey )
290 if (!hkey || hkey >= 0x80000000) return ERROR_SUCCESS;
291 return RtlNtStatusToDosError( NtClose( hkey ) );
295 /******************************************************************************
296 * RegDeleteKeyA [ADVAPI32.@]
298 DWORD WINAPI RegDeleteKeyA( HKEY hkey, LPCSTR name )
300 DWORD ret;
301 HKEY tmp;
303 if (!name || !*name) return NtDeleteKey( hkey );
304 if (!(ret = RegOpenKeyExA( hkey, name, 0, 0, &tmp )))
306 ret = RtlNtStatusToDosError( NtDeleteKey( tmp ) );
307 RegCloseKey( tmp );
309 return ret;
314 /******************************************************************************
315 * RegSetValueExA [ADVAPI32.@]
317 DWORD WINAPI RegSetValueExA( HKEY hkey, LPCSTR name, DWORD reserved, DWORD type,
318 CONST BYTE *data, DWORD count )
320 UNICODE_STRING nameW;
321 ANSI_STRING nameA;
322 WCHAR *dataW = NULL;
323 NTSTATUS status;
325 if (count && is_string(type))
327 /* if user forgot to count terminating null, add it (yes NT does this) */
328 if (data[count-1] && !data[count]) count++;
331 if (is_string( type )) /* need to convert to Unicode */
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;
341 RtlInitAnsiString( &nameA, name );
342 /* FIXME: should use Unicode buffer in TEB */
343 if (!(status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE )))
345 status = NtSetValueKey( hkey, &nameW, 0, type, data, count );
346 RtlFreeUnicodeString( &nameW );
348 if (dataW) HeapFree( GetProcessHeap(), 0, dataW );
349 return RtlNtStatusToDosError( status );
353 /******************************************************************************
354 * RegSetValueA [ADVAPI32.@]
356 DWORD WINAPI RegSetValueA( HKEY hkey, LPCSTR name, DWORD type, LPCSTR data, DWORD count )
358 HKEY subkey = hkey;
359 DWORD ret;
361 TRACE("(0x%x,%s,%ld,%s,%ld)\n", hkey, debugstr_a(name), type, debugstr_a(data), count );
363 if (type != REG_SZ) return ERROR_INVALID_PARAMETER;
365 if (name && name[0]) /* need to create the subkey */
367 if ((ret = RegCreateKeyA( hkey, name, &subkey )) != ERROR_SUCCESS) return ret;
369 ret = RegSetValueExA( subkey, NULL, 0, REG_SZ, (LPBYTE)data, strlen(data)+1 );
370 if (subkey != hkey) RegCloseKey( subkey );
371 return ret;
376 /******************************************************************************
377 * RegQueryValueExA [ADVAPI32.@]
379 * NOTES:
380 * the documentation is wrong: if the buffer is too small it remains untouched
382 DWORD WINAPI RegQueryValueExA( HKEY hkey, LPCSTR name, LPDWORD reserved, LPDWORD type,
383 LPBYTE data, LPDWORD count )
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 = info->Data - (UCHAR *)info;
393 TRACE("(0x%x,%s,%p,%p,%p,%p=%ld)\n",
394 hkey, debugstr_a(name), reserved, type, data, count, count ? *count : 0 );
396 if ((data && !count) || reserved) return ERROR_INVALID_PARAMETER;
398 RtlInitAnsiString( &nameA, name );
399 /* FIXME: should use Unicode buffer in TEB */
400 if ((status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE )))
401 return RtlNtStatusToDosError(status);
403 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation,
404 buffer, sizeof(buffer), &total_size );
405 if (status && status != STATUS_BUFFER_OVERFLOW) goto done;
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))
411 /* retry with a dynamically allocated buffer */
412 while (status == STATUS_BUFFER_OVERFLOW)
414 if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
415 if (!(buf_ptr = HeapAlloc( GetProcessHeap(), 0, total_size )))
417 status = STATUS_NO_MEMORY;
418 goto done;
420 info = (KEY_VALUE_PARTIAL_INFORMATION *)buf_ptr;
421 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation,
422 buf_ptr, total_size, &total_size );
425 if (!status)
427 if (is_string(info->Type))
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)
434 if (len > *count) status = STATUS_BUFFER_OVERFLOW;
435 else
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;
445 total_size = len + info_size;
447 else if (data)
449 if (total_size - info_size > *count) status = STATUS_BUFFER_OVERFLOW;
450 else memcpy( data, buf_ptr + info_size, total_size - info_size );
453 else if (status != STATUS_BUFFER_OVERFLOW) goto done;
456 if (type) *type = info->Type;
457 if (count) *count = total_size - info_size;
459 done:
460 if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
461 RtlFreeUnicodeString( &nameW );
462 return RtlNtStatusToDosError(status);
466 /******************************************************************************
467 * RegQueryValueA [ADVAPI32.@]
469 DWORD WINAPI RegQueryValueA( HKEY hkey, LPCSTR name, LPSTR data, LPLONG count )
471 DWORD ret;
472 HKEY subkey = hkey;
474 TRACE("(%x,%s,%p,%ld)\n", hkey, debugstr_a(name), data, count ? *count : 0 );
476 if (name && name[0])
478 if ((ret = RegOpenKeyA( hkey, name, &subkey )) != ERROR_SUCCESS) return ret;
480 ret = RegQueryValueExA( subkey, NULL, NULL, NULL, (LPBYTE)data, count );
481 if (subkey != hkey) RegCloseKey( subkey );
482 if (ret == ERROR_FILE_NOT_FOUND)
484 /* return empty string if default value not found */
485 if (data) *data = 0;
486 if (count) *count = 1;
487 ret = ERROR_SUCCESS;
489 return ret;
493 /******************************************************************************
494 * RegEnumValueA [ADVAPI32.@]
496 DWORD WINAPI RegEnumValueA( HKEY hkey, DWORD index, LPSTR value, LPDWORD val_count,
497 LPDWORD reserved, LPDWORD type, LPBYTE data, LPDWORD count )
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 = (char *)info->Name - (char *)info;
505 TRACE("(%x,%ld,%p,%p,%p,%p,%p,%p)\n",
506 hkey, index, value, val_count, reserved, type, data, count );
508 /* NT only checks count, not val_count */
509 if ((data && !count) || reserved) return ERROR_INVALID_PARAMETER;
511 total_size = info_size + (MAX_PATH + 1) * sizeof(WCHAR);
512 if (data) total_size += *count;
513 total_size = min( sizeof(buffer), total_size );
515 status = NtEnumerateValueKey( hkey, index, KeyValueFullInformation,
516 buffer, total_size, &total_size );
517 if (status && status != STATUS_BUFFER_OVERFLOW) goto done;
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))
523 /* retry with a dynamically allocated buffer */
524 while (status == STATUS_BUFFER_OVERFLOW)
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 );
534 if (status) goto done;
536 if (value)
538 DWORD len;
540 RtlUnicodeToMultiByteSize( &len, info->Name, info->NameLength );
541 if (len >= *val_count)
543 status = STATUS_BUFFER_OVERFLOW;
544 goto done;
546 RtlUnicodeToMultiByteN( value, len, NULL, info->Name, info->NameLength );
547 value[len] = 0;
548 *val_count = len;
551 if (is_string(info->Type))
553 DWORD len;
554 RtlUnicodeToMultiByteSize( &len, (WCHAR *)(buf_ptr + info->DataOffset),
555 total_size - info->DataOffset );
556 if (data && len)
558 if (len > *count)
560 status = STATUS_BUFFER_OVERFLOW;
561 goto done;
563 RtlUnicodeToMultiByteN( data, len, NULL, (WCHAR *)(buf_ptr + info->DataOffset),
564 total_size - info->DataOffset );
565 /* if the type is REG_SZ and data is not 0-terminated
566 * and there is enough space in the buffer NT appends a \0 */
567 if (len < *count && data[len-1]) data[len] = 0;
569 info->DataLength = len;
571 else if (data)
573 if (total_size - info->DataOffset > *count) status = STATUS_BUFFER_OVERFLOW;
574 else memcpy( data, buf_ptr + info->DataOffset, total_size - info->DataOffset );
577 else status = STATUS_SUCCESS;
579 if (type) *type = info->Type;
580 if (count) *count = info->DataLength;
582 done:
583 if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
584 return RtlNtStatusToDosError(status);
589 /******************************************************************************
590 * RegDeleteValueA [ADVAPI32.@]
592 DWORD WINAPI RegDeleteValueA( HKEY hkey, LPCSTR name )
594 UNICODE_STRING nameW;
595 STRING nameA;
596 NTSTATUS status;
598 RtlInitAnsiString( &nameA, name );
599 /* FIXME: should use Unicode buffer in TEB */
600 if (!(status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE )))
602 status = NtDeleteValueKey( hkey, &nameW );
603 RtlFreeUnicodeString( &nameW );
605 return RtlNtStatusToDosError( status );
609 /******************************************************************************
610 * RegLoadKeyA [ADVAPI32.@]
612 LONG WINAPI RegLoadKeyA( HKEY hkey, LPCSTR subkey, LPCSTR filename )
614 HANDLE file;
615 WCHAR buffer[MAX_PATH];
616 DWORD ret, len, err = GetLastError();
618 TRACE( "(%x,%s,%s)\n", hkey, debugstr_a(subkey), debugstr_a(filename) );
620 if (!filename || !*filename) return ERROR_INVALID_PARAMETER;
621 if (!subkey || !*subkey) return ERROR_INVALID_PARAMETER;
623 if (!(len = MultiByteToWideChar( CP_ACP, 0, subkey, strlen(subkey), buffer, MAX_PATH )))
624 return ERROR_INVALID_PARAMETER;
626 if ((file = CreateFileA( filename, GENERIC_READ, 0, NULL, OPEN_EXISTING,
627 FILE_ATTRIBUTE_NORMAL, 0 )) == INVALID_HANDLE_VALUE)
629 ret = GetLastError();
630 goto done;
633 SERVER_START_REQ( load_registry )
635 req->hkey = hkey;
636 req->file = file;
637 wine_server_add_data( req, buffer, len * sizeof(WCHAR) );
638 ret = RtlNtStatusToDosError( wine_server_call(req) );
640 SERVER_END_REQ;
641 CloseHandle( file );
643 done:
644 SetLastError( err ); /* restore the last error code */
645 return ret;
649 /******************************************************************************
650 * RegSaveKeyA [ADVAPI32.@]
652 * PARAMS
653 * hkey [I] Handle of key where save begins
654 * lpFile [I] Address of filename to save to
655 * sa [I] Address of security structure
657 LONG WINAPI RegSaveKeyA( HKEY hkey, LPCSTR file, LPSECURITY_ATTRIBUTES sa )
659 char buffer[1024];
660 int count = 0;
661 LPSTR name;
662 DWORD ret, err;
663 HANDLE handle;
665 TRACE( "(%x,%s,%p)\n", hkey, debugstr_a(file), sa );
667 if (!file || !*file) return ERROR_INVALID_PARAMETER;
669 err = GetLastError();
670 GetFullPathNameA( file, sizeof(buffer), buffer, &name );
671 for (;;)
673 sprintf( name, "reg%04x.tmp", count++ );
674 handle = CreateFileA( buffer, GENERIC_WRITE, 0, NULL,
675 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
676 if (handle != INVALID_HANDLE_VALUE) break;
677 if ((ret = GetLastError()) != ERROR_ALREADY_EXISTS) goto done;
679 /* Something gone haywire ? Please report if this happens abnormally */
680 if (count >= 100)
681 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);
684 SERVER_START_REQ( save_registry )
686 req->hkey = hkey;
687 req->file = handle;
688 ret = RtlNtStatusToDosError( wine_server_call( req ) );
690 SERVER_END_REQ;
692 CloseHandle( handle );
693 if (!ret)
695 if (!MoveFileExA( buffer, file, MOVEFILE_REPLACE_EXISTING ))
697 ERR( "Failed to move %s to %s\n", buffer, file );
698 ret = GetLastError();
701 if (ret) DeleteFileA( buffer );
703 done:
704 SetLastError( err ); /* restore last error code */
705 return ret;