Fixed packing and layout of many system structures.
[wine/wine64.git] / memory / registry.c
blob1470254b48915558b8a918b5b486b72c822b9170
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 = WideCharToMultiByte( CP_ACP, 0, info->Name, info->NameLength/sizeof(WCHAR),
156 NULL, 0, NULL, NULL );
157 DWORD cls_len = WideCharToMultiByte( CP_ACP, 0, (WCHAR *)(buf_ptr + info->ClassOffset),
158 info->ClassLength / sizeof(WCHAR),
159 NULL, 0, NULL, NULL );
161 if (ft) *ft = *(FILETIME *)&info->LastWriteTime;
163 if (len >= *name_len || (class_len && (cls_len >= *class_len)))
164 status = STATUS_BUFFER_OVERFLOW;
165 else
167 *name_len = len;
168 WideCharToMultiByte( CP_ACP, 0, info->Name, info->NameLength/sizeof(WCHAR),
169 name, len, NULL, NULL );
170 name[len] = 0;
171 if (class_len)
173 *class_len = cls_len;
174 if (class)
176 WideCharToMultiByte( CP_ACP, 0, (WCHAR *)(buf_ptr + info->ClassOffset),
177 info->ClassLength / sizeof(WCHAR),
178 class, cls_len, NULL, NULL );
179 class[cls_len] = 0;
185 if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
186 return RtlNtStatusToDosError( status );
190 /******************************************************************************
191 * RegEnumKeyA [ADVAPI32.@]
193 DWORD WINAPI RegEnumKeyA( HKEY hkey, DWORD index, LPSTR name, DWORD name_len )
195 return RegEnumKeyExA( hkey, index, name, &name_len, NULL, NULL, NULL, NULL );
199 /******************************************************************************
200 * RegQueryInfoKeyA [ADVAPI32.@]
202 DWORD WINAPI RegQueryInfoKeyA( HKEY hkey, LPSTR class, LPDWORD class_len, LPDWORD reserved,
203 LPDWORD subkeys, LPDWORD max_subkey, LPDWORD max_class,
204 LPDWORD values, LPDWORD max_value, LPDWORD max_data,
205 LPDWORD security, FILETIME *modif )
207 NTSTATUS status;
208 char buffer[256], *buf_ptr = buffer;
209 KEY_FULL_INFORMATION *info = (KEY_FULL_INFORMATION *)buffer;
210 DWORD total_size;
212 TRACE( "(0x%x,%p,%ld,%p,%p,%p,%p,%p,%p,%p,%p)\n", hkey, class, class_len ? *class_len : 0,
213 reserved, subkeys, max_subkey, values, max_value, max_data, security, modif );
215 if (class && !class_len && !(GetVersion() & 0x80000000 /*NT*/))
216 return ERROR_INVALID_PARAMETER;
218 status = NtQueryKey( hkey, KeyFullInformation, buffer, sizeof(buffer), &total_size );
220 if (class || class_len)
222 /* retry with a dynamically allocated buffer */
223 while (status == STATUS_BUFFER_OVERFLOW)
225 if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
226 if (!(buf_ptr = HeapAlloc( GetProcessHeap(), 0, total_size )))
227 return ERROR_NOT_ENOUGH_MEMORY;
228 info = (KEY_FULL_INFORMATION *)buf_ptr;
229 status = NtQueryKey( hkey, KeyFullInformation, buf_ptr, total_size, &total_size );
232 if (!status)
234 DWORD len = WideCharToMultiByte( CP_ACP, 0,
235 (WCHAR *)(buf_ptr + info->ClassOffset),
236 info->ClassLength/sizeof(WCHAR),
237 NULL, 0, NULL, NULL );
238 if (class_len)
240 if (len + 1 > *class_len) status = STATUS_BUFFER_OVERFLOW;
241 *class_len = len;
243 if (class && !status)
245 WideCharToMultiByte( CP_ACP, 0,
246 (WCHAR *)(buf_ptr + info->ClassOffset),
247 info->ClassLength/sizeof(WCHAR),
248 class, len, NULL, NULL );
249 class[len] = 0;
254 if (!status || status == STATUS_BUFFER_OVERFLOW)
256 if (subkeys) *subkeys = info->SubKeys;
257 if (max_subkey) *max_subkey = info->MaxNameLen;
258 if (max_class) *max_class = info->MaxClassLen;
259 if (values) *values = info->Values;
260 if (max_value) *max_value = info->MaxValueNameLen;
261 if (max_data) *max_data = info->MaxValueDataLen;
262 if (modif) *modif = *(FILETIME *)&info->LastWriteTime;
265 if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
266 return RtlNtStatusToDosError( status );
270 /******************************************************************************
271 * RegCloseKey [ADVAPI32.@]
273 * Releases the handle of the specified key
275 * PARAMS
276 * hkey [I] Handle of key to close
278 * RETURNS
279 * Success: ERROR_SUCCESS
280 * Failure: Error code
282 DWORD WINAPI RegCloseKey( HKEY hkey )
284 if (!hkey || hkey >= 0x80000000) return ERROR_SUCCESS;
285 return RtlNtStatusToDosError( NtClose( hkey ) );
289 /******************************************************************************
290 * RegDeleteKeyA [ADVAPI32.@]
292 DWORD WINAPI RegDeleteKeyA( HKEY hkey, LPCSTR name )
294 DWORD ret;
295 HKEY tmp;
297 if (!name || !*name) return NtDeleteKey( hkey );
298 if (!(ret = RegOpenKeyExA( hkey, name, 0, 0, &tmp )))
300 ret = RtlNtStatusToDosError( NtDeleteKey( tmp ) );
301 RegCloseKey( tmp );
303 return ret;
308 /******************************************************************************
309 * RegSetValueExA [ADVAPI32.@]
311 DWORD WINAPI RegSetValueExA( HKEY hkey, LPCSTR name, DWORD reserved, DWORD type,
312 CONST BYTE *data, DWORD count )
314 UNICODE_STRING nameW;
315 ANSI_STRING nameA;
316 WCHAR *dataW = NULL;
317 NTSTATUS status;
319 if (count && is_string(type))
321 /* if user forgot to count terminating null, add it (yes NT does this) */
322 if (data[count-1] && !data[count]) count++;
325 if (is_string( type )) /* need to convert to Unicode */
327 DWORD lenW = MultiByteToWideChar( CP_ACP, 0, data, count, NULL, 0 );
328 if (!(dataW = HeapAlloc( GetProcessHeap(), 0, lenW*sizeof(WCHAR) )))
329 return ERROR_OUTOFMEMORY;
330 MultiByteToWideChar( CP_ACP, 0, data, count, dataW, lenW );
331 count = lenW * sizeof(WCHAR);
332 data = (BYTE *)dataW;
335 RtlInitAnsiString( &nameA, name );
336 /* FIXME: should use Unicode buffer in TEB */
337 if (!(status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE )))
339 status = NtSetValueKey( hkey, &nameW, 0, type, data, count );
340 RtlFreeUnicodeString( &nameW );
342 if (dataW) HeapFree( GetProcessHeap(), 0, dataW );
343 return RtlNtStatusToDosError( status );
347 /******************************************************************************
348 * RegSetValueA [ADVAPI32.@]
350 DWORD WINAPI RegSetValueA( HKEY hkey, LPCSTR name, DWORD type, LPCSTR data, DWORD count )
352 HKEY subkey = hkey;
353 DWORD ret;
355 TRACE("(0x%x,%s,%ld,%s,%ld)\n", hkey, debugstr_a(name), type, debugstr_a(data), count );
357 if (type != REG_SZ) return ERROR_INVALID_PARAMETER;
359 if (name && name[0]) /* need to create the subkey */
361 if ((ret = RegCreateKeyA( hkey, name, &subkey )) != ERROR_SUCCESS) return ret;
363 ret = RegSetValueExA( subkey, NULL, 0, REG_SZ, (LPBYTE)data, strlen(data)+1 );
364 if (subkey != hkey) RegCloseKey( subkey );
365 return ret;
370 /******************************************************************************
371 * RegQueryValueExA [ADVAPI32.@]
373 * NOTES:
374 * the documentation is wrong: if the buffer is too small it remains untouched
376 DWORD WINAPI RegQueryValueExA( HKEY hkey, LPCSTR name, LPDWORD reserved, LPDWORD type,
377 LPBYTE data, LPDWORD count )
379 NTSTATUS status;
380 ANSI_STRING nameA;
381 UNICODE_STRING nameW;
382 DWORD total_size;
383 char buffer[256], *buf_ptr = buffer;
384 KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
385 static const int info_size = sizeof(*info) - sizeof(info->Data);
387 TRACE("(0x%x,%s,%p,%p,%p,%p=%ld)\n",
388 hkey, debugstr_a(name), reserved, type, data, count, count ? *count : 0 );
390 if ((data && !count) || reserved) return ERROR_INVALID_PARAMETER;
392 RtlInitAnsiString( &nameA, name );
393 /* FIXME: should use Unicode buffer in TEB */
394 if ((status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE )))
395 return RtlNtStatusToDosError(status);
397 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation,
398 buffer, sizeof(buffer), &total_size );
399 if (status && status != STATUS_BUFFER_OVERFLOW) goto done;
401 /* we need to fetch the contents for a string type even if not requested,
402 * because we need to compute the length of the ASCII string. */
403 if (data || is_string(info->Type))
405 /* retry with a dynamically allocated buffer */
406 while (status == STATUS_BUFFER_OVERFLOW)
408 if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
409 if (!(buf_ptr = HeapAlloc( GetProcessHeap(), 0, total_size )))
411 status = STATUS_NO_MEMORY;
412 goto done;
414 info = (KEY_VALUE_PARTIAL_INFORMATION *)buf_ptr;
415 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation,
416 buf_ptr, total_size, &total_size );
419 if (!status)
421 if (is_string(info->Type))
423 DWORD len = WideCharToMultiByte( CP_ACP, 0, (WCHAR *)(buf_ptr + info_size),
424 (total_size - info_size) /sizeof(WCHAR),
425 NULL, 0, NULL, NULL );
426 if (data && len)
428 if (len > *count) status = STATUS_BUFFER_OVERFLOW;
429 else
431 WideCharToMultiByte( CP_ACP, 0, (WCHAR *)(buf_ptr + info_size),
432 (total_size - info_size) /sizeof(WCHAR),
433 data, len, NULL, NULL );
434 /* if the type is REG_SZ and data is not 0-terminated
435 * and there is enough space in the buffer NT appends a \0 */
436 if (len < *count && data[len-1]) data[len] = 0;
439 total_size = len + info_size;
441 else if (data)
443 if (total_size - info_size > *count) status = STATUS_BUFFER_OVERFLOW;
444 else memcpy( data, buf_ptr + info_size, total_size - info_size );
447 else if (status != STATUS_BUFFER_OVERFLOW) goto done;
450 if (type) *type = info->Type;
451 if (count) *count = total_size - info_size;
453 done:
454 if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
455 RtlFreeUnicodeString( &nameW );
456 return RtlNtStatusToDosError(status);
460 /******************************************************************************
461 * RegQueryValueA [ADVAPI32.@]
463 DWORD WINAPI RegQueryValueA( HKEY hkey, LPCSTR name, LPSTR data, LPLONG count )
465 DWORD ret;
466 HKEY subkey = hkey;
468 TRACE("(%x,%s,%p,%ld)\n", hkey, debugstr_a(name), data, count ? *count : 0 );
470 if (name && name[0])
472 if ((ret = RegOpenKeyA( hkey, name, &subkey )) != ERROR_SUCCESS) return ret;
474 ret = RegQueryValueExA( subkey, NULL, NULL, NULL, (LPBYTE)data, count );
475 if (subkey != hkey) RegCloseKey( subkey );
476 if (ret == ERROR_FILE_NOT_FOUND)
478 /* return empty string if default value not found */
479 if (data) *data = 0;
480 if (count) *count = 1;
481 ret = ERROR_SUCCESS;
483 return ret;
487 /******************************************************************************
488 * RegEnumValueA [ADVAPI32.@]
490 DWORD WINAPI RegEnumValueA( HKEY hkey, DWORD index, LPSTR value, LPDWORD val_count,
491 LPDWORD reserved, LPDWORD type, LPBYTE data, LPDWORD count )
493 NTSTATUS status;
494 DWORD total_size;
495 char buffer[256], *buf_ptr = buffer;
496 KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
497 static const int info_size = sizeof(*info) - sizeof(info->Name);
499 TRACE("(%x,%ld,%p,%p,%p,%p,%p,%p)\n",
500 hkey, index, value, val_count, reserved, type, data, count );
502 /* NT only checks count, not val_count */
503 if ((data && !count) || reserved) return ERROR_INVALID_PARAMETER;
505 total_size = info_size + (MAX_PATH + 1) * sizeof(WCHAR);
506 if (data) total_size += *count;
507 total_size = min( sizeof(buffer), total_size );
509 status = NtEnumerateValueKey( hkey, index, KeyValueFullInformation,
510 buffer, total_size, &total_size );
511 if (status && status != STATUS_BUFFER_OVERFLOW) goto done;
513 /* we need to fetch the contents for a string type even if not requested,
514 * because we need to compute the length of the ASCII string. */
515 if (value || data || is_string(info->Type))
517 /* retry with a dynamically allocated buffer */
518 while (status == STATUS_BUFFER_OVERFLOW)
520 if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
521 if (!(buf_ptr = HeapAlloc( GetProcessHeap(), 0, total_size )))
522 return ERROR_NOT_ENOUGH_MEMORY;
523 info = (KEY_VALUE_FULL_INFORMATION *)buf_ptr;
524 status = NtEnumerateValueKey( hkey, index, KeyValueFullInformation,
525 buf_ptr, total_size, &total_size );
528 if (status) goto done;
530 if (value)
532 DWORD len = WideCharToMultiByte( CP_ACP, 0, info->Name, info->NameLength/sizeof(WCHAR),
533 NULL, 0, NULL, NULL );
534 if (len >= *val_count)
536 status = STATUS_BUFFER_OVERFLOW;
537 goto done;
539 WideCharToMultiByte( CP_ACP, 0, info->Name, info->NameLength/sizeof(WCHAR),
540 value, len, NULL, NULL );
541 value[len] = 0;
542 *val_count = len;
545 if (is_string(info->Type))
547 DWORD len = WideCharToMultiByte( CP_ACP, 0, (WCHAR *)(buf_ptr + info->DataOffset),
548 (total_size - info->DataOffset) / sizeof(WCHAR),
549 NULL, 0, NULL, NULL );
550 if (data && len)
552 if (len > *count)
554 status = STATUS_BUFFER_OVERFLOW;
555 goto done;
557 WideCharToMultiByte( CP_ACP, 0, (WCHAR *)(buf_ptr + info->DataOffset),
558 (total_size - info->DataOffset) / sizeof(WCHAR),
559 data, len, NULL, NULL );
560 /* if the type is REG_SZ and data is not 0-terminated
561 * and there is enough space in the buffer NT appends a \0 */
562 if (len < *count && data[len-1]) data[len] = 0;
564 info->DataLength = len;
566 else if (data)
568 if (total_size - info->DataOffset > *count) status = STATUS_BUFFER_OVERFLOW;
569 else memcpy( data, buf_ptr + info->DataOffset, total_size - info->DataOffset );
573 if (type) *type = info->Type;
574 if (count) *count = info->DataLength;
576 done:
577 if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
578 return RtlNtStatusToDosError(status);
583 /******************************************************************************
584 * RegDeleteValueA [ADVAPI32.@]
586 DWORD WINAPI RegDeleteValueA( HKEY hkey, LPCSTR name )
588 UNICODE_STRING nameW;
589 STRING nameA;
590 NTSTATUS status;
592 RtlInitAnsiString( &nameA, name );
593 /* FIXME: should use Unicode buffer in TEB */
594 if (!(status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE )))
596 status = NtDeleteValueKey( hkey, &nameW );
597 RtlFreeUnicodeString( &nameW );
599 return RtlNtStatusToDosError( status );
603 /******************************************************************************
604 * RegLoadKeyA [ADVAPI32.@]
606 LONG WINAPI RegLoadKeyA( HKEY hkey, LPCSTR subkey, LPCSTR filename )
608 HANDLE file;
609 DWORD ret, len, err = GetLastError();
611 TRACE( "(%x,%s,%s)\n", hkey, debugstr_a(subkey), debugstr_a(filename) );
613 if (!filename || !*filename) return ERROR_INVALID_PARAMETER;
614 if (!subkey || !*subkey) return ERROR_INVALID_PARAMETER;
616 len = MultiByteToWideChar( CP_ACP, 0, subkey, strlen(subkey), NULL, 0 ) * sizeof(WCHAR);
617 if (len > MAX_PATH*sizeof(WCHAR)) return ERROR_INVALID_PARAMETER;
619 if ((file = CreateFileA( filename, GENERIC_READ, 0, NULL, OPEN_EXISTING,
620 FILE_ATTRIBUTE_NORMAL, 0 )) == INVALID_HANDLE_VALUE)
622 ret = GetLastError();
623 goto done;
626 SERVER_START_VAR_REQ( load_registry, len )
628 req->hkey = hkey;
629 req->file = file;
630 MultiByteToWideChar( CP_ACP, 0, subkey, strlen(subkey),
631 server_data_ptr(req), len/sizeof(WCHAR) );
632 ret = RtlNtStatusToDosError( SERVER_CALL() );
634 SERVER_END_VAR_REQ;
635 CloseHandle( file );
637 done:
638 SetLastError( err ); /* restore the last error code */
639 return ret;
643 /******************************************************************************
644 * RegSaveKeyA [ADVAPI32.@]
646 * PARAMS
647 * hkey [I] Handle of key where save begins
648 * lpFile [I] Address of filename to save to
649 * sa [I] Address of security structure
651 LONG WINAPI RegSaveKeyA( HKEY hkey, LPCSTR file, LPSECURITY_ATTRIBUTES sa )
653 char buffer[1024];
654 int count = 0;
655 LPSTR name;
656 DWORD ret, err;
657 HANDLE handle;
659 TRACE( "(%x,%s,%p)\n", hkey, debugstr_a(file), sa );
661 if (!file || !*file) return ERROR_INVALID_PARAMETER;
663 err = GetLastError();
664 GetFullPathNameA( file, sizeof(buffer), buffer, &name );
665 for (;;)
667 sprintf( name, "reg%04x.tmp", count++ );
668 handle = CreateFileA( buffer, GENERIC_WRITE, 0, NULL,
669 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
670 if (handle != INVALID_HANDLE_VALUE) break;
671 if ((ret = GetLastError()) != ERROR_ALREADY_EXISTS) goto done;
673 /* Something gone haywire ? Please report if this happens abnormally */
674 if (count >= 100)
675 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);
678 SERVER_START_REQ( save_registry )
680 req->hkey = hkey;
681 req->file = handle;
682 ret = RtlNtStatusToDosError( SERVER_CALL() );
684 SERVER_END_REQ;
686 CloseHandle( handle );
687 if (!ret)
689 if (!MoveFileExA( buffer, file, MOVEFILE_REPLACE_EXISTING ))
691 ERR( "Failed to move %s to %s\n", buffer, file );
692 ret = GetLastError();
695 if (ret) DeleteFileA( buffer );
697 done:
698 SetLastError( err ); /* restore last error code */
699 return ret;