Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=4096 Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/kernel32/profile.c | 170 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 160 insertions(+), 10 deletions(-)
diff --git a/dlls/kernel32/profile.c b/dlls/kernel32/profile.c index 2cf0819023d..3e06543a660 100644 --- a/dlls/kernel32/profile.c +++ b/dlls/kernel32/profile.c @@ -29,7 +29,9 @@ #include "winbase.h" #include "winnls.h" #include "winerror.h" +#include "winreg.h" #include "winternl.h" +#include "shlwapi.h" #include "wine/unicode.h" #include "wine/library.h" #include "wine/debug.h" @@ -1016,6 +1018,129 @@ static BOOL PROFILE_SetString( LPCWSTR section_name, LPCWSTR key_name, return TRUE; }
+static HKEY open_file_mapping_key( const WCHAR *filename ) +{ + static const WCHAR mapping_pathW[] = {'S','o','f','t','w','a','r','e', + '\','M','i','c','r','o','s','o','f','t', + '\','W','i','n','d','o','w','s',' ','N','T', + '\','C','u','r','r','e','n','t','V','e','r','s','i','o','n', + '\','I','n','i','F','i','l','e','M','a','p','p','i','n','g',0}; + static HKEY mapping_key; + HKEY key; + + EnterCriticalSection( &PROFILE_CritSect ); + + if (!mapping_key && RegOpenKeyExW( HKEY_LOCAL_MACHINE, mapping_pathW, 0, KEY_WOW64_64KEY, &mapping_key )) + mapping_key = NULL; + + LeaveCriticalSection( &PROFILE_CritSect ); + + if (mapping_key && !RegOpenKeyExW( mapping_key, PathFindFileNameW( filename ), 0, KEY_READ, &key )) + return key; + return NULL; +} + +static WCHAR *get_key_value( HKEY key, const WCHAR *value ) +{ + DWORD size = 0; + WCHAR *data; + + if (RegGetValueW( key, NULL, value, RRF_RT_REG_SZ | RRF_NOEXPAND, NULL, NULL, &size )) return NULL; + if (!(data = HeapAlloc( GetProcessHeap(), 0, size ))) return NULL; + if (!RegGetValueW( key, NULL, value, RRF_RT_REG_SZ | RRF_NOEXPAND, NULL, (BYTE *)data, &size )) return data; + HeapFree( GetProcessHeap(), 0, data ); + return NULL; +} + +static HKEY open_mapped_key( const WCHAR *path, BOOL write ) +{ + static const WCHAR softwareW[] = {'S','o','f','t','w','a','r','e','\',0}; + static const WCHAR usrW[] = {'U','S','R',':'}; + static const WCHAR sysW[] = {'S','Y','S',':'}; + WCHAR *combined_path; + const WCHAR *p; + LSTATUS res; + HKEY key; + + TRACE("%s\n", debugstr_w( path )); + + for (p = path; strchr("!#@", *p); p++) + FIXME("ignoring %c modifier\n", *p); + + if (!strncmpW( p, usrW, ARRAY_SIZE( usrW ) )) + { + if (write) + res = RegCreateKeyExW( HKEY_CURRENT_USER, p + 4, 0, NULL, 0, KEY_READ | KEY_WRITE, NULL, &key, NULL ); + else + res = RegOpenKeyExW( HKEY_CURRENT_USER, p + 4, 0, KEY_READ, &key ); + return res ? NULL : key; + } + + if (!strncmpW( p, sysW, ARRAY_SIZE( sysW ) )) + { + p += 4; + if (!(combined_path = HeapAlloc( GetProcessHeap(), 0, + (ARRAY_SIZE( softwareW ) + strlenW( p )) * sizeof(WCHAR) ))) + return NULL; + strcpyW( combined_path, softwareW ); + strcatW( combined_path, p ); + if (write) + res = RegCreateKeyExW( HKEY_LOCAL_MACHINE, combined_path, 0, NULL, + 0, KEY_READ | KEY_WRITE, NULL, &key, NULL ); + else + res = RegOpenKeyExW( HKEY_LOCAL_MACHINE, combined_path, 0, KEY_READ, &key ); + HeapFree( GetProcessHeap(), 0, combined_path ); + return res ? NULL : key; + } + + FIXME("unhandled path syntax %s\n", debugstr_w( path )); + return NULL; +} + +/* returns TRUE if the given section + name is mapped */ +static BOOL get_mapped_section_key( const WCHAR *filename, const WCHAR *section, + const WCHAR *name, BOOL write, HKEY *ret_key ) +{ + static const WCHAR backslashW[] = {'\',0}; + WCHAR *path = NULL, *combined_path; + HKEY key, subkey = NULL; + + if (!(key = open_file_mapping_key( filename ))) + return FALSE; + + if (!RegOpenKeyExW( key, section, 0, KEY_READ, &subkey )) + { + if (!(path = get_key_value( subkey, name ))) + path = get_key_value( subkey, NULL ); + RegCloseKey( subkey ); + RegCloseKey( key ); + if (!path) return FALSE; + } + else + { + if (!(path = get_key_value( key, section ))) + { + if ((path = get_key_value( key, NULL ))) + { + if ((combined_path = HeapAlloc( GetProcessHeap(), 0, + (strlenW( path ) + strlenW( section ) + 2) * sizeof(WCHAR) ))) + { + strcpyW( combined_path, path ); + strcatW( combined_path, backslashW ); + strcatW( combined_path, section ); + } + HeapFree( GetProcessHeap(), 0, path ); + path = combined_path; + } + } + RegCloseKey( key ); + if (!path) return FALSE; + } + + *ret_key = open_mapped_key( path, write ); + HeapFree( GetProcessHeap(), 0, path ); + return TRUE; +}
/********************* API functions **********************************/
@@ -1047,6 +1172,7 @@ INT WINAPI GetPrivateProfileStringW( LPCWSTR section, LPCWSTR entry, int ret; LPWSTR defval_tmp = NULL; const WCHAR *p; + HKEY key;
TRACE("%s,%s,%s,%p,%u,%s\n", debugstr_w(section), debugstr_w(entry), debugstr_w(def_val), buffer, len, debugstr_w(filename)); @@ -1080,22 +1206,46 @@ INT WINAPI GetPrivateProfileStringW( LPCWSTR section, LPCWSTR entry, def_val = defval_tmp; }
- RtlEnterCriticalSection( &PROFILE_CritSect ); - - if (PROFILE_Open( filename, FALSE )) + if (get_mapped_section_key( filename, section, entry, FALSE, &key )) { - PROFILEKEY *key = PROFILE_Find( &CurProfile->section, section, entry, FALSE, FALSE ); - PROFILE_CopyEntry( buffer, (key && key->value) ? key->value : def_val, len ); - TRACE("-> %s\n", debugstr_w( buffer )); + if (key) + { + WCHAR *value; + + if ((value = get_key_value( key, entry ))) + { + lstrcpynW( buffer, value, len ); + HeapFree( GetProcessHeap(), 0, value ); + } + else + lstrcpynW( buffer, def_val, len ); + + RegCloseKey( key ); + } + else + lstrcpynW( buffer, def_val, len ); + ret = strlenW( buffer ); } else { - lstrcpynW( buffer, def_val, len ); - ret = strlenW( buffer ); - } + EnterCriticalSection( &PROFILE_CritSect );
- RtlLeaveCriticalSection( &PROFILE_CritSect ); + if (PROFILE_Open( filename, FALSE )) + { + PROFILEKEY *key = PROFILE_Find( &CurProfile->section, section, entry, FALSE, FALSE ); + PROFILE_CopyEntry( buffer, (key && key->value) ? key->value : def_val, len ); + TRACE("-> %s\n", debugstr_w( buffer )); + ret = strlenW( buffer ); + } + else + { + lstrcpynW( buffer, def_val, len ); + ret = strlenW( buffer ); + } + + LeaveCriticalSection( &PROFILE_CritSect ); + }
HeapFree(GetProcessHeap(), 0, defval_tmp);
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/kernel32/profile.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/dlls/kernel32/profile.c b/dlls/kernel32/profile.c index 3e06543a660..27057e62f7a 100644 --- a/dlls/kernel32/profile.c +++ b/dlls/kernel32/profile.c @@ -1473,6 +1473,9 @@ BOOL WINAPI WritePrivateProfileStringW( LPCWSTR section, LPCWSTR entry, LPCWSTR string, LPCWSTR filename ) { BOOL ret = FALSE; + HKEY key; + + TRACE("(%s, %s, %s, %s)\n", debugstr_w(section), debugstr_w(entry), debugstr_w(string), debugstr_w(filename));
if (!section && !entry && !string) /* documented "file flush" case */ { @@ -1486,6 +1489,20 @@ BOOL WINAPI WritePrivateProfileStringW( LPCWSTR section, LPCWSTR entry, } if (!entry) return PROFILE_DeleteSection( filename, section );
+ if (get_mapped_section_key( filename, section, entry, TRUE, &key )) + { + LSTATUS res; + + if (string) + res = RegSetValueExW( key, entry, 0, REG_SZ, (const BYTE *)string, + (strlenW( string ) + 1) * sizeof(WCHAR) ); + else + res = RegDeleteValueW( key, entry ); + RegCloseKey( key ); + if (res) SetLastError( res ); + return !res; + } + EnterCriticalSection( &PROFILE_CritSect );
if (PROFILE_Open( filename, TRUE ))
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/kernel32/profile.c | 124 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 122 insertions(+), 2 deletions(-)
diff --git a/dlls/kernel32/profile.c b/dlls/kernel32/profile.c index 27057e62f7a..b2360d8e65c 100644 --- a/dlls/kernel32/profile.c +++ b/dlls/kernel32/profile.c @@ -1040,6 +1040,29 @@ static HKEY open_file_mapping_key( const WCHAR *filename ) return NULL; }
+static WCHAR *enum_key( HKEY key, DWORD i ) +{ + WCHAR *value, *new_value; + DWORD max = 256, len; + LSTATUS res; + + if (!(value = HeapAlloc( GetProcessHeap(), 0, max * sizeof(WCHAR) ))) return NULL; + len = max; + while ((res = RegEnumValueW( key, i, value, &len, NULL, NULL, NULL, NULL )) == ERROR_MORE_DATA) + { + max *= 2; + if (!(new_value = HeapReAlloc( GetProcessHeap(), 0, value, max * sizeof(WCHAR) ))) + { + HeapFree( GetProcessHeap(), 0, value ); + return NULL; + } + len = max; + } + if (!res) return value; + HeapFree( GetProcessHeap(), 0, value ); + return NULL; +} + static WCHAR *get_key_value( HKEY key, const WCHAR *value ) { DWORD size = 0; @@ -1142,6 +1165,103 @@ static BOOL get_mapped_section_key( const WCHAR *filename, const WCHAR *section, return TRUE; }
+static DWORD get_mapped_section( HKEY key, WCHAR *buffer, DWORD size, BOOL return_values ) +{ + WCHAR *entry, *value; + DWORD i, ret = 0; + + for (i = 0; (entry = enum_key( key, i )); ++i) + { + lstrcpynW( buffer + ret, entry, size - ret - 1 ); + ret = min( ret + strlenW( entry ) + 1, size - 1 ); + if (return_values && ret < size - 1 && (value = get_key_value( key, entry ))) + { + buffer[ret - 1] = '='; + lstrcpynW( buffer + ret, value, size - ret - 1 ); + ret = min( ret + strlenW( value ) + 1, size - 1 ); + HeapFree( GetProcessHeap(), 0, value ); + } + HeapFree( GetProcessHeap(), 0, entry ); + } + + return ret; +} + +static DWORD get_section( const WCHAR *filename, const WCHAR *section, + WCHAR *buffer, DWORD size, BOOL return_values ) +{ + HKEY key, subkey, section_key; + BOOL use_ini = TRUE; + DWORD ret = 0; + WCHAR *path; + + if ((key = open_file_mapping_key( filename ))) + { + if (!RegOpenKeyExW( key, section, 0, KEY_READ, &subkey )) + { + WCHAR *entry, *value; + HKEY entry_key; + DWORD i; + + for (i = 0; (entry = enum_key( subkey, i )); ++i) + { + if (!(path = get_key_value( subkey, entry ))) + { + HeapFree( GetProcessHeap(), 0, entry ); + continue; + } + + entry_key = open_mapped_key( path, FALSE ); + HeapFree( GetProcessHeap(), 0, path ); + if (!entry_key) + { + HeapFree( GetProcessHeap(), 0, entry ); + continue; + } + + if (entry[0]) + { + if ((value = get_key_value( entry_key, entry ))) + { + lstrcpynW( buffer + ret, entry, size - ret - 1 ); + ret = min( ret + strlenW( entry ) + 1, size - 1 ); + if (return_values && ret < size - 1) + { + buffer[ret - 1] = '='; + lstrcpynW( buffer + ret, value, size - ret - 1 ); + ret = min( ret + strlenW( value ) + 1, size - 1 ); + } + HeapFree( GetProcessHeap(), 0, value ); + } + } + else + { + ret = get_mapped_section( entry_key, buffer, size, return_values ); + use_ini = FALSE; + } + + HeapFree( GetProcessHeap(), 0, entry ); + RegCloseKey( entry_key ); + } + + RegCloseKey( subkey ); + } + else if (get_mapped_section_key( filename, section, NULL, FALSE, §ion_key )) + { + ret = get_mapped_section( section_key, buffer, size, return_values ); + use_ini = FALSE; + RegCloseKey( section_key ); + } + + RegCloseKey( key ); + } + + if (use_ini) + ret += PROFILE_GetSection( filename, section, buffer + ret, size - ret, return_values ); + + return ret; +} + /********************* API functions **********************************/
@@ -1182,7 +1302,7 @@ INT WINAPI GetPrivateProfileStringW( LPCWSTR section, LPCWSTR entry, if (!section) return GetPrivateProfileSectionNamesW( buffer, len, filename ); if (!entry) { - ret = PROFILE_GetSection( filename, section, buffer, len, FALSE ); + ret = get_section( filename, section, buffer, len, FALSE ); if (!buffer[0]) { PROFILE_CopyEntry( buffer, def_val, len ); @@ -1400,7 +1520,7 @@ INT WINAPI GetPrivateProfileSectionW( LPCWSTR section, LPWSTR buffer,
TRACE("(%s, %p, %d, %s)\n", debugstr_w(section), buffer, len, debugstr_w(filename));
- return PROFILE_GetSection( filename, section, buffer, len, TRUE ); + return get_section( filename, section, buffer, len, TRUE ); }
/***********************************************************************
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=75254
Your paranoid android.
=== debiant (32 bit Chinese:China report) ===
Report validation errors: kernel32:console has no test summary line (early exit of the main process?) kernel32:console has unaccounted for todo messages kernel32:console has unaccounted for skip messages
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/kernel32/profile.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/dlls/kernel32/profile.c b/dlls/kernel32/profile.c index b2360d8e65c..0fb4b96aedf 100644 --- a/dlls/kernel32/profile.c +++ b/dlls/kernel32/profile.c @@ -1806,11 +1806,26 @@ DWORD WINAPI GetPrivateProfileSectionNamesW( LPWSTR buffer, DWORD size, LPCWSTR filename) { DWORD ret = 0; + HKEY key; + + if ((key = open_file_mapping_key( filename ))) + { + WCHAR *section; + DWORD i; + + for (i = 0; (section = enum_key( key, i )); ++i) + { + lstrcpynW( buffer + ret, section, size - ret - 1 ); + ret = min( ret + strlenW( section ) + 1, size - 1 ); + } + + RegCloseKey( key ); + }
RtlEnterCriticalSection( &PROFILE_CritSect );
if (PROFILE_Open( filename, FALSE )) - ret = PROFILE_GetSectionNames(buffer, size); + ret += PROFILE_GetSectionNames( buffer + ret, size - ret );
RtlLeaveCriticalSection( &PROFILE_CritSect );
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/kernel32/profile.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+)
diff --git a/dlls/kernel32/profile.c b/dlls/kernel32/profile.c index 0fb4b96aedf..c21c2e3d7d4 100644 --- a/dlls/kernel32/profile.c +++ b/dlls/kernel32/profile.c @@ -1673,6 +1673,7 @@ BOOL WINAPI WritePrivateProfileSectionW( LPCWSTR section, { BOOL ret = FALSE; LPWSTR p; + HKEY key, section_key;
if (!section && !string) { @@ -1686,6 +1687,44 @@ BOOL WINAPI WritePrivateProfileSectionW( LPCWSTR section, } if (!string) return PROFILE_DeleteSection( filename, section );
+ if ((key = open_file_mapping_key( filename ))) + { + /* replace existing entries, but only if they are mapped, and do not + * delete any keys */ + + const WCHAR *entry, *p; + + for (entry = string; *entry; entry += strlenW( entry ) + 1) + { + if ((p = strchrW( entry, '=' ))) + { + WCHAR *entry_copy; + p++; + if (!(entry_copy = HeapAlloc( GetProcessHeap(), 0, (p - entry) * sizeof(WCHAR) ))) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + RegCloseKey( key ); + return FALSE; + } + lstrcpynW( entry_copy, entry, p - entry ); + if (get_mapped_section_key( filename, section, entry_copy, TRUE, §ion_key )) + { + LSTATUS res = RegSetValueExW( section_key, entry_copy, 0, REG_SZ, (const BYTE *)p, + (strlenW( p ) + 1) * sizeof(WCHAR) ); + RegCloseKey( section_key ); + if (res) + { + SetLastError( res ); + RegCloseKey( key ); + return FALSE; + } + } + } + } + RegCloseKey( key ); + return TRUE; + } + EnterCriticalSection( &PROFILE_CritSect );
if (PROFILE_Open( filename, TRUE ))
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/kernel32/profile.c | 66 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 2 deletions(-)
diff --git a/dlls/kernel32/profile.c b/dlls/kernel32/profile.c index c21c2e3d7d4..76f74748a12 100644 --- a/dlls/kernel32/profile.c +++ b/dlls/kernel32/profile.c @@ -1262,6 +1262,68 @@ static DWORD get_section( const WCHAR *filename, const WCHAR *section, return ret; }
+static void delete_key_values( HKEY key ) +{ + WCHAR *entry; + + while ((entry = enum_key( key, 0 ))) + { + RegDeleteValueW( key, entry ); + HeapFree( GetProcessHeap(), 0, entry ); + } +} + +static BOOL delete_section( const WCHAR *filename, const WCHAR *section ) +{ + HKEY key, subkey, section_key; + + if ((key = open_file_mapping_key( filename ))) + { + if (!RegOpenKeyExW( key, section, 0, KEY_READ, &subkey )) + { + WCHAR *entry, *path; + HKEY entry_key; + DWORD i; + + for (i = 0; (entry = enum_key( subkey, i )); ++i) + { + if (!(path = get_key_value( subkey, entry ))) + { + HeapFree( GetProcessHeap(), 0, path ); + continue; + } + + entry_key = open_mapped_key( path, TRUE ); + HeapFree( GetProcessHeap(), 0, path ); + if (!entry_key) + { + HeapFree( GetProcessHeap(), 0, entry ); + continue; + } + + if (entry[0]) + RegDeleteValueW( entry_key, entry ); + else + delete_key_values( entry_key ); + + HeapFree( GetProcessHeap(), 0, entry ); + RegCloseKey( entry_key ); + } + + RegCloseKey( subkey ); + } + else if (get_mapped_section_key( filename, section, NULL, TRUE, §ion_key )) + { + delete_key_values( section_key ); + RegCloseKey( section_key ); + } + + RegCloseKey( key ); + } + + return PROFILE_DeleteSection( filename, section ); +} + /********************* API functions **********************************/
@@ -1607,7 +1669,7 @@ BOOL WINAPI WritePrivateProfileStringW( LPCWSTR section, LPCWSTR entry, LeaveCriticalSection( &PROFILE_CritSect ); return FALSE; } - if (!entry) return PROFILE_DeleteSection( filename, section ); + if (!entry) return delete_section( filename, section );
if (get_mapped_section_key( filename, section, entry, TRUE, &key )) { @@ -1685,7 +1747,7 @@ BOOL WINAPI WritePrivateProfileSectionW( LPCWSTR section, LeaveCriticalSection( &PROFILE_CritSect ); return FALSE; } - if (!string) return PROFILE_DeleteSection( filename, section ); + if (!string) return delete_section( filename, section );
if ((key = open_file_mapping_key( filename ))) {
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- v4: prevent virtualization another way...
dlls/kernel32/tests/profile.c | 419 ++++++++++++++++++++++++++++++++++ 1 file changed, 419 insertions(+)
diff --git a/dlls/kernel32/tests/profile.c b/dlls/kernel32/tests/profile.c index ce82a746e71..2fba26542f8 100644 --- a/dlls/kernel32/tests/profile.c +++ b/dlls/kernel32/tests/profile.c @@ -32,6 +32,15 @@ #define TESTFILE ".\testwine.ini" #define TESTFILE2 ".\testwine2.ini"
+static void check_profile_string_(int line, const char *section, const char *name, const char *file, const char *expect) +{ + char value[200] = {0}; + DWORD ret = GetPrivateProfileStringA(section, name, "default", value, sizeof(value), file); + ok_(__FILE__, line)(ret == strlen(expect), "expected len %u, got %u\n", strlen(expect), ret); + ok_(__FILE__, line)(!strcmp(value, expect), "expected %s, got %s\n", debugstr_a(expect), debugstr_a(value)); +} +#define check_profile_string(a, b, c, d) check_profile_string_(__LINE__, a, b, c, d); + struct _profileInt { LPCSTR section; LPCSTR key; @@ -1163,6 +1172,415 @@ static void test_profile_struct(void) ok(ret, "got error %u\n", GetLastError()); }
+static void check_registry_value_(int line, HKEY key, const char *value, const char *expect) +{ + char buffer[30]; + DWORD type, size = sizeof(buffer); + LSTATUS ret; + + memset(buffer, 0xcc, sizeof(buffer)); + ret = RegQueryValueExA(key, value, 0, &type, (BYTE *)buffer, &size); + ok_(__FILE__, line)(!ret, "got error %u\n", ret); + ok_(__FILE__, line)(!strcmp(buffer, expect), "expected %s, got %s\n", debugstr_a(expect), debugstr_a(buffer)); + ok_(__FILE__, line)(type == REG_SZ, "got type %u\n", type); +} +#define check_registry_value(a, b, c) check_registry_value_(__LINE__, a, b, c) + +static void test_registry_mapping(void) +{ + static const DWORD ivalue = 0xabacab; + HKEY mapping_key, mapped_key, mapping_subkey; + char buffer[30]; + LSTATUS ret; + + /* impersonate ourselves to prevent registry virtualization */ + ret = ImpersonateSelf(SecurityImpersonation); + ok(ret, "got error %u\n", GetLastError()); + + ret = RegCreateKeyExA(HKEY_LOCAL_MACHINE, + "Software\Microsoft\Windows NT\CurrentVersion\IniFileMapping\winetest.ini", + 0, NULL, 0, KEY_READ | KEY_WRITE | KEY_WOW64_64KEY, NULL, &mapping_key, NULL); + if (ret == ERROR_ACCESS_DENIED) + { + skip("Not enough permissions to write to the IniFileMapping key.\n"); + return; + } + ok(!ret, "got error %u\n", ret); + + ret = RegSetValueExA(mapping_key, "section1", 0, REG_SZ, (BYTE *)"USR:winetest_map", sizeof("USR:winetest_map")); + ok(!ret, "got error %u\n", ret); + ret = WritePrivateProfileStringA(NULL, NULL, NULL, "winetest.ini"); + todo_wine ok(ret, "got error %u\n", GetLastError()); + + check_profile_string("section1", "name1", "winetest.ini", "default"); + + ret = WritePrivateProfileStringA("section1", "name1", "value1", "winetest.ini"); + ok(ret, "got error %u\n", GetLastError()); + + check_profile_string("section1", "name1", "winetest.ini", "value1"); + check_profile_string("section1", "name1", "C:/fake/path/winetest.ini", "value1"); + + ret = RegOpenKeyExA(HKEY_CURRENT_USER, "winetest_map", 0, KEY_READ | KEY_WRITE, &mapped_key); + ok(!ret, "got error %u\n", ret); + check_registry_value(mapped_key, "name1", "value1"); + + ret = RegSetValueExA(mapped_key, "name2", 0, REG_SZ, (BYTE *)"value2", sizeof("value2")); + ok(!ret, "got error %u\n", ret); + + check_profile_string("section1", "name2", "winetest.ini", "value2"); + + ret = GetFileAttributesA("C:/windows/winetest.ini"); + ok(ret == INVALID_FILE_ATTRIBUTES, "winetest.ini should not exist.\n"); + + ret = WritePrivateProfileStringA("section1", "name2", NULL, "winetest.ini"); + ok(ret, "got error %u\n", GetLastError()); + ret = RegQueryValueExA(mapped_key, "name2", 0, NULL, NULL, NULL); + ok(ret == ERROR_FILE_NOT_FOUND, "got error %u\n", ret); + + /* Test non-string types. */ + + ret = RegSetValueExA(mapped_key, "name3", 0, REG_DWORD, (BYTE *)&ivalue, sizeof(ivalue)); + ok(!ret, "got error %u\n", ret); + check_profile_string("section1", "name3", "winetest.ini", "default"); + + ret = GetPrivateProfileIntA("section1", "name3", 0, "winetest.ini"); + ok(ret == 0, "got %#x\n", ret); + + ret = RegSetValueExA(mapped_key, "name3", 0, REG_BINARY, (BYTE *)"value3", sizeof("value3")); + ok(!ret, "got error %u\n", ret); + check_profile_string("section1", "name3", "winetest.ini", "default"); + + ret = RegSetValueExA(mapped_key, "name3", 0, REG_MULTI_SZ, (BYTE *)"one\0two\0", sizeof("one\0two\0")); + ok(!ret, "got error %u\n", ret); + check_profile_string("section1", "name3", "winetest.ini", "default"); + + ret = RegSetValueExA(mapped_key, "name3", 0, REG_EXPAND_SZ, (BYTE *)"x%SystemRoot%", sizeof("x%SystemRoot%")); + ok(!ret, "got error %u\n", ret); + check_profile_string("section1", "name3", "winetest.ini", "default"); + + /* Test WritePrivateProfileSection(). Unlike with .ini files, it doesn't + * remove existing entries. */ + + ret = WritePrivateProfileStringA("section1", "name4", "value4", "winetest.ini"); + ok(ret, "got error %u\n", GetLastError()); + ret = WritePrivateProfileStringA("section1", "name5", "value5", "winetest.ini"); + ok(ret, "got error %u\n", GetLastError()); + ret = WritePrivateProfileSectionA("section1", "name4=four\0name6=six\0", "winetest.ini"); + ok(ret, "got error %u\n", GetLastError()); + check_profile_string("section1", "name4", "winetest.ini", "four"); + check_profile_string("section1", "name5", "winetest.ini", "value5"); + check_profile_string("section1", "name6", "winetest.ini", "six"); + + /* Test deleting the section. */ + + RegCloseKey(mapped_key); + + ret = RegCreateKeyExA(HKEY_CURRENT_USER, "winetest_map\subkey", 0, NULL, 0, 0, NULL, &mapped_key, NULL); + ok(!ret, "got error %u\n", ret); + RegCloseKey(mapped_key); + + ret = WritePrivateProfileStringA("section1", "name1", "value1", "winetest.ini"); + ok(ret, "got error %u\n", GetLastError()); + ret = WritePrivateProfileStringA("section1", NULL, NULL, "winetest.ini"); + ok(ret, "got error %u\n", GetLastError()); + check_profile_string("section1", "name1", "winetest.ini", "default"); + + ret = WritePrivateProfileStringA("section1", "name1", "value1", "winetest.ini"); + ok(ret, "got error %u\n", GetLastError()); + ret = WritePrivateProfileSectionA("section1", NULL, "winetest.ini"); + ok(ret, "got error %u\n", GetLastError()); + check_profile_string("section1", "name1", "winetest.ini", "default"); + + ret = RegDeleteKeyA(HKEY_CURRENT_USER, "winetest_map\subkey"); + ok(!ret, "got error %u\n", ret); + ret = RegDeleteKeyA(HKEY_CURRENT_USER, "winetest_map"); + ok(!ret, "got error %u\n", ret); + + /* Test GetPrivateProfileSectionNames(). */ + + memset(buffer, 0xcc, sizeof(buffer)); + ret = GetPrivateProfileSectionNamesA(buffer, 5, "winetest.ini"); + ok(ret == 3, "got %u\n", ret); + ok(!memcmp(buffer, "sec\0", 5), "got %s\n", debugstr_an(buffer, ret)); + + memset(buffer, 0xcc, sizeof(buffer)); + ret = GetPrivateProfileSectionNamesA(buffer, sizeof(buffer), "winetest.ini"); + ok(ret == 9, "got %u\n", ret); + ok(!memcmp(buffer, "section1\0", 10), "got %s\n", debugstr_an(buffer, ret)); + + ret = WritePrivateProfileStringA("file_section", "name1", "value1", "winetest.ini"); + ok(ret, "got error %u\n", GetLastError()); + + memset(buffer, 0xcc, sizeof(buffer)); + ret = GetPrivateProfileSectionNamesA(buffer, 5, "winetest.ini"); + ok(ret == 3, "got %u\n", ret); + ok(!memcmp(buffer, "sec\0", 5), "got %s\n", debugstr_an(buffer, ret)); + + memset(buffer, 0xcc, sizeof(buffer)); + ret = GetPrivateProfileSectionNamesA(buffer, sizeof(buffer), "winetest.ini"); + ok(ret == 22, "got %u\n", ret); + ok(!memcmp(buffer, "section1\0file_section\0", 23), "got %s\n", debugstr_an(buffer, ret)); + + ret = DeleteFileA("C:/windows/winetest.ini"); + ok(ret, "got error %u\n", GetLastError()); + + /* Test the SYS: prefix. */ + + ret = RegSetValueExA(mapping_key, "section2", 0, REG_SZ, (BYTE *)"SYS:winetest_map", sizeof("SYS:winetest_map")); + ok(!ret, "got error %u\n", ret); + ret = WritePrivateProfileStringA(NULL, NULL, NULL, "winetest.ini"); + todo_wine ok(ret, "got error %u\n", GetLastError()); + + check_profile_string("section2", "name1", "winetest.ini", "default"); + + ret = WritePrivateProfileStringA("section2", "name1", "value1", "winetest.ini"); + ok(ret, "got error %u\n", GetLastError()); + + check_profile_string("section2", "name1", "winetest.ini", "value1"); + + ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\winetest_map", 0, KEY_READ | KEY_WRITE, &mapped_key); + ok(!ret, "got error %u\n", ret); + check_registry_value(mapped_key, "name1", "value1"); + + ret = RegSetValueExA(mapped_key, "name2", 0, REG_SZ, (BYTE *)"value2", sizeof("value2")); + ok(!ret, "got error %u\n", ret); + + check_profile_string("section2", "name2", "winetest.ini", "value2"); + + ret = GetFileAttributesA("C:/windows/winetest.ini"); + ok(ret == INVALID_FILE_ATTRIBUTES, "winetest.ini should not exist.\n"); + + ret = RegDeleteKeyA(mapped_key, ""); + ok(!ret, "got error %u\n", ret); + RegCloseKey(mapped_key); + + /* Try writing directly to the .ini file on disk instead. */ + + ret = WritePrivateProfileStringA("section3", "name1", "value1", "winetest.ini"); + ok(ret, "got error %u\n", GetLastError()); + check_profile_string("section3", "name1", "winetest.ini", "value1"); + + ret = RegSetValueExA(mapping_key, "section3", 0, REG_SZ, (BYTE *)"USR:winetest_map", sizeof("USR:winetest_map")); + ok(!ret, "got error %u\n", ret); + ret = WritePrivateProfileStringA(NULL, NULL, NULL, "winetest.ini"); + todo_wine ok(ret, "got error %u\n", GetLastError()); + + check_profile_string("section3", "name1", "winetest.ini", "default"); + + ret = RegOpenKeyExA(HKEY_CURRENT_USER, "winetest_section3", 0, KEY_READ | KEY_WRITE, &mapped_key); + ok(ret == ERROR_FILE_NOT_FOUND, "got error %u\n", ret); + + ret = WritePrivateProfileStringA("section3", "name1", "value2", "winetest.ini"); + ok(ret, "got error %u\n", GetLastError()); + + check_profile_string("section3", "name1", "winetest.ini", "value2"); + + ret = RegOpenKeyExA(HKEY_CURRENT_USER, "winetest_map", 0, KEY_READ | KEY_WRITE, &mapped_key); + ok(!ret, "got error %u\n", ret); + + ret = RegDeleteKeyA(mapped_key, ""); + ok(!ret, "got error %u\n", ret); + RegCloseKey(mapped_key); + + ret = RegDeleteValueA(mapping_key, "section3"); + ok(!ret, "got error %u\n", ret); + ret = WritePrivateProfileStringA(NULL, NULL, NULL, "winetest.ini"); + todo_wine ok(ret, "got error %u\n", GetLastError()); + + check_profile_string("section3", "name1", "winetest.ini", "value1"); + + ret = DeleteFileA("C:/windows/winetest.ini"); + ok(ret, "got error %u\n", GetLastError()); + + /* Test default keys. */ + + ret = WritePrivateProfileStringA("section4", "name1", "value1", "winetest.ini"); + ok(ret, "got error %u\n", GetLastError()); + + check_profile_string("section4", "name1", "winetest.ini", "value1"); + + ret = DeleteFileA("C:/windows/winetest.ini"); + ok(ret, "got error %u\n", GetLastError()); + + ret = RegSetValueExA(mapping_key, NULL, 0, REG_SZ, (BYTE *)"SYS:winetest_default", sizeof("SYS:winetest_default")); + ok(!ret, "got error %u\n", ret); + ret = WritePrivateProfileStringA(NULL, NULL, NULL, "winetest.ini"); + todo_wine ok(ret, "got error %u\n", GetLastError()); + + ret = WritePrivateProfileStringA("section4", "name1", "value1", "winetest.ini"); + ok(ret, "got error %u\n", GetLastError()); + + ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\winetest_default\section4", 0, KEY_READ, &mapped_key); + ok(!ret, "got error %u\n", ret); + check_registry_value(mapped_key, "name1", "value1"); + RegCloseKey(mapped_key); + + ret = RegCreateKeyExA(HKEY_LOCAL_MACHINE, "Software\winetest_default\section5", + 0, NULL, 0, KEY_WRITE, NULL, &mapped_key, NULL); + ok(!ret, "got error %u\n", ret); + ret = RegSetValueExA(mapped_key, "name2", 0, REG_SZ, (BYTE *)"value2", sizeof("value2")); + ok(!ret, "got error %u\n", ret); + RegCloseKey(mapped_key); + + check_profile_string("section5", "name2", "winetest.ini", "value2"); + + ret = GetFileAttributesA("C:/windows/winetest.ini"); + ok(ret == INVALID_FILE_ATTRIBUTES, "winetest.ini should not exist.\n"); + + ret = RegDeleteKeyA(HKEY_LOCAL_MACHINE, "Software\winetest_default\Section4"); + ret = RegDeleteKeyA(HKEY_LOCAL_MACHINE, "Software\winetest_default\Section5"); + ret = RegDeleteKeyA(HKEY_LOCAL_MACHINE, "Software\winetest_default"); + ok(!ret, "got error %u\n", ret); + ret = RegDeleteValueA(mapping_key, NULL); + ok(!ret, "got error %u\n", ret); + + /* Test name-specific mapping. */ + + ret = RegCreateKeyExA(mapping_key, "section6", 0, NULL, 0, KEY_READ | KEY_WRITE, NULL, &mapping_subkey, NULL); + ok(!ret, "got error %u\n", ret); + ret = RegSetValueExA(mapping_subkey, "name1", 0, REG_SZ, (BYTE *)"USR:winetest_name1", sizeof("USR:winetest_name1")); + ok(!ret, "got error %u\n", ret); + ret = RegSetValueExA(mapping_subkey, "name2", 0, REG_SZ, (BYTE *)"SYS:winetest_name2", sizeof("SYS:winetest_name2")); + ok(!ret, "got error %u\n", ret); + ret = WritePrivateProfileStringA(NULL, NULL, NULL, "winetest.ini"); + todo_wine ok(ret, "got error %u\n", GetLastError()); + + ret = WritePrivateProfileStringA("section6", "name1", "value1", "winetest.ini"); + ok(ret, "got error %u\n", GetLastError()); + check_profile_string("section6", "name1", "winetest.ini", "value1"); + + ret = RegOpenKeyExA(HKEY_CURRENT_USER, "winetest_name1", 0, KEY_READ | KEY_WRITE, &mapped_key); + ok(!ret, "got error %u\n", ret); + check_registry_value(mapped_key, "name1", "value1"); + + ret = RegSetValueExA(mapped_key, "name1", 0, REG_SZ, (BYTE *)"one", sizeof("one")); + ok(!ret, "got error %u\n", ret); + check_profile_string("section6", "name1", "winetest.ini", "one"); + + ret = RegDeleteKeyA(mapped_key, ""); + ok(!ret, "got error %u\n", ret); + RegCloseKey(mapped_key); + + ret = WritePrivateProfileStringA("section6", "name2", "value2", "winetest.ini"); + ok(ret, "got error %u\n", GetLastError()); + + ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\winetest_name2", 0, KEY_READ | KEY_WRITE, &mapped_key); + ok(!ret, "got error %u\n", ret); + check_registry_value(mapped_key, "name2", "value2"); + + ret = RegSetValueExA(mapped_key, "name2", 0, REG_SZ, (BYTE *)"two", sizeof("two")); + ok(!ret, "got error %u\n", ret); + check_profile_string("section6", "name2", "winetest.ini", "two"); + + ret = RegDeleteKeyA(mapped_key, ""); + ok(!ret, "got error %u\n", ret); + RegCloseKey(mapped_key); + + ret = WritePrivateProfileStringA("section6", "name3", "value3", "winetest.ini"); + ok(ret, "got error %u\n", GetLastError()); + check_profile_string("section6", "name3", "winetest.ini", "value3"); + ret = DeleteFileA("C:/windows/winetest.ini"); + ok(ret, "got error %u\n", GetLastError()); + + /* Test name-specific mapping with Get/WritePrivateProfileSection(). */ + + ret = WritePrivateProfileStringA("section6", "name2", "value2", "winetest.ini"); + ok(ret, "got error %u\n", GetLastError()); + + ret = WritePrivateProfileStringA("section6", "name3", "value3", "winetest.ini"); + ok(ret, "got error %u\n", GetLastError()); + + ret = WritePrivateProfileSectionA("section6", "name1=one\0name3=three\0", "winetest.ini"); + ok(ret, "got error %u\n", GetLastError()); + check_profile_string("section6", "name1", "winetest.ini", "one"); + check_profile_string("section6", "name2", "winetest.ini", "value2"); + check_profile_string("section6", "name3", "winetest.ini", "value3"); + + ret = RegOpenKeyExA(HKEY_CURRENT_USER, "winetest_name1", 0, KEY_READ | KEY_WRITE, &mapped_key); + ok(!ret, "got error %u\n", ret); + ret = RegDeleteValueA(mapped_key, "name1"); + ok(!ret, "got error %u\n", ret); + RegCloseKey(mapped_key); + + memset(buffer, 0xcc, sizeof(buffer)); + ret = GetPrivateProfileSectionA("section6", buffer, 5, "winetest.ini"); + ok(ret == 3, "got %u\n", ret); + ok(!memcmp(buffer, "nam\0", 5), "got %s\n", debugstr_an(buffer, ret)); + + memset(buffer, 0xcc, sizeof(buffer)); + ret = GetPrivateProfileSectionA("section6", buffer, sizeof(buffer), "winetest.ini"); + ok(ret == 26, "got %u\n", ret); + ok(!memcmp(buffer, "name2=value2\0name3=value3\0", 27), "got %s\n", debugstr_an(buffer, ret)); + + ret = WritePrivateProfileStringA("section6", NULL, NULL, "winetest.ini"); + ok(ret, "got error %u\n", GetLastError()); + check_profile_string("section6", "name1", "winetest.ini", "default"); + check_profile_string("section6", "name2", "winetest.ini", "default"); + check_profile_string("section6", "name3", "winetest.ini", "default"); + + ret = RegDeleteKeyA(HKEY_CURRENT_USER, "winetest_name1"); + ok(!ret, "got error %u\n", ret); + ret = RegDeleteKeyA(HKEY_LOCAL_MACHINE, "Software\winetest_name2"); + ok(!ret, "got error %u\n", ret); + ret = DeleteFileA("C:/windows/winetest.ini"); + ok(ret, "got error %u\n", GetLastError()); + + /* Test name-specific mapping with a default value. */ + + ret = RegSetValueExA(mapping_subkey, NULL, 0, REG_SZ, (BYTE *)"USR:winetest_default", sizeof("USR:winetest_default")); + ok(!ret, "got error %u\n", ret); + ret = WritePrivateProfileStringA(NULL, NULL, NULL, "winetest.ini"); + todo_wine ok(ret, "got error %u\n", GetLastError()); + + ret = WritePrivateProfileStringA("section6", "name2", "value2", "winetest.ini"); + ok(ret, "got error %u\n", GetLastError()); + ret = WritePrivateProfileStringA("section6", "name3", "value3", "winetest.ini"); + ok(ret, "got error %u\n", GetLastError()); + + ret = RegOpenKeyExA(HKEY_CURRENT_USER, "winetest_default", 0, KEY_READ | KEY_WRITE, &mapped_key); + ok(!ret, "got error %u\n", ret); + check_registry_value(mapped_key, "name3", "value3"); + + ret = RegSetValueExA(mapped_key, "name3", 0, REG_SZ, (BYTE *)"three", sizeof("three")); + ok(!ret, "got error %u\n", ret); + check_profile_string("section6", "name3", "winetest.ini", "three"); + + memset(buffer, 0xcc, sizeof(buffer)); + ret = GetPrivateProfileSectionA("section6", buffer, sizeof(buffer), "winetest.ini"); + ok(ret == 25, "got %u\n", ret); + todo_wine ok(!memcmp(buffer, "name2=value2\0name3=three\0", 26), "got %s\n", debugstr_an(buffer, ret)); + + ret = WritePrivateProfileSectionA("section6", "name2=duo\0name3=treis\0", "winetest.ini"); + ok(ret, "got error %u\n", GetLastError()); + check_profile_string("section6", "name2", "winetest.ini", "duo"); + check_profile_string("section6", "name3", "winetest.ini", "treis"); + + ret = WritePrivateProfileStringA("section6", NULL, NULL, "winetest.ini"); + ok(ret, "got error %u\n", GetLastError()); + check_profile_string("section6", "name2", "winetest.ini", "default"); + check_profile_string("section6", "name3", "winetest.ini", "default"); + + ret = RegDeleteKeyA(HKEY_LOCAL_MACHINE, "Software\winetest_name2"); + ok(!ret, "got error %u\n", ret); + ret = RegDeleteKeyA(mapped_key, ""); + ok(!ret, "got error %u\n", ret); + RegCloseKey(mapped_key); + + ret = RegDeleteKeyA(mapping_subkey, ""); + ok(!ret, "got error %u\n", ret); + RegCloseKey(mapping_subkey); + + ret = RegDeleteKeyA(mapping_key, ""); + ok(!ret, "got error %u\n", ret); + RegCloseKey(mapping_key); + + ret = DeleteFileA("C:/windows/winetest.ini"); + ok(!ret, "expected failure\n"); + ok(GetLastError() == ERROR_FILE_NOT_FOUND, "got error %u\n", GetLastError()); + ret = RevertToSelf(); + ok(ret, "got error %u\n", GetLastError()); +} + START_TEST(profile) { test_profile_int(); @@ -1191,4 +1609,5 @@ START_TEST(profile) "CR only"); test_WritePrivateProfileString(); test_profile_struct(); + test_registry_mapping(); }
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=75258
Your paranoid android.
=== debiant (32 bit French report) ===
kernel32: profile.c:222: Test succeeded inside todo block: expected ERROR_FILE_NOT_FOUND, got 2
=== debiant (32 bit Japanese:Japan report) ===
kernel32: profile.c:222: Test succeeded inside todo block: expected ERROR_FILE_NOT_FOUND, got 2
=== debiant (32 bit Chinese:China report) ===
Report validation errors: kernel32:console has no test summary line (early exit of the main process?) kernel32:console has unaccounted for todo messages kernel32:console has unaccounted for skip messages