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
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
;
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
;
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
;
110 attr
.Length
= sizeof(attr
);
111 attr
.RootDirectory
= hkey
;
112 attr
.ObjectName
= &nameW
;
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
)
144 char buffer
[256], *buf_ptr
= buffer
;
145 KEY_NODE_INFORMATION
*info
= (KEY_NODE_INFORMATION
*)buffer
;
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
);
171 RtlUnicodeToMultiByteSize( &len
, info
->Name
, info
->NameLength
);
172 RtlUnicodeToMultiByteSize( &cls_len
, (WCHAR
*)(buf_ptr
+ info
->ClassOffset
),
174 if (ft
) *ft
= *(FILETIME
*)&info
->LastWriteTime
;
176 if (len
>= *name_len
|| (class_len
&& (cls_len
>= *class_len
)))
177 status
= STATUS_BUFFER_OVERFLOW
;
181 RtlUnicodeToMultiByteN( name
, len
, NULL
, info
->Name
, info
->NameLength
);
185 *class_len
= cls_len
;
188 RtlUnicodeToMultiByteN( class, cls_len
, NULL
,
189 (WCHAR
*)(buf_ptr
+ info
->ClassOffset
),
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
)
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
);
250 if (len
+ 1 > *class_len
) status
= STATUS_BUFFER_OVERFLOW
;
253 if (class && !status
)
255 RtlUnicodeToMultiByteN( class, len
, NULL
, (WCHAR
*)(buf_ptr
+ info
->ClassOffset
),
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
;
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
282 * hkey [I] Handle of key to close
285 * Success: ERROR_SUCCESS
286 * Failure: Error code
288 DWORD WINAPI
RegCloseKey( HKEY hkey
)
290 if (!hkey
|| hkey
>= (HKEY
)0x80000000) return ERROR_SUCCESS
;
291 return RtlNtStatusToDosError( NtClose( hkey
) );
295 /******************************************************************************
296 * RegDeleteKeyA [ADVAPI32.@]
298 DWORD WINAPI
RegDeleteKeyA( HKEY hkey
, LPCSTR name
)
303 if (!name
|| !*name
) return NtDeleteKey( hkey
);
304 if (!(ret
= RegOpenKeyExA( hkey
, name
, 0, 0, &tmp
)))
306 ret
= RtlNtStatusToDosError( NtDeleteKey( tmp
) );
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
;
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 */
334 RtlMultiByteToUnicodeSize( &lenW
, data
, count
);
335 if (!(dataW
= HeapAlloc( GetProcessHeap(), 0, lenW
))) return ERROR_OUTOFMEMORY
;
336 RtlMultiByteToUnicodeN( dataW
, lenW
, NULL
, data
, count
);
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
)
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
);
376 /******************************************************************************
377 * RegQueryValueExA [ADVAPI32.@]
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
)
387 UNICODE_STRING nameW
;
389 char buffer
[256], *buf_ptr
= buffer
;
390 KEY_VALUE_PARTIAL_INFORMATION
*info
= (KEY_VALUE_PARTIAL_INFORMATION
*)buffer
;
391 static const int info_size
= offsetof( KEY_VALUE_PARTIAL_INFORMATION
, Data
);
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
;
420 info
= (KEY_VALUE_PARTIAL_INFORMATION
*)buf_ptr
;
421 status
= NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
,
422 buf_ptr
, total_size
, &total_size
);
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
);
434 if (len
> *count
) status
= STATUS_BUFFER_OVERFLOW
;
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
;
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
;
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
)
474 TRACE("(%x,%s,%p,%ld)\n", hkey
, debugstr_a(name
), data
, count
? *count
: 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 */
486 if (count
) *count
= 1;
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
)
501 char buffer
[256], *buf_ptr
= buffer
;
502 KEY_VALUE_FULL_INFORMATION
*info
= (KEY_VALUE_FULL_INFORMATION
*)buffer
;
503 static const int info_size
= offsetof( KEY_VALUE_FULL_INFORMATION
, Name
);
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 (is_string(info
->Type
))
539 RtlUnicodeToMultiByteSize( &len
, (WCHAR
*)(buf_ptr
+ info
->DataOffset
),
540 total_size
- info
->DataOffset
);
543 if (len
> *count
) status
= STATUS_BUFFER_OVERFLOW
;
546 RtlUnicodeToMultiByteN( data
, len
, NULL
, (WCHAR
*)(buf_ptr
+ info
->DataOffset
),
547 total_size
- info
->DataOffset
);
548 /* if the type is REG_SZ and data is not 0-terminated
549 * and there is enough space in the buffer NT appends a \0 */
550 if (len
< *count
&& data
[len
-1]) data
[len
] = 0;
553 info
->DataLength
= len
;
557 if (total_size
- info
->DataOffset
> *count
) status
= STATUS_BUFFER_OVERFLOW
;
558 else memcpy( data
, buf_ptr
+ info
->DataOffset
, total_size
- info
->DataOffset
);
561 if (value
&& !status
)
565 RtlUnicodeToMultiByteSize( &len
, info
->Name
, info
->NameLength
);
566 if (len
>= *val_count
)
568 status
= STATUS_BUFFER_OVERFLOW
;
571 len
= *val_count
- 1;
572 RtlUnicodeToMultiByteN( value
, len
, NULL
, info
->Name
, info
->NameLength
);
578 RtlUnicodeToMultiByteN( value
, len
, NULL
, info
->Name
, info
->NameLength
);
584 else status
= STATUS_SUCCESS
;
586 if (type
) *type
= info
->Type
;
587 if (count
) *count
= info
->DataLength
;
590 if (buf_ptr
!= buffer
) HeapFree( GetProcessHeap(), 0, buf_ptr
);
591 return RtlNtStatusToDosError(status
);
596 /******************************************************************************
597 * RegDeleteValueA [ADVAPI32.@]
599 DWORD WINAPI
RegDeleteValueA( HKEY hkey
, LPCSTR name
)
601 UNICODE_STRING nameW
;
605 RtlInitAnsiString( &nameA
, name
);
606 /* FIXME: should use Unicode buffer in TEB */
607 if (!(status
= RtlAnsiStringToUnicodeString( &nameW
, &nameA
, TRUE
)))
609 status
= NtDeleteValueKey( hkey
, &nameW
);
610 RtlFreeUnicodeString( &nameW
);
612 return RtlNtStatusToDosError( status
);
616 /******************************************************************************
617 * RegLoadKeyA [ADVAPI32.@]
619 LONG WINAPI
RegLoadKeyA( HKEY hkey
, LPCSTR subkey
, LPCSTR filename
)
622 WCHAR buffer
[MAX_PATH
];
623 DWORD ret
, len
, err
= GetLastError();
625 TRACE( "(%x,%s,%s)\n", hkey
, debugstr_a(subkey
), debugstr_a(filename
) );
627 if (!filename
|| !*filename
) return ERROR_INVALID_PARAMETER
;
628 if (!subkey
|| !*subkey
) return ERROR_INVALID_PARAMETER
;
630 if (!(len
= MultiByteToWideChar( CP_ACP
, 0, subkey
, strlen(subkey
), buffer
, MAX_PATH
)))
631 return ERROR_INVALID_PARAMETER
;
633 if ((file
= CreateFileA( filename
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
,
634 FILE_ATTRIBUTE_NORMAL
, 0 )) == INVALID_HANDLE_VALUE
)
636 ret
= GetLastError();
640 SERVER_START_REQ( load_registry
)
644 wine_server_add_data( req
, buffer
, len
* sizeof(WCHAR
) );
645 ret
= RtlNtStatusToDosError( wine_server_call(req
) );
651 SetLastError( err
); /* restore the last error code */
656 /******************************************************************************
657 * RegSaveKeyA [ADVAPI32.@]
660 * hkey [I] Handle of key where save begins
661 * lpFile [I] Address of filename to save to
662 * sa [I] Address of security structure
664 LONG WINAPI
RegSaveKeyA( HKEY hkey
, LPCSTR file
, LPSECURITY_ATTRIBUTES sa
)
672 TRACE( "(%x,%s,%p)\n", hkey
, debugstr_a(file
), sa
);
674 if (!file
|| !*file
) return ERROR_INVALID_PARAMETER
;
676 err
= GetLastError();
677 GetFullPathNameA( file
, sizeof(buffer
), buffer
, &name
);
680 sprintf( name
, "reg%04x.tmp", count
++ );
681 handle
= CreateFileA( buffer
, GENERIC_WRITE
, 0, NULL
,
682 CREATE_NEW
, FILE_ATTRIBUTE_NORMAL
, 0 );
683 if (handle
!= INVALID_HANDLE_VALUE
) break;
684 if ((ret
= GetLastError()) != ERROR_ALREADY_EXISTS
) goto done
;
686 /* Something gone haywire ? Please report if this happens abnormally */
688 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
);
691 SERVER_START_REQ( save_registry
)
695 ret
= RtlNtStatusToDosError( wine_server_call( req
) );
699 CloseHandle( handle
);
702 if (!MoveFileExA( buffer
, file
, MOVEFILE_REPLACE_EXISTING
))
704 ERR( "Failed to move %s to %s\n", buffer
, file
);
705 ret
= GetLastError();
708 if (ret
) DeleteFileA( buffer
);
711 SetLastError( err
); /* restore last error code */