Thawed some freezes.
[wine/wine-kai.git] / memory / registry.c
blob2d1be8ed38e23377356c2add4d3afd663a2628b8
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.
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <unistd.h>
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"
27 DEFAULT_DEBUG_CHANNEL(reg);
30 /* check if value type needs string conversion (Ansi<->Unicode) */
31 static inline int is_string( DWORD type )
33 return (type == REG_SZ) || (type == REG_EXPAND_SZ) || (type == REG_MULTI_SZ);
37 /******************************************************************************
38 * RegCreateKeyExA [ADVAPI32.@]
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 )
44 OBJECT_ATTRIBUTES attr;
45 UNICODE_STRING nameW, classW;
46 ANSI_STRING nameA, classA;
47 NTSTATUS status;
49 if (reserved) return ERROR_INVALID_PARAMETER;
50 if (!(access & KEY_ALL_ACCESS) || (access & ~KEY_ALL_ACCESS)) return ERROR_ACCESS_DENIED;
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 );
61 /* FIXME: should use Unicode buffer in TEB */
62 if (!(status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE )))
64 if (!(status = RtlAnsiStringToUnicodeString( &classW, &classA, TRUE )))
66 status = NtCreateKey( retkey, access, &attr, 0, &classW, options, dispos );
67 RtlFreeUnicodeString( &classW );
69 RtlFreeUnicodeString( &nameW );
71 return RtlNtStatusToDosError( status );
75 /******************************************************************************
76 * RegCreateKeyA [ADVAPI32.@]
78 DWORD WINAPI RegCreateKeyA( HKEY hkey, LPCSTR name, LPHKEY retkey )
80 return RegCreateKeyExA( hkey, name, 0, NULL, REG_OPTION_NON_VOLATILE,
81 KEY_ALL_ACCESS, NULL, retkey, NULL );
86 /******************************************************************************
87 * RegOpenKeyExA [ADVAPI32.@]
89 DWORD WINAPI RegOpenKeyExA( HKEY hkey, LPCSTR name, DWORD reserved, REGSAM access, LPHKEY retkey )
91 OBJECT_ATTRIBUTES attr;
92 UNICODE_STRING nameW;
93 STRING nameA;
94 NTSTATUS status;
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;
103 RtlInitAnsiString( &nameA, name );
104 /* FIXME: should use Unicode buffer in TEB */
105 if (!(status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE )))
107 status = NtOpenKey( retkey, access, &attr );
108 RtlFreeUnicodeString( &nameW );
110 return RtlNtStatusToDosError( status );
114 /******************************************************************************
115 * RegOpenKeyA [ADVAPI32.@]
117 DWORD WINAPI RegOpenKeyA( HKEY hkey, LPCSTR name, LPHKEY retkey )
119 return RegOpenKeyExA( hkey, name, 0, KEY_ALL_ACCESS, retkey );
123 /******************************************************************************
124 * RegEnumKeyExA [ADVAPI32.@]
126 DWORD WINAPI RegEnumKeyExA( HKEY hkey, DWORD index, LPSTR name, LPDWORD name_len,
127 LPDWORD reserved, LPSTR class, LPDWORD class_len, FILETIME *ft )
129 NTSTATUS status;
130 char buffer[256], *buf_ptr = buffer;
131 KEY_NODE_INFORMATION *info = (KEY_NODE_INFORMATION *)buffer;
132 DWORD total_size;
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 );
137 if (reserved) return ERROR_INVALID_PARAMETER;
139 status = NtEnumerateKey( hkey, index, KeyNodeInformation,
140 buffer, sizeof(buffer), &total_size );
142 while (status == STATUS_BUFFER_OVERFLOW)
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 );
153 if (!status)
155 DWORD len, cls_len;
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;
162 if (len >= *name_len || (class_len && (cls_len >= *class_len)))
163 status = STATUS_BUFFER_OVERFLOW;
164 else
166 *name_len = len;
167 RtlUnicodeToMultiByteN( name, len, NULL, info->Name, info->NameLength );
168 name[len] = 0;
169 if (class_len)
171 *class_len = cls_len;
172 if (class)
174 RtlUnicodeToMultiByteN( class, cls_len, NULL,
175 (WCHAR *)(buf_ptr + info->ClassOffset),
176 info->ClassLength );
177 class[cls_len] = 0;
183 if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
184 return RtlNtStatusToDosError( status );
188 /******************************************************************************
189 * RegEnumKeyA [ADVAPI32.@]
191 DWORD WINAPI RegEnumKeyA( HKEY hkey, DWORD index, LPSTR name, DWORD name_len )
193 return RegEnumKeyExA( hkey, index, name, &name_len, NULL, NULL, NULL, NULL );
197 /******************************************************************************
198 * RegQueryInfoKeyA [ADVAPI32.@]
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 )
205 NTSTATUS status;
206 char buffer[256], *buf_ptr = buffer;
207 KEY_FULL_INFORMATION *info = (KEY_FULL_INFORMATION *)buffer;
208 DWORD total_size, len;
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 );
213 if (class && !class_len && !(GetVersion() & 0x80000000 /*NT*/))
214 return ERROR_INVALID_PARAMETER;
216 status = NtQueryKey( hkey, KeyFullInformation, buffer, sizeof(buffer), &total_size );
217 if (status && status != STATUS_BUFFER_OVERFLOW) goto done;
219 if (class || class_len)
221 /* retry with a dynamically allocated buffer */
222 while (status == STATUS_BUFFER_OVERFLOW)
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 );
231 if (status) goto done;
233 RtlUnicodeToMultiByteSize( &len, (WCHAR *)(buf_ptr + info->ClassOffset), info->ClassLength);
234 if (class_len)
236 if (len + 1 > *class_len) status = STATUS_BUFFER_OVERFLOW;
237 *class_len = len;
239 if (class && !status)
241 RtlUnicodeToMultiByteN( class, len, NULL, (WCHAR *)(buf_ptr + info->ClassOffset),
242 info->ClassLength );
243 class[len] = 0;
246 else status = STATUS_SUCCESS;
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;
256 done:
257 if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
258 return RtlNtStatusToDosError( status );
262 /******************************************************************************
263 * RegCloseKey [ADVAPI32.@]
265 * Releases the handle of the specified key
267 * PARAMS
268 * hkey [I] Handle of key to close
270 * RETURNS
271 * Success: ERROR_SUCCESS
272 * Failure: Error code
274 DWORD WINAPI RegCloseKey( HKEY hkey )
276 if (!hkey || hkey >= 0x80000000) return ERROR_SUCCESS;
277 return RtlNtStatusToDosError( NtClose( hkey ) );
281 /******************************************************************************
282 * RegDeleteKeyA [ADVAPI32.@]
284 DWORD WINAPI RegDeleteKeyA( HKEY hkey, LPCSTR name )
286 DWORD ret;
287 HKEY tmp;
289 if (!name || !*name) return NtDeleteKey( hkey );
290 if (!(ret = RegOpenKeyExA( hkey, name, 0, 0, &tmp )))
292 ret = RtlNtStatusToDosError( NtDeleteKey( tmp ) );
293 RegCloseKey( tmp );
295 return ret;
300 /******************************************************************************
301 * RegSetValueExA [ADVAPI32.@]
303 DWORD WINAPI RegSetValueExA( HKEY hkey, LPCSTR name, DWORD reserved, DWORD type,
304 CONST BYTE *data, DWORD count )
306 UNICODE_STRING nameW;
307 ANSI_STRING nameA;
308 WCHAR *dataW = NULL;
309 NTSTATUS status;
311 if (count && is_string(type))
313 /* if user forgot to count terminating null, add it (yes NT does this) */
314 if (data[count-1] && !data[count]) count++;
317 if (is_string( type )) /* need to convert to Unicode */
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;
327 RtlInitAnsiString( &nameA, name );
328 /* FIXME: should use Unicode buffer in TEB */
329 if (!(status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE )))
331 status = NtSetValueKey( hkey, &nameW, 0, type, data, count );
332 RtlFreeUnicodeString( &nameW );
334 if (dataW) HeapFree( GetProcessHeap(), 0, dataW );
335 return RtlNtStatusToDosError( status );
339 /******************************************************************************
340 * RegSetValueA [ADVAPI32.@]
342 DWORD WINAPI RegSetValueA( HKEY hkey, LPCSTR name, DWORD type, LPCSTR data, DWORD count )
344 HKEY subkey = hkey;
345 DWORD ret;
347 TRACE("(0x%x,%s,%ld,%s,%ld)\n", hkey, debugstr_a(name), type, debugstr_a(data), count );
349 if (type != REG_SZ) return ERROR_INVALID_PARAMETER;
351 if (name && name[0]) /* need to create the subkey */
353 if ((ret = RegCreateKeyA( hkey, name, &subkey )) != ERROR_SUCCESS) return ret;
355 ret = RegSetValueExA( subkey, NULL, 0, REG_SZ, (LPBYTE)data, strlen(data)+1 );
356 if (subkey != hkey) RegCloseKey( subkey );
357 return ret;
362 /******************************************************************************
363 * RegQueryValueExA [ADVAPI32.@]
365 * NOTES:
366 * the documentation is wrong: if the buffer is too small it remains untouched
368 DWORD WINAPI RegQueryValueExA( HKEY hkey, LPCSTR name, LPDWORD reserved, LPDWORD type,
369 LPBYTE data, LPDWORD count )
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;
379 TRACE("(0x%x,%s,%p,%p,%p,%p=%ld)\n",
380 hkey, debugstr_a(name), reserved, type, data, count, count ? *count : 0 );
382 if ((data && !count) || reserved) return ERROR_INVALID_PARAMETER;
384 RtlInitAnsiString( &nameA, name );
385 /* FIXME: should use Unicode buffer in TEB */
386 if ((status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE )))
387 return RtlNtStatusToDosError(status);
389 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation,
390 buffer, sizeof(buffer), &total_size );
391 if (status && status != STATUS_BUFFER_OVERFLOW) goto done;
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))
397 /* retry with a dynamically allocated buffer */
398 while (status == STATUS_BUFFER_OVERFLOW)
400 if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
401 if (!(buf_ptr = HeapAlloc( GetProcessHeap(), 0, total_size )))
403 status = STATUS_NO_MEMORY;
404 goto done;
406 info = (KEY_VALUE_PARTIAL_INFORMATION *)buf_ptr;
407 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation,
408 buf_ptr, total_size, &total_size );
411 if (!status)
413 if (is_string(info->Type))
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)
420 if (len > *count) status = STATUS_BUFFER_OVERFLOW;
421 else
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;
431 total_size = len + info_size;
433 else if (data)
435 if (total_size - info_size > *count) status = STATUS_BUFFER_OVERFLOW;
436 else memcpy( data, buf_ptr + info_size, total_size - info_size );
439 else if (status != STATUS_BUFFER_OVERFLOW) goto done;
442 if (type) *type = info->Type;
443 if (count) *count = total_size - info_size;
445 done:
446 if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
447 RtlFreeUnicodeString( &nameW );
448 return RtlNtStatusToDosError(status);
452 /******************************************************************************
453 * RegQueryValueA [ADVAPI32.@]
455 DWORD WINAPI RegQueryValueA( HKEY hkey, LPCSTR name, LPSTR data, LPLONG count )
457 DWORD ret;
458 HKEY subkey = hkey;
460 TRACE("(%x,%s,%p,%ld)\n", hkey, debugstr_a(name), data, count ? *count : 0 );
462 if (name && name[0])
464 if ((ret = RegOpenKeyA( hkey, name, &subkey )) != ERROR_SUCCESS) return ret;
466 ret = RegQueryValueExA( subkey, NULL, NULL, NULL, (LPBYTE)data, count );
467 if (subkey != hkey) RegCloseKey( subkey );
468 if (ret == ERROR_FILE_NOT_FOUND)
470 /* return empty string if default value not found */
471 if (data) *data = 0;
472 if (count) *count = 1;
473 ret = ERROR_SUCCESS;
475 return ret;
479 /******************************************************************************
480 * RegEnumValueA [ADVAPI32.@]
482 DWORD WINAPI RegEnumValueA( HKEY hkey, DWORD index, LPSTR value, LPDWORD val_count,
483 LPDWORD reserved, LPDWORD type, LPBYTE data, LPDWORD count )
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;
491 TRACE("(%x,%ld,%p,%p,%p,%p,%p,%p)\n",
492 hkey, index, value, val_count, reserved, type, data, count );
494 /* NT only checks count, not val_count */
495 if ((data && !count) || reserved) return ERROR_INVALID_PARAMETER;
497 total_size = info_size + (MAX_PATH + 1) * sizeof(WCHAR);
498 if (data) total_size += *count;
499 total_size = min( sizeof(buffer), total_size );
501 status = NtEnumerateValueKey( hkey, index, KeyValueFullInformation,
502 buffer, total_size, &total_size );
503 if (status && status != STATUS_BUFFER_OVERFLOW) goto done;
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))
509 /* retry with a dynamically allocated buffer */
510 while (status == STATUS_BUFFER_OVERFLOW)
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 );
520 if (status) goto done;
522 if (value)
524 DWORD len;
526 RtlUnicodeToMultiByteSize( &len, info->Name, info->NameLength );
527 if (len >= *val_count)
529 status = STATUS_BUFFER_OVERFLOW;
530 goto done;
532 RtlUnicodeToMultiByteN( value, len, NULL, info->Name, info->NameLength );
533 value[len] = 0;
534 *val_count = len;
537 if (is_string(info->Type))
539 DWORD len;
540 RtlUnicodeToMultiByteSize( &len, (WCHAR *)(buf_ptr + info->DataOffset),
541 total_size - info->DataOffset );
542 if (data && len)
544 if (len > *count)
546 status = STATUS_BUFFER_OVERFLOW;
547 goto done;
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;
555 info->DataLength = len;
557 else if (data)
559 if (total_size - info->DataOffset > *count) status = STATUS_BUFFER_OVERFLOW;
560 else memcpy( data, buf_ptr + info->DataOffset, total_size - info->DataOffset );
563 else status = STATUS_SUCCESS;
565 if (type) *type = info->Type;
566 if (count) *count = info->DataLength;
568 done:
569 if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
570 return RtlNtStatusToDosError(status);
575 /******************************************************************************
576 * RegDeleteValueA [ADVAPI32.@]
578 DWORD WINAPI RegDeleteValueA( HKEY hkey, LPCSTR name )
580 UNICODE_STRING nameW;
581 STRING nameA;
582 NTSTATUS status;
584 RtlInitAnsiString( &nameA, name );
585 /* FIXME: should use Unicode buffer in TEB */
586 if (!(status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE )))
588 status = NtDeleteValueKey( hkey, &nameW );
589 RtlFreeUnicodeString( &nameW );
591 return RtlNtStatusToDosError( status );
595 /******************************************************************************
596 * RegLoadKeyA [ADVAPI32.@]
598 LONG WINAPI RegLoadKeyA( HKEY hkey, LPCSTR subkey, LPCSTR filename )
600 HANDLE file;
601 WCHAR buffer[MAX_PATH];
602 DWORD ret, len, err = GetLastError();
604 TRACE( "(%x,%s,%s)\n", hkey, debugstr_a(subkey), debugstr_a(filename) );
606 if (!filename || !*filename) return ERROR_INVALID_PARAMETER;
607 if (!subkey || !*subkey) return ERROR_INVALID_PARAMETER;
609 if (!(len = MultiByteToWideChar( CP_ACP, 0, subkey, strlen(subkey), buffer, MAX_PATH )))
610 return ERROR_INVALID_PARAMETER;
612 if ((file = CreateFileA( filename, GENERIC_READ, 0, NULL, OPEN_EXISTING,
613 FILE_ATTRIBUTE_NORMAL, 0 )) == INVALID_HANDLE_VALUE)
615 ret = GetLastError();
616 goto done;
619 SERVER_START_REQ( load_registry )
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) );
626 SERVER_END_REQ;
627 CloseHandle( file );
629 done:
630 SetLastError( err ); /* restore the last error code */
631 return ret;
635 /******************************************************************************
636 * RegSaveKeyA [ADVAPI32.@]
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
643 LONG WINAPI RegSaveKeyA( HKEY hkey, LPCSTR file, LPSECURITY_ATTRIBUTES sa )
645 char buffer[1024];
646 int count = 0;
647 LPSTR name;
648 DWORD ret, err;
649 HANDLE handle;
651 TRACE( "(%x,%s,%p)\n", hkey, debugstr_a(file), sa );
653 if (!file || !*file) return ERROR_INVALID_PARAMETER;
655 err = GetLastError();
656 GetFullPathNameA( file, sizeof(buffer), buffer, &name );
657 for (;;)
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;
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);
670 SERVER_START_REQ( save_registry )
672 req->hkey = hkey;
673 req->file = handle;
674 ret = RtlNtStatusToDosError( wine_server_call( req ) );
676 SERVER_END_REQ;
678 CloseHandle( handle );
679 if (!ret)
681 if (!MoveFileExA( buffer, file, MOVEFILE_REPLACE_EXISTING ))
683 ERR( "Failed to move %s to %s\n", buffer, file );
684 ret = GetLastError();
687 if (ret) DeleteFileA( buffer );
689 done:
690 SetLastError( err ); /* restore last error code */
691 return ret;