Several bug fixes and additions.
[wine.git] / memory / registry.c
blob0c34811b3f3e75cc9ed189df2057da70f6b7df37
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 "heap.h"
25 #include "server.h"
26 #include "debugtools.h"
28 DEFAULT_DEBUG_CHANNEL(reg);
31 /* check if value type needs string conversion (Ansi<->Unicode) */
32 static inline int is_string( DWORD type )
34 return (type == REG_SZ) || (type == REG_EXPAND_SZ) || (type == REG_MULTI_SZ);
38 /******************************************************************************
39 * RegCreateKeyExA [ADVAPI32.@]
41 DWORD WINAPI RegCreateKeyExA( HKEY hkey, LPCSTR name, DWORD reserved, LPSTR class,
42 DWORD options, REGSAM access, SECURITY_ATTRIBUTES *sa,
43 LPHKEY retkey, LPDWORD dispos )
45 OBJECT_ATTRIBUTES attr;
46 UNICODE_STRING nameW, classW;
47 ANSI_STRING nameA, classA;
48 NTSTATUS status;
50 if (reserved) return ERROR_INVALID_PARAMETER;
51 if (!(access & KEY_ALL_ACCESS) || (access & ~KEY_ALL_ACCESS)) return ERROR_ACCESS_DENIED;
53 attr.Length = sizeof(attr);
54 attr.RootDirectory = hkey;
55 attr.ObjectName = &nameW;
56 attr.Attributes = 0;
57 attr.SecurityDescriptor = NULL;
58 attr.SecurityQualityOfService = NULL;
59 RtlInitAnsiString( &nameA, name );
60 RtlInitAnsiString( &classA, class );
62 /* FIXME: should use Unicode buffer in TEB */
63 if (!(status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE )))
65 if (!(status = RtlAnsiStringToUnicodeString( &classW, &classA, TRUE )))
67 status = NtCreateKey( retkey, access, &attr, 0, &classW, options, dispos );
68 RtlFreeUnicodeString( &classW );
70 RtlFreeUnicodeString( &nameW );
72 return RtlNtStatusToDosError( status );
76 /******************************************************************************
77 * RegCreateKeyA [ADVAPI32.@]
79 DWORD WINAPI RegCreateKeyA( HKEY hkey, LPCSTR name, LPHKEY retkey )
81 return RegCreateKeyExA( hkey, name, 0, NULL, REG_OPTION_NON_VOLATILE,
82 KEY_ALL_ACCESS, NULL, retkey, NULL );
87 /******************************************************************************
88 * RegOpenKeyExA [ADVAPI32.@]
90 DWORD WINAPI RegOpenKeyExA( HKEY hkey, LPCSTR name, DWORD reserved, REGSAM access, LPHKEY retkey )
92 OBJECT_ATTRIBUTES attr;
93 UNICODE_STRING nameW;
94 STRING nameA;
95 NTSTATUS status;
97 attr.Length = sizeof(attr);
98 attr.RootDirectory = hkey;
99 attr.ObjectName = &nameW;
100 attr.Attributes = 0;
101 attr.SecurityDescriptor = NULL;
102 attr.SecurityQualityOfService = NULL;
104 RtlInitAnsiString( &nameA, name );
105 /* FIXME: should use Unicode buffer in TEB */
106 if (!(status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE )))
108 status = NtOpenKey( retkey, access, &attr );
109 RtlFreeUnicodeString( &nameW );
111 return RtlNtStatusToDosError( status );
115 /******************************************************************************
116 * RegOpenKeyA [ADVAPI32.@]
118 DWORD WINAPI RegOpenKeyA( HKEY hkey, LPCSTR name, LPHKEY retkey )
120 return RegOpenKeyExA( hkey, name, 0, KEY_ALL_ACCESS, retkey );
124 /******************************************************************************
125 * RegEnumKeyExA [ADVAPI32.@]
127 DWORD WINAPI RegEnumKeyExA( HKEY hkey, DWORD index, LPSTR name, LPDWORD name_len,
128 LPDWORD reserved, LPSTR class, LPDWORD class_len, FILETIME *ft )
130 NTSTATUS status;
131 char buffer[256], *buf_ptr = buffer;
132 KEY_NODE_INFORMATION *info = (KEY_NODE_INFORMATION *)buffer;
133 DWORD total_size;
135 TRACE( "(0x%x,%ld,%p,%p(%ld),%p,%p,%p,%p)\n", hkey, index, name, name_len,
136 name_len ? *name_len : -1, reserved, class, class_len, ft );
138 if (reserved) return ERROR_INVALID_PARAMETER;
140 status = NtEnumerateKey( hkey, index, KeyNodeInformation,
141 buffer, sizeof(buffer), &total_size );
143 while (status == STATUS_BUFFER_OVERFLOW)
145 /* retry with a dynamically allocated buffer */
146 if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
147 if (!(buf_ptr = HeapAlloc( GetProcessHeap(), 0, total_size )))
148 return ERROR_NOT_ENOUGH_MEMORY;
149 info = (KEY_NODE_INFORMATION *)buf_ptr;
150 status = NtEnumerateKey( hkey, index, KeyNodeInformation,
151 buf_ptr, total_size, &total_size );
154 if (!status)
156 DWORD len = WideCharToMultiByte( CP_ACP, 0, info->Name, info->NameLength/sizeof(WCHAR),
157 NULL, 0, NULL, NULL );
158 DWORD cls_len = WideCharToMultiByte( CP_ACP, 0, (WCHAR *)(buf_ptr + info->ClassOffset),
159 info->ClassLength / sizeof(WCHAR),
160 NULL, 0, NULL, NULL );
162 if (ft) *ft = info->LastWriteTime;
164 if (len >= *name_len || (class_len && (cls_len >= *class_len)))
165 status = STATUS_BUFFER_OVERFLOW;
166 else
168 *name_len = len;
169 WideCharToMultiByte( CP_ACP, 0, info->Name, info->NameLength/sizeof(WCHAR),
170 name, len, NULL, NULL );
171 name[len] = 0;
172 if (class_len)
174 *class_len = cls_len;
175 if (class)
177 WideCharToMultiByte( CP_ACP, 0, (WCHAR *)(buf_ptr + info->ClassOffset),
178 info->ClassLength / sizeof(WCHAR),
179 class, cls_len, NULL, NULL );
180 class[cls_len] = 0;
186 if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
187 return RtlNtStatusToDosError( status );
191 /******************************************************************************
192 * RegEnumKeyA [ADVAPI32.@]
194 DWORD WINAPI RegEnumKeyA( HKEY hkey, DWORD index, LPSTR name, DWORD name_len )
196 return RegEnumKeyExA( hkey, index, name, &name_len, NULL, NULL, NULL, NULL );
200 /******************************************************************************
201 * RegQueryInfoKeyA [ADVAPI32.@]
203 DWORD WINAPI RegQueryInfoKeyA( HKEY hkey, LPSTR class, LPDWORD class_len, LPDWORD reserved,
204 LPDWORD subkeys, LPDWORD max_subkey, LPDWORD max_class,
205 LPDWORD values, LPDWORD max_value, LPDWORD max_data,
206 LPDWORD security, FILETIME *modif )
208 NTSTATUS status;
209 char buffer[256], *buf_ptr = buffer;
210 KEY_FULL_INFORMATION *info = (KEY_FULL_INFORMATION *)buffer;
211 DWORD total_size;
213 TRACE( "(0x%x,%p,%ld,%p,%p,%p,%p,%p,%p,%p,%p)\n", hkey, class, class_len ? *class_len : 0,
214 reserved, subkeys, max_subkey, values, max_value, max_data, security, modif );
216 if (class && !class_len && !(GetVersion() & 0x80000000 /*NT*/))
217 return ERROR_INVALID_PARAMETER;
219 status = NtQueryKey( hkey, KeyFullInformation, buffer, sizeof(buffer), &total_size );
221 if (class || class_len)
223 /* retry with a dynamically allocated buffer */
224 while (status == STATUS_BUFFER_OVERFLOW)
226 if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
227 if (!(buf_ptr = HeapAlloc( GetProcessHeap(), 0, total_size )))
228 return ERROR_NOT_ENOUGH_MEMORY;
229 info = (KEY_FULL_INFORMATION *)buf_ptr;
230 status = NtQueryKey( hkey, KeyFullInformation, buf_ptr, total_size, &total_size );
233 if (!status)
235 DWORD len = WideCharToMultiByte( CP_ACP, 0,
236 (WCHAR *)(buf_ptr + info->ClassOffset),
237 info->ClassLength/sizeof(WCHAR),
238 NULL, 0, NULL, NULL );
239 if (class_len)
241 if (len + 1 > *class_len) status = STATUS_BUFFER_OVERFLOW;
242 *class_len = len;
244 if (class && !status)
246 WideCharToMultiByte( CP_ACP, 0,
247 (WCHAR *)(buf_ptr + info->ClassOffset),
248 info->ClassLength/sizeof(WCHAR),
249 class, len, NULL, NULL );
250 class[len] = 0;
255 if (!status || status == STATUS_BUFFER_OVERFLOW)
257 if (subkeys) *subkeys = info->SubKeys;
258 if (max_subkey) *max_subkey = info->MaxNameLen;
259 if (max_class) *max_class = info->MaxClassLen;
260 if (values) *values = info->Values;
261 if (max_value) *max_value = info->MaxValueNameLen;
262 if (max_data) *max_data = info->MaxValueDataLen;
263 if (modif) *modif = info->LastWriteTime;
266 if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
267 return RtlNtStatusToDosError( status );
271 /******************************************************************************
272 * RegCloseKey [ADVAPI32.@]
274 * Releases the handle of the specified key
276 * PARAMS
277 * hkey [I] Handle of key to close
279 * RETURNS
280 * Success: ERROR_SUCCESS
281 * Failure: Error code
283 DWORD WINAPI RegCloseKey( HKEY hkey )
285 if (!hkey || hkey >= 0x80000000) return ERROR_SUCCESS;
286 return RtlNtStatusToDosError( NtClose( hkey ) );
290 /******************************************************************************
291 * RegDeleteKeyA [ADVAPI32.@]
293 DWORD WINAPI RegDeleteKeyA( HKEY hkey, LPCSTR name )
295 DWORD ret;
296 HKEY tmp;
298 if (!name || !*name) return NtDeleteKey( hkey );
299 if (!(ret = RegOpenKeyExA( hkey, name, 0, 0, &tmp )))
301 ret = RtlNtStatusToDosError( NtDeleteKey( tmp ) );
302 RegCloseKey( tmp );
304 return ret;
309 /******************************************************************************
310 * RegSetValueExA [ADVAPI32.@]
312 DWORD WINAPI RegSetValueExA( HKEY hkey, LPCSTR name, DWORD reserved, DWORD type,
313 CONST BYTE *data, DWORD count )
315 UNICODE_STRING nameW;
316 ANSI_STRING nameA;
317 WCHAR *dataW = NULL;
318 NTSTATUS status;
320 if (count && is_string(type))
322 /* if user forgot to count terminating null, add it (yes NT does this) */
323 if (data[count-1] && !data[count]) count++;
326 if (is_string( type )) /* need to convert to Unicode */
328 DWORD lenW = MultiByteToWideChar( CP_ACP, 0, data, count, NULL, 0 );
329 if (!(dataW = HeapAlloc( GetProcessHeap(), 0, lenW*sizeof(WCHAR) )))
330 return ERROR_OUTOFMEMORY;
331 MultiByteToWideChar( CP_ACP, 0, data, count, dataW, lenW );
332 count = lenW * sizeof(WCHAR);
333 data = (BYTE *)dataW;
336 RtlInitAnsiString( &nameA, name );
337 /* FIXME: should use Unicode buffer in TEB */
338 if (!(status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE )))
340 status = NtSetValueKey( hkey, &nameW, 0, type, data, count );
341 RtlFreeUnicodeString( &nameW );
343 if (dataW) HeapFree( GetProcessHeap(), 0, dataW );
344 return RtlNtStatusToDosError( status );
348 /******************************************************************************
349 * RegSetValueA [ADVAPI32.@]
351 DWORD WINAPI RegSetValueA( HKEY hkey, LPCSTR name, DWORD type, LPCSTR data, DWORD count )
353 HKEY subkey = hkey;
354 DWORD ret;
356 TRACE("(0x%x,%s,%ld,%s,%ld)\n", hkey, debugstr_a(name), type, debugstr_a(data), count );
358 if (type != REG_SZ) return ERROR_INVALID_PARAMETER;
360 if (name && name[0]) /* need to create the subkey */
362 if ((ret = RegCreateKeyA( hkey, name, &subkey )) != ERROR_SUCCESS) return ret;
364 ret = RegSetValueExA( subkey, NULL, 0, REG_SZ, (LPBYTE)data, strlen(data)+1 );
365 if (subkey != hkey) RegCloseKey( subkey );
366 return ret;
371 /******************************************************************************
372 * RegQueryValueExA [ADVAPI32.@]
374 * NOTES:
375 * the documentation is wrong: if the buffer is too small it remains untouched
377 DWORD WINAPI RegQueryValueExA( HKEY hkey, LPCSTR name, LPDWORD reserved, LPDWORD type,
378 LPBYTE data, LPDWORD count )
380 NTSTATUS status;
381 ANSI_STRING nameA;
382 UNICODE_STRING nameW;
383 DWORD total_size;
384 char buffer[256], *buf_ptr = buffer;
385 KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
386 static const int info_size = sizeof(*info) - sizeof(info->Data);
388 TRACE("(0x%x,%s,%p,%p,%p,%p=%ld)\n",
389 hkey, debugstr_a(name), reserved, type, data, count, count ? *count : 0 );
391 if ((data && !count) || reserved) return ERROR_INVALID_PARAMETER;
393 RtlInitAnsiString( &nameA, name );
394 /* FIXME: should use Unicode buffer in TEB */
395 if ((status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE )))
396 return RtlNtStatusToDosError(status);
398 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation,
399 buffer, sizeof(buffer), &total_size );
400 if (status && status != STATUS_BUFFER_OVERFLOW) goto done;
402 /* we need to fetch the contents for a string type even if not requested,
403 * because we need to compute the length of the ASCII string. */
404 if (data || is_string(info->Type))
406 /* retry with a dynamically allocated buffer */
407 while (status == STATUS_BUFFER_OVERFLOW)
409 if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
410 if (!(buf_ptr = HeapAlloc( GetProcessHeap(), 0, total_size )))
412 status = STATUS_NO_MEMORY;
413 goto done;
415 info = (KEY_VALUE_PARTIAL_INFORMATION *)buf_ptr;
416 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation,
417 buf_ptr, total_size, &total_size );
420 if (!status)
422 if (is_string(info->Type))
424 DWORD len = WideCharToMultiByte( CP_ACP, 0, (WCHAR *)(buf_ptr + info_size),
425 (total_size - info_size) /sizeof(WCHAR),
426 NULL, 0, NULL, NULL );
427 if (data && len)
429 if (len > *count) status = STATUS_BUFFER_OVERFLOW;
430 else
432 WideCharToMultiByte( CP_ACP, 0, (WCHAR *)(buf_ptr + info_size),
433 (total_size - info_size) /sizeof(WCHAR),
434 data, len, NULL, NULL );
435 /* if the type is REG_SZ and data is not 0-terminated
436 * and there is enough space in the buffer NT appends a \0 */
437 if (len < *count && data[len-1]) data[len] = 0;
440 total_size = len + info_size;
442 else if (data)
444 if (total_size - info_size > *count) status = STATUS_BUFFER_OVERFLOW;
445 else memcpy( data, buf_ptr + info_size, total_size - info_size );
448 else if (status != STATUS_BUFFER_OVERFLOW) goto done;
451 if (type) *type = info->Type;
452 if (count) *count = total_size - info_size;
454 done:
455 if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
456 RtlFreeUnicodeString( &nameW );
457 return RtlNtStatusToDosError(status);
461 /******************************************************************************
462 * RegQueryValueA [ADVAPI32.@]
464 DWORD WINAPI RegQueryValueA( HKEY hkey, LPCSTR name, LPSTR data, LPLONG count )
466 DWORD ret;
467 HKEY subkey = hkey;
469 TRACE("(%x,%s,%p,%ld)\n", hkey, debugstr_a(name), data, count ? *count : 0 );
471 if (name && name[0])
473 if ((ret = RegOpenKeyA( hkey, name, &subkey )) != ERROR_SUCCESS) return ret;
475 ret = RegQueryValueExA( subkey, NULL, NULL, NULL, (LPBYTE)data, count );
476 if (subkey != hkey) RegCloseKey( subkey );
477 if (ret == ERROR_FILE_NOT_FOUND)
479 /* return empty string if default value not found */
480 if (data) *data = 0;
481 if (count) *count = 1;
482 ret = ERROR_SUCCESS;
484 return ret;
488 /******************************************************************************
489 * RegEnumValueA [ADVAPI32.@]
491 DWORD WINAPI RegEnumValueA( HKEY hkey, DWORD index, LPSTR value, LPDWORD val_count,
492 LPDWORD reserved, LPDWORD type, LPBYTE data, LPDWORD count )
494 NTSTATUS status;
495 DWORD total_size;
496 char buffer[256], *buf_ptr = buffer;
497 KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
498 static const int info_size = sizeof(*info) - sizeof(info->Name);
500 TRACE("(%x,%ld,%p,%p,%p,%p,%p,%p)\n",
501 hkey, index, value, val_count, reserved, type, data, count );
503 /* NT only checks count, not val_count */
504 if ((data && !count) || reserved) return ERROR_INVALID_PARAMETER;
506 total_size = info_size + (MAX_PATH + 1) * sizeof(WCHAR);
507 if (data) total_size += *count;
508 total_size = min( sizeof(buffer), total_size );
510 status = NtEnumerateValueKey( hkey, index, KeyValueFullInformation,
511 buffer, total_size, &total_size );
512 if (status && status != STATUS_BUFFER_OVERFLOW) goto done;
514 /* we need to fetch the contents for a string type even if not requested,
515 * because we need to compute the length of the ASCII string. */
516 if (value || data || is_string(info->Type))
518 /* retry with a dynamically allocated buffer */
519 while (status == STATUS_BUFFER_OVERFLOW)
521 if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
522 if (!(buf_ptr = HeapAlloc( GetProcessHeap(), 0, total_size )))
523 return ERROR_NOT_ENOUGH_MEMORY;
524 info = (KEY_VALUE_FULL_INFORMATION *)buf_ptr;
525 status = NtEnumerateValueKey( hkey, index, KeyValueFullInformation,
526 buf_ptr, total_size, &total_size );
529 if (status) goto done;
531 if (value)
533 DWORD len = WideCharToMultiByte( CP_ACP, 0, info->Name, info->NameLength/sizeof(WCHAR),
534 NULL, 0, NULL, NULL );
535 if (len >= *val_count)
537 status = STATUS_BUFFER_OVERFLOW;
538 goto done;
540 WideCharToMultiByte( CP_ACP, 0, info->Name, info->NameLength/sizeof(WCHAR),
541 value, len, NULL, NULL );
542 value[len] = 0;
543 *val_count = len;
546 if (is_string(info->Type))
548 DWORD len = WideCharToMultiByte( CP_ACP, 0, (WCHAR *)(buf_ptr + info->DataOffset),
549 (total_size - info->DataOffset) / sizeof(WCHAR),
550 NULL, 0, NULL, NULL );
551 if (data && len)
553 if (len > *count)
555 status = STATUS_BUFFER_OVERFLOW;
556 goto done;
558 WideCharToMultiByte( CP_ACP, 0, (WCHAR *)(buf_ptr + info->DataOffset),
559 (total_size - info->DataOffset) / sizeof(WCHAR),
560 data, len, NULL, NULL );
561 /* if the type is REG_SZ and data is not 0-terminated
562 * and there is enough space in the buffer NT appends a \0 */
563 if (len < *count && data[len-1]) data[len] = 0;
565 info->DataLength = len;
567 else if (data)
569 if (total_size - info->DataOffset > *count) status = STATUS_BUFFER_OVERFLOW;
570 else memcpy( data, buf_ptr + info->DataOffset, total_size - info->DataOffset );
574 if (type) *type = info->Type;
575 if (count) *count = info->DataLength;
577 done:
578 if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
579 return RtlNtStatusToDosError(status);
584 /******************************************************************************
585 * RegDeleteValueA [ADVAPI32.@]
587 DWORD WINAPI RegDeleteValueA( HKEY hkey, LPCSTR name )
589 UNICODE_STRING nameW;
590 STRING nameA;
591 NTSTATUS status;
593 RtlInitAnsiString( &nameA, name );
594 /* FIXME: should use Unicode buffer in TEB */
595 if (!(status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE )))
597 status = NtDeleteValueKey( hkey, &nameW );
598 RtlFreeUnicodeString( &nameW );
600 return RtlNtStatusToDosError( status );
604 /******************************************************************************
605 * RegLoadKeyA [ADVAPI32.@]
607 LONG WINAPI RegLoadKeyA( HKEY hkey, LPCSTR subkey, LPCSTR filename )
609 HANDLE file;
610 DWORD ret, len, err = GetLastError();
612 TRACE( "(%x,%s,%s)\n", hkey, debugstr_a(subkey), debugstr_a(filename) );
614 if (!filename || !*filename) return ERROR_INVALID_PARAMETER;
615 if (!subkey || !*subkey) return ERROR_INVALID_PARAMETER;
617 len = MultiByteToWideChar( CP_ACP, 0, subkey, strlen(subkey), NULL, 0 ) * sizeof(WCHAR);
618 if (len > MAX_PATH*sizeof(WCHAR)) return ERROR_INVALID_PARAMETER;
620 if ((file = CreateFileA( filename, GENERIC_READ, 0, NULL, OPEN_EXISTING,
621 FILE_ATTRIBUTE_NORMAL, 0 )) == INVALID_HANDLE_VALUE)
623 ret = GetLastError();
624 goto done;
627 SERVER_START_VAR_REQ( load_registry, len )
629 req->hkey = hkey;
630 req->file = file;
631 MultiByteToWideChar( CP_ACP, 0, subkey, strlen(subkey),
632 server_data_ptr(req), len/sizeof(WCHAR) );
633 ret = RtlNtStatusToDosError( SERVER_CALL() );
635 SERVER_END_VAR_REQ;
636 CloseHandle( file );
638 done:
639 SetLastError( err ); /* restore the last error code */
640 return ret;
644 /******************************************************************************
645 * RegSaveKeyA [ADVAPI32.@]
647 * PARAMS
648 * hkey [I] Handle of key where save begins
649 * lpFile [I] Address of filename to save to
650 * sa [I] Address of security structure
652 LONG WINAPI RegSaveKeyA( HKEY hkey, LPCSTR file, LPSECURITY_ATTRIBUTES sa )
654 char buffer[1024];
655 int count = 0;
656 LPSTR name;
657 DWORD ret, err;
658 HANDLE handle;
660 TRACE( "(%x,%s,%p)\n", hkey, debugstr_a(file), sa );
662 if (!file || !*file) return ERROR_INVALID_PARAMETER;
664 err = GetLastError();
665 GetFullPathNameA( file, sizeof(buffer), buffer, &name );
666 for (;;)
668 sprintf( name, "reg%04x.tmp", count++ );
669 handle = CreateFileA( buffer, GENERIC_WRITE, 0, NULL,
670 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
671 if (handle != INVALID_HANDLE_VALUE) break;
672 if ((ret = GetLastError()) != ERROR_ALREADY_EXISTS) goto done;
674 /* Something gone haywire ? Please report if this happens abnormally */
675 if (count >= 100)
676 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);
679 SERVER_START_REQ( save_registry )
681 req->hkey = hkey;
682 req->file = handle;
683 ret = RtlNtStatusToDosError( SERVER_CALL() );
685 SERVER_END_REQ;
687 CloseHandle( handle );
688 if (!ret)
690 if (!MoveFileExA( buffer, file, MOVEFILE_REPLACE_EXISTING ))
692 ERR( "Failed to move %s to %s\n", buffer, file );
693 ret = GetLastError();
696 if (ret) DeleteFileA( buffer );
698 done:
699 SetLastError( err ); /* restore last error code */
700 return ret;