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.
22 #include "wine/winbase16.h"
23 #include "wine/unicode.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
);
37 /* do a server call without setting the last error code */
38 static inline int reg_server_call( enum request req
)
40 unsigned int res
= server_call_noerr( req
);
41 if (res
) res
= RtlNtStatusToDosError(res
);
45 /******************************************************************************
46 * RegCreateKeyExA [ADVAPI32.130]
48 DWORD WINAPI
RegCreateKeyExA( HKEY hkey
, LPCSTR name
, DWORD reserved
, LPSTR
class,
49 DWORD options
, REGSAM access
, SECURITY_ATTRIBUTES
*sa
,
50 LPHKEY retkey
, LPDWORD dispos
)
52 OBJECT_ATTRIBUTES attr
;
53 UNICODE_STRING nameW
, classW
;
54 ANSI_STRING nameA
, classA
;
57 if (reserved
) return ERROR_INVALID_PARAMETER
;
58 if (!(access
& KEY_ALL_ACCESS
) || (access
& ~KEY_ALL_ACCESS
)) return ERROR_ACCESS_DENIED
;
60 attr
.Length
= sizeof(attr
);
61 attr
.RootDirectory
= hkey
;
62 attr
.ObjectName
= &nameW
;
64 attr
.SecurityDescriptor
= NULL
;
65 attr
.SecurityQualityOfService
= NULL
;
66 RtlInitAnsiString( &nameA
, name
);
67 RtlInitAnsiString( &classA
, class );
69 /* FIXME: should use Unicode buffer in TEB */
70 if (!(status
= RtlAnsiStringToUnicodeString( &nameW
, &nameA
, TRUE
)))
72 if (!(status
= RtlAnsiStringToUnicodeString( &classW
, &classA
, TRUE
)))
74 status
= NtCreateKey( retkey
, access
, &attr
, 0, &classW
, options
, dispos
);
75 RtlFreeUnicodeString( &classW
);
77 RtlFreeUnicodeString( &nameW
);
79 return RtlNtStatusToDosError( status
);
83 /******************************************************************************
84 * RegCreateKeyA [ADVAPI32.129]
86 DWORD WINAPI
RegCreateKeyA( HKEY hkey
, LPCSTR name
, LPHKEY retkey
)
88 return RegCreateKeyExA( hkey
, name
, 0, NULL
, REG_OPTION_NON_VOLATILE
,
89 KEY_ALL_ACCESS
, NULL
, retkey
, NULL
);
94 /******************************************************************************
95 * RegOpenKeyExA [ADVAPI32.149]
97 DWORD WINAPI
RegOpenKeyExA( HKEY hkey
, LPCSTR name
, DWORD reserved
, REGSAM access
, LPHKEY retkey
)
99 OBJECT_ATTRIBUTES attr
;
100 UNICODE_STRING nameW
;
104 attr
.Length
= sizeof(attr
);
105 attr
.RootDirectory
= hkey
;
106 attr
.ObjectName
= &nameW
;
108 attr
.SecurityDescriptor
= NULL
;
109 attr
.SecurityQualityOfService
= NULL
;
111 RtlInitAnsiString( &nameA
, name
);
112 /* FIXME: should use Unicode buffer in TEB */
113 if (!(status
= RtlAnsiStringToUnicodeString( &nameW
, &nameA
, TRUE
)))
115 status
= NtOpenKey( retkey
, access
, &attr
);
116 RtlFreeUnicodeString( &nameW
);
118 return RtlNtStatusToDosError( status
);
122 /******************************************************************************
123 * RegOpenKeyA [ADVAPI32.148]
125 DWORD WINAPI
RegOpenKeyA( HKEY hkey
, LPCSTR name
, LPHKEY retkey
)
127 return RegOpenKeyExA( hkey
, name
, 0, KEY_ALL_ACCESS
, retkey
);
131 /******************************************************************************
132 * RegEnumKeyExA [ADVAPI32.138]
134 DWORD WINAPI
RegEnumKeyExA( HKEY hkey
, DWORD index
, LPSTR name
, LPDWORD name_len
,
135 LPDWORD reserved
, LPSTR
class, LPDWORD class_len
, FILETIME
*ft
)
138 char buffer
[256], *buf_ptr
= buffer
;
139 KEY_NODE_INFORMATION
*info
= (KEY_NODE_INFORMATION
*)buffer
;
142 TRACE( "(0x%x,%ld,%p,%p(%ld),%p,%p,%p,%p)\n", hkey
, index
, name
, name_len
,
143 name_len
? *name_len
: -1, reserved
, class, class_len
, ft
);
145 if (reserved
) return ERROR_INVALID_PARAMETER
;
147 status
= NtEnumerateKey( hkey
, index
, KeyNodeInformation
,
148 buffer
, sizeof(buffer
), &total_size
);
150 while (status
== STATUS_BUFFER_OVERFLOW
)
152 /* retry with a dynamically allocated buffer */
153 if (buf_ptr
!= buffer
) HeapFree( GetProcessHeap(), 0, buf_ptr
);
154 if (!(buf_ptr
= HeapAlloc( GetProcessHeap(), 0, total_size
)))
155 return ERROR_NOT_ENOUGH_MEMORY
;
156 info
= (KEY_NODE_INFORMATION
*)buf_ptr
;
157 status
= NtEnumerateKey( hkey
, index
, KeyNodeInformation
,
158 buf_ptr
, total_size
, &total_size
);
163 DWORD len
= WideCharToMultiByte( CP_ACP
, 0, info
->Name
, info
->NameLength
/sizeof(WCHAR
),
164 NULL
, 0, NULL
, NULL
);
165 DWORD cls_len
= WideCharToMultiByte( CP_ACP
, 0, (WCHAR
*)(buf_ptr
+ info
->ClassOffset
),
166 info
->ClassLength
/ sizeof(WCHAR
),
167 NULL
, 0, NULL
, NULL
);
169 if (ft
) *ft
= info
->LastWriteTime
;
171 if (len
>= *name_len
|| (class_len
&& (cls_len
>= *class_len
)))
172 status
= STATUS_BUFFER_OVERFLOW
;
176 WideCharToMultiByte( CP_ACP
, 0, info
->Name
, info
->NameLength
/sizeof(WCHAR
),
177 name
, len
, NULL
, NULL
);
181 *class_len
= cls_len
;
184 WideCharToMultiByte( CP_ACP
, 0, (WCHAR
*)(buf_ptr
+ info
->ClassOffset
),
185 info
->ClassLength
/ sizeof(WCHAR
),
186 class, cls_len
, NULL
, NULL
);
193 if (buf_ptr
!= buffer
) HeapFree( GetProcessHeap(), 0, buf_ptr
);
194 return RtlNtStatusToDosError( status
);
198 /******************************************************************************
199 * RegEnumKeyA [ADVAPI32.137]
201 DWORD WINAPI
RegEnumKeyA( HKEY hkey
, DWORD index
, LPSTR name
, DWORD name_len
)
203 return RegEnumKeyExA( hkey
, index
, name
, &name_len
, NULL
, NULL
, NULL
, NULL
);
207 /******************************************************************************
208 * RegQueryInfoKeyA [ADVAPI32.152]
210 DWORD WINAPI
RegQueryInfoKeyA( HKEY hkey
, LPSTR
class, LPDWORD class_len
, LPDWORD reserved
,
211 LPDWORD subkeys
, LPDWORD max_subkey
, LPDWORD max_class
,
212 LPDWORD values
, LPDWORD max_value
, LPDWORD max_data
,
213 LPDWORD security
, FILETIME
*modif
)
216 char buffer
[256], *buf_ptr
= buffer
;
217 KEY_FULL_INFORMATION
*info
= (KEY_FULL_INFORMATION
*)buffer
;
220 TRACE( "(0x%x,%p,%ld,%p,%p,%p,%p,%p,%p,%p,%p)\n", hkey
, class, class_len
? *class_len
: 0,
221 reserved
, subkeys
, max_subkey
, values
, max_value
, max_data
, security
, modif
);
223 if (class && !class_len
&& !(GetVersion() & 0x80000000 /*NT*/))
224 return ERROR_INVALID_PARAMETER
;
226 status
= NtQueryKey( hkey
, KeyFullInformation
, buffer
, sizeof(buffer
), &total_size
);
228 if (class || class_len
)
230 /* retry with a dynamically allocated buffer */
231 while (status
== STATUS_BUFFER_OVERFLOW
)
233 if (buf_ptr
!= buffer
) HeapFree( GetProcessHeap(), 0, buf_ptr
);
234 if (!(buf_ptr
= HeapAlloc( GetProcessHeap(), 0, total_size
)))
235 return ERROR_NOT_ENOUGH_MEMORY
;
236 info
= (KEY_FULL_INFORMATION
*)buf_ptr
;
237 status
= NtQueryKey( hkey
, KeyFullInformation
, buf_ptr
, total_size
, &total_size
);
242 DWORD len
= WideCharToMultiByte( CP_ACP
, 0,
243 (WCHAR
*)(buf_ptr
+ info
->ClassOffset
),
244 info
->ClassLength
/sizeof(WCHAR
),
245 NULL
, 0, NULL
, NULL
);
248 if (len
+ 1 > *class_len
) status
= STATUS_BUFFER_OVERFLOW
;
251 if (class && !status
)
253 WideCharToMultiByte( CP_ACP
, 0,
254 (WCHAR
*)(buf_ptr
+ info
->ClassOffset
),
255 info
->ClassLength
/sizeof(WCHAR
),
256 class, len
, NULL
, NULL
);
262 if (!status
|| status
== STATUS_BUFFER_OVERFLOW
)
264 if (subkeys
) *subkeys
= info
->SubKeys
;
265 if (max_subkey
) *max_subkey
= info
->MaxNameLen
;
266 if (max_class
) *max_class
= info
->MaxClassLen
;
267 if (values
) *values
= info
->Values
;
268 if (max_value
) *max_value
= info
->MaxValueNameLen
;
269 if (max_data
) *max_data
= info
->MaxValueDataLen
;
270 if (modif
) *modif
= info
->LastWriteTime
;
273 if (buf_ptr
!= buffer
) HeapFree( GetProcessHeap(), 0, buf_ptr
);
274 return RtlNtStatusToDosError( status
);
278 /******************************************************************************
279 * RegCloseKey [ADVAPI32.126]
281 * Releases the handle of the specified key
284 * hkey [I] Handle of key to close
287 * Success: ERROR_SUCCESS
288 * Failure: Error code
290 DWORD WINAPI
RegCloseKey( HKEY hkey
)
292 if (!hkey
|| hkey
>= 0x80000000) return ERROR_SUCCESS
;
293 return RtlNtStatusToDosError( NtClose( hkey
) );
297 /******************************************************************************
298 * RegDeleteKeyA [ADVAPI32.133]
300 DWORD WINAPI
RegDeleteKeyA( HKEY hkey
, LPCSTR name
)
305 if (!name
|| !*name
) return NtDeleteKey( hkey
);
306 if (!(ret
= RegOpenKeyExA( hkey
, name
, 0, 0, &tmp
)))
308 ret
= RtlNtStatusToDosError( NtDeleteKey( tmp
) );
316 /******************************************************************************
317 * RegSetValueExA [ADVAPI32.169]
319 DWORD WINAPI
RegSetValueExA( HKEY hkey
, LPCSTR name
, DWORD reserved
, DWORD type
,
320 CONST BYTE
*data
, DWORD count
)
322 UNICODE_STRING nameW
;
327 if (count
&& is_string(type
))
329 /* if user forgot to count terminating null, add it (yes NT does this) */
330 if (data
[count
-1] && !data
[count
]) count
++;
333 if (is_string( type
)) /* need to convert to Unicode */
335 DWORD lenW
= MultiByteToWideChar( CP_ACP
, 0, data
, count
, NULL
, 0 );
336 if (!(dataW
= HeapAlloc( GetProcessHeap(), 0, lenW
*sizeof(WCHAR
) )))
337 return ERROR_OUTOFMEMORY
;
338 MultiByteToWideChar( CP_ACP
, 0, data
, count
, dataW
, lenW
);
339 count
= lenW
* sizeof(WCHAR
);
340 data
= (BYTE
*)dataW
;
343 RtlInitAnsiString( &nameA
, name
);
344 /* FIXME: should use Unicode buffer in TEB */
345 if (!(status
= RtlAnsiStringToUnicodeString( &nameW
, &nameA
, TRUE
)))
347 status
= NtSetValueKey( hkey
, &nameW
, 0, type
, data
, count
);
348 RtlFreeUnicodeString( &nameW
);
350 if (dataW
) HeapFree( GetProcessHeap(), 0, dataW
);
351 return RtlNtStatusToDosError( status
);
355 /******************************************************************************
356 * RegSetValueA [ADVAPI32.168]
358 DWORD WINAPI
RegSetValueA( HKEY hkey
, LPCSTR name
, DWORD type
, LPCSTR data
, DWORD count
)
363 TRACE("(0x%x,%s,%ld,%s,%ld)\n", hkey
, debugstr_a(name
), type
, debugstr_a(data
), count
);
365 if (type
!= REG_SZ
) return ERROR_INVALID_PARAMETER
;
367 if (name
&& name
[0]) /* need to create the subkey */
369 if ((ret
= RegCreateKeyA( hkey
, name
, &subkey
)) != ERROR_SUCCESS
) return ret
;
371 ret
= RegSetValueExA( subkey
, NULL
, 0, REG_SZ
, (LPBYTE
)data
, strlen(data
)+1 );
372 if (subkey
!= hkey
) RegCloseKey( subkey
);
378 /******************************************************************************
379 * RegQueryValueExA [ADVAPI32.157]
382 * the documentation is wrong: if the buffer is too small it remains untouched
384 DWORD WINAPI
RegQueryValueExA( HKEY hkey
, LPCSTR name
, LPDWORD reserved
, LPDWORD type
,
385 LPBYTE data
, LPDWORD count
)
389 UNICODE_STRING nameW
;
391 char buffer
[256], *buf_ptr
= buffer
;
392 KEY_VALUE_PARTIAL_INFORMATION
*info
= (KEY_VALUE_PARTIAL_INFORMATION
*)buffer
;
393 static const int info_size
= sizeof(*info
) - sizeof(info
->Data
);
395 TRACE("(0x%x,%s,%p,%p,%p,%p=%ld)\n",
396 hkey
, debugstr_a(name
), reserved
, type
, data
, count
, count
? *count
: 0 );
398 if ((data
&& !count
) || reserved
) return ERROR_INVALID_PARAMETER
;
400 RtlInitAnsiString( &nameA
, name
);
401 /* FIXME: should use Unicode buffer in TEB */
402 if ((status
= RtlAnsiStringToUnicodeString( &nameW
, &nameA
, TRUE
)))
403 return RtlNtStatusToDosError(status
);
405 status
= NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
,
406 buffer
, sizeof(buffer
), &total_size
);
407 if (status
&& status
!= STATUS_BUFFER_OVERFLOW
) goto done
;
409 /* we need to fetch the contents for a string type even if not requested,
410 * because we need to compute the length of the ASCII string. */
411 if (data
|| is_string(info
->Type
))
413 /* retry with a dynamically allocated buffer */
414 while (status
== STATUS_BUFFER_OVERFLOW
)
416 if (buf_ptr
!= buffer
) HeapFree( GetProcessHeap(), 0, buf_ptr
);
417 if (!(buf_ptr
= HeapAlloc( GetProcessHeap(), 0, total_size
)))
419 status
= STATUS_NO_MEMORY
;
422 info
= (KEY_VALUE_PARTIAL_INFORMATION
*)buf_ptr
;
423 status
= NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
,
424 buf_ptr
, total_size
, &total_size
);
429 if (is_string(info
->Type
))
431 DWORD len
= WideCharToMultiByte( CP_ACP
, 0, (WCHAR
*)(buf_ptr
+ info_size
),
432 (total_size
- info_size
) /sizeof(WCHAR
),
433 NULL
, 0, NULL
, NULL
);
436 if (len
> *count
) status
= STATUS_BUFFER_OVERFLOW
;
439 WideCharToMultiByte( CP_ACP
, 0, (WCHAR
*)(buf_ptr
+ info_size
),
440 (total_size
- info_size
) /sizeof(WCHAR
),
441 data
, len
, NULL
, NULL
);
442 /* if the type is REG_SZ and data is not 0-terminated
443 * and there is enough space in the buffer NT appends a \0 */
444 if (len
< *count
&& data
[len
-1]) data
[len
] = 0;
447 total_size
= len
+ info_size
;
451 if (total_size
- info_size
> *count
) status
= STATUS_BUFFER_OVERFLOW
;
452 else memcpy( data
, buf_ptr
+ info_size
, total_size
- info_size
);
455 else if (status
!= STATUS_BUFFER_OVERFLOW
) goto done
;
458 if (type
) *type
= info
->Type
;
459 if (count
) *count
= total_size
- info_size
;
462 if (buf_ptr
!= buffer
) HeapFree( GetProcessHeap(), 0, buf_ptr
);
463 RtlFreeUnicodeString( &nameW
);
464 return RtlNtStatusToDosError(status
);
468 /******************************************************************************
469 * RegQueryValueA [ADVAPI32.156]
471 DWORD WINAPI
RegQueryValueA( HKEY hkey
, LPCSTR name
, LPSTR data
, LPLONG count
)
476 TRACE("(%x,%s,%p,%ld)\n", hkey
, debugstr_a(name
), data
, count
? *count
: 0 );
480 if ((ret
= RegOpenKeyA( hkey
, name
, &subkey
)) != ERROR_SUCCESS
) return ret
;
482 ret
= RegQueryValueExA( subkey
, NULL
, NULL
, NULL
, (LPBYTE
)data
, count
);
483 if (subkey
!= hkey
) RegCloseKey( subkey
);
484 if (ret
== ERROR_FILE_NOT_FOUND
)
486 /* return empty string if default value not found */
488 if (count
) *count
= 1;
495 /******************************************************************************
496 * RegEnumValueA [ADVAPI32.141]
498 DWORD WINAPI
RegEnumValueA( HKEY hkey
, DWORD index
, LPSTR value
, LPDWORD val_count
,
499 LPDWORD reserved
, LPDWORD type
, LPBYTE data
, LPDWORD count
)
503 char buffer
[256], *buf_ptr
= buffer
;
504 KEY_VALUE_FULL_INFORMATION
*info
= (KEY_VALUE_FULL_INFORMATION
*)buffer
;
505 static const int info_size
= sizeof(*info
) - sizeof(info
->Name
);
507 TRACE("(%x,%ld,%p,%p,%p,%p,%p,%p)\n",
508 hkey
, index
, value
, val_count
, reserved
, type
, data
, count
);
510 /* NT only checks count, not val_count */
511 if ((data
&& !count
) || reserved
) return ERROR_INVALID_PARAMETER
;
513 total_size
= info_size
+ (MAX_PATH
+ 1) * sizeof(WCHAR
);
514 if (data
) total_size
+= *count
;
515 total_size
= min( sizeof(buffer
), total_size
);
517 status
= NtEnumerateValueKey( hkey
, index
, KeyValueFullInformation
,
518 buffer
, total_size
, &total_size
);
519 if (status
&& status
!= STATUS_BUFFER_OVERFLOW
) goto done
;
521 /* we need to fetch the contents for a string type even if not requested,
522 * because we need to compute the length of the ASCII string. */
523 if (value
|| data
|| is_string(info
->Type
))
525 /* retry with a dynamically allocated buffer */
526 while (status
== STATUS_BUFFER_OVERFLOW
)
528 if (buf_ptr
!= buffer
) HeapFree( GetProcessHeap(), 0, buf_ptr
);
529 if (!(buf_ptr
= HeapAlloc( GetProcessHeap(), 0, total_size
)))
530 return ERROR_NOT_ENOUGH_MEMORY
;
531 info
= (KEY_VALUE_FULL_INFORMATION
*)buf_ptr
;
532 status
= NtEnumerateValueKey( hkey
, index
, KeyValueFullInformation
,
533 buf_ptr
, total_size
, &total_size
);
536 if (status
) goto done
;
540 DWORD len
= WideCharToMultiByte( CP_ACP
, 0, info
->Name
, info
->NameLength
/sizeof(WCHAR
),
541 NULL
, 0, NULL
, NULL
);
542 if (len
>= *val_count
)
544 status
= STATUS_BUFFER_OVERFLOW
;
547 WideCharToMultiByte( CP_ACP
, 0, info
->Name
, info
->NameLength
/sizeof(WCHAR
),
548 value
, len
, NULL
, NULL
);
553 if (is_string(info
->Type
))
555 DWORD len
= WideCharToMultiByte( CP_ACP
, 0, (WCHAR
*)(buf_ptr
+ info
->DataOffset
),
556 (total_size
- info
->DataOffset
) / sizeof(WCHAR
),
557 NULL
, 0, NULL
, NULL
);
562 status
= STATUS_BUFFER_OVERFLOW
;
565 WideCharToMultiByte( CP_ACP
, 0, (WCHAR
*)(buf_ptr
+ info
->DataOffset
),
566 (total_size
- info
->DataOffset
) / sizeof(WCHAR
),
567 data
, len
, NULL
, NULL
);
568 /* if the type is REG_SZ and data is not 0-terminated
569 * and there is enough space in the buffer NT appends a \0 */
570 if (len
< *count
&& data
[len
-1]) data
[len
] = 0;
572 info
->DataLength
= len
;
576 if (total_size
- info
->DataOffset
> *count
) status
= STATUS_BUFFER_OVERFLOW
;
577 else memcpy( data
, buf_ptr
+ info
->DataOffset
, total_size
- info
->DataOffset
);
581 if (type
) *type
= info
->Type
;
582 if (count
) *count
= info
->DataLength
;
585 if (buf_ptr
!= buffer
) HeapFree( GetProcessHeap(), 0, buf_ptr
);
586 return RtlNtStatusToDosError(status
);
591 /******************************************************************************
592 * RegDeleteValueA [ADVAPI32.135]
594 DWORD WINAPI
RegDeleteValueA( HKEY hkey
, LPCSTR name
)
596 UNICODE_STRING nameW
;
600 RtlInitAnsiString( &nameA
, name
);
601 /* FIXME: should use Unicode buffer in TEB */
602 if (!(status
= RtlAnsiStringToUnicodeString( &nameW
, &nameA
, TRUE
)))
604 status
= NtDeleteValueKey( hkey
, &nameW
);
605 RtlFreeUnicodeString( &nameW
);
607 return RtlNtStatusToDosError( status
);
611 /******************************************************************************
612 * RegLoadKeyA [ADVAPI32.184]
614 LONG WINAPI
RegLoadKeyA( HKEY hkey
, LPCSTR subkey
, LPCSTR filename
)
617 DWORD ret
, len
, err
= GetLastError();
619 TRACE( "(%x,%s,%s)\n", hkey
, debugstr_a(subkey
), debugstr_a(filename
) );
621 if (!filename
|| !*filename
) return ERROR_INVALID_PARAMETER
;
622 if (!subkey
|| !*subkey
) return ERROR_INVALID_PARAMETER
;
624 len
= MultiByteToWideChar( CP_ACP
, 0, subkey
, strlen(subkey
), NULL
, 0 ) * sizeof(WCHAR
);
625 if (len
> MAX_PATH
*sizeof(WCHAR
)) return ERROR_INVALID_PARAMETER
;
627 if ((file
= CreateFileA( filename
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
,
628 FILE_ATTRIBUTE_NORMAL
, 0 )) == INVALID_HANDLE_VALUE
)
630 ret
= GetLastError();
636 struct load_registry_request
*req
= server_alloc_req( sizeof(*req
), len
);
639 MultiByteToWideChar( CP_ACP
, 0, subkey
, strlen(subkey
),
640 server_data_ptr(req
), len
/sizeof(WCHAR
) );
641 ret
= reg_server_call( REQ_LOAD_REGISTRY
);
647 SetLastError( err
); /* restore the last error code */
652 /******************************************************************************
653 * RegSaveKeyA [ADVAPI32.165]
656 * hkey [I] Handle of key where save begins
657 * lpFile [I] Address of filename to save to
658 * sa [I] Address of security structure
660 LONG WINAPI
RegSaveKeyA( HKEY hkey
, LPCSTR file
, LPSECURITY_ATTRIBUTES sa
)
668 TRACE( "(%x,%s,%p)\n", hkey
, debugstr_a(file
), sa
);
670 if (!file
|| !*file
) return ERROR_INVALID_PARAMETER
;
672 err
= GetLastError();
673 GetFullPathNameA( file
, sizeof(buffer
), buffer
, &name
);
676 sprintf( name
, "reg%04x.tmp", count
++ );
677 handle
= CreateFileA( buffer
, GENERIC_WRITE
, 0, NULL
,
678 CREATE_NEW
, FILE_ATTRIBUTE_NORMAL
, 0 );
679 if (handle
!= INVALID_HANDLE_VALUE
) break;
680 if ((ret
= GetLastError()) != ERROR_ALREADY_EXISTS
) goto done
;
682 /* Something gone haywire ? Please report if this happens abnormally */
684 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
);
689 struct save_registry_request
*req
= server_alloc_req( sizeof(*req
), 0 );
692 ret
= reg_server_call( REQ_SAVE_REGISTRY
);
696 CloseHandle( handle
);
699 if (!MoveFileExA( buffer
, file
, MOVEFILE_REPLACE_EXISTING
))
701 ERR( "Failed to move %s to %s\n", buffer
, file
);
702 ret
= GetLastError();
705 if (ret
) DeleteFileA( buffer
);
708 SetLastError( err
); /* restore last error code */