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);
38 /******************************************************************************
39 * RegCreateKeyExA [ADVAPI32.130]
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;
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;
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.129]
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.149]
90 DWORD WINAPI RegOpenKeyExA( HKEY hkey, LPCSTR name, DWORD reserved, REGSAM access, LPHKEY retkey )
92 OBJECT_ATTRIBUTES attr;
97 attr.Length = sizeof(attr);
98 attr.RootDirectory = hkey;
99 attr.ObjectName = &nameW;
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.148]
118 DWORD WINAPI RegOpenKeyA( HKEY hkey, LPCSTR name, LPHKEY retkey )
120 return RegOpenKeyExA( hkey, name, 0, KEY_ALL_ACCESS, retkey );
124 /******************************************************************************
125 * RegEnumKeyExA [ADVAPI32.138]
127 DWORD WINAPI RegEnumKeyExA( HKEY hkey, DWORD index, LPSTR name, LPDWORD name_len,
128 LPDWORD reserved, LPSTR class, LPDWORD class_len, FILETIME *ft )
131 char buffer[256], *buf_ptr = buffer;
132 KEY_NODE_INFORMATION *info = (KEY_NODE_INFORMATION *)buffer;
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 );
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;
169 WideCharToMultiByte( CP_ACP, 0, info->Name, info->NameLength/sizeof(WCHAR),
170 name, len, NULL, NULL );
174 *class_len = cls_len;
177 WideCharToMultiByte( CP_ACP, 0, (WCHAR *)(buf_ptr + info->ClassOffset),
178 info->ClassLength / sizeof(WCHAR),
179 class, cls_len, NULL, NULL );
186 if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
187 return RtlNtStatusToDosError( status );
191 /******************************************************************************
192 * RegEnumKeyA [ADVAPI32.137]
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.152]
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 )
209 char buffer[256], *buf_ptr = buffer;
210 KEY_FULL_INFORMATION *info = (KEY_FULL_INFORMATION *)buffer;
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 );
235 DWORD len = WideCharToMultiByte( CP_ACP, 0,
236 (WCHAR *)(buf_ptr + info->ClassOffset),
237 info->ClassLength/sizeof(WCHAR),
238 NULL, 0, NULL, NULL );
241 if (len + 1 > *class_len) status = STATUS_BUFFER_OVERFLOW;
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 );
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.126]
274 * Releases the handle of the specified key
277 * hkey [I] Handle of key to close
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.133]
293 DWORD WINAPI RegDeleteKeyA( HKEY hkey, LPCSTR name )
298 if (!name || !*name) return NtDeleteKey( hkey );
299 if (!(ret = RegOpenKeyExA( hkey, name, 0, 0, &tmp )))
301 ret = RtlNtStatusToDosError( NtDeleteKey( tmp ) );
309 /******************************************************************************
310 * RegSetValueExA [ADVAPI32.169]
312 DWORD WINAPI RegSetValueExA( HKEY hkey, LPCSTR name, DWORD reserved, DWORD type,
313 CONST BYTE *data, DWORD count )
315 UNICODE_STRING nameW;
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.168]
351 DWORD WINAPI RegSetValueA( HKEY hkey, LPCSTR name, DWORD type, LPCSTR data, DWORD count )
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 );
371 /******************************************************************************
372 * RegQueryValueExA [ADVAPI32.157]
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 )
382 UNICODE_STRING nameW;
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;
415 info = (KEY_VALUE_PARTIAL_INFORMATION *)buf_ptr;
416 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation,
417 buf_ptr, total_size, &total_size );
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 );
429 if (len > *count) status = STATUS_BUFFER_OVERFLOW;
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;
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;
455 if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
456 RtlFreeUnicodeString( &nameW );
457 return RtlNtStatusToDosError(status);
461 /******************************************************************************
462 * RegQueryValueA [ADVAPI32.156]
464 DWORD WINAPI RegQueryValueA( HKEY hkey, LPCSTR name, LPSTR data, LPLONG count )
469 TRACE("(%x,%s,%p,%ld)\n", hkey, debugstr_a(name), data, count ? *count : 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 */
481 if (count) *count = 1;
488 /******************************************************************************
489 * RegEnumValueA [ADVAPI32.141]
491 DWORD WINAPI RegEnumValueA( HKEY hkey, DWORD index, LPSTR value, LPDWORD val_count,
492 LPDWORD reserved, LPDWORD type, LPBYTE data, LPDWORD count )
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;
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;
540 WideCharToMultiByte( CP_ACP, 0, info->Name, info->NameLength/sizeof(WCHAR),
541 value, len, NULL, NULL );
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 );
555 status = STATUS_BUFFER_OVERFLOW;
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;
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;
578 if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
579 return RtlNtStatusToDosError(status);
584 /******************************************************************************
585 * RegDeleteValueA [ADVAPI32.135]
587 DWORD WINAPI RegDeleteValueA( HKEY hkey, LPCSTR name )
589 UNICODE_STRING nameW;
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.184]
607 LONG WINAPI RegLoadKeyA( HKEY hkey, LPCSTR subkey, LPCSTR filename )
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();
627 SERVER_START_VAR_REQ( load_registry, len )
631 MultiByteToWideChar( CP_ACP, 0, subkey, strlen(subkey),
632 server_data_ptr(req), len/sizeof(WCHAR) );
633 ret = RtlNtStatusToDosError( SERVER_CALL() );
639 SetLastError( err ); /* restore the last error code */
644 /******************************************************************************
645 * RegSaveKeyA [ADVAPI32.165]
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 )
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 );
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 */
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 )
683 ret = RtlNtStatusToDosError( SERVER_CALL() );
687 CloseHandle( handle );
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 );
699 SetLastError( err ); /* restore last error code */