diff --git a/dlls/userenv/Makefile.in b/dlls/userenv/Makefile.in index f3a4149d635..aaf0de1c632 100644 --- a/dlls/userenv/Makefile.in +++ b/dlls/userenv/Makefile.in @@ -3,6 +3,7 @@ TOPOBJDIR = ../.. SRCDIR = @srcdir@ VPATH = @srcdir@ MODULE = userenv.dll +IMPORTS = advapi32 IMPORTLIB = userenv C_SRCS = \ diff --git a/dlls/userenv/tests/userenv.c b/dlls/userenv/tests/userenv.c index 790cc800747..4fa257bba2c 100644 --- a/dlls/userenv/tests/userenv.c +++ b/dlls/userenv/tests/userenv.c @@ -240,32 +240,29 @@ static void test_get_profiles_dir(void) SetLastError(0xdeadbeef); r = GetProfilesDirectoryA(NULL, NULL); expect(FALSE, r); - todo_wine expect_gle(ERROR_INVALID_PARAMETER); SetLastError(0xdeadbeef); r = GetProfilesDirectoryA(NULL, &cch); expect(FALSE, r); - todo_wine expect_gle(ERROR_INVALID_PARAMETER); SetLastError(0xdeadbeef); cch = 1; r = GetProfilesDirectoryA(small_buf, &cch); expect(FALSE, r); - todo_wine expect_gle(ERROR_INSUFFICIENT_BUFFER); /* MSDN claims the returned character count includes the NULL terminator * when the buffer is too small, but that's not in fact what gets returned. */ - todo_wine ok(cch == profiles_len - 1, "expected %d, got %d\n", profiles_len - 1, cch); - buf = HeapAlloc(GetProcessHeap(), 0, cch); + /* Allocate one more character than the return value to prevent a buffer + * overrun. + */ + buf = HeapAlloc(GetProcessHeap(), 0, cch + 1); r = GetProfilesDirectoryA(buf, &cch); /* Rather than a BOOL, the return value is also the number of characters * stored in the buffer. */ - todo_wine expect(profiles_len - 1, r); - todo_wine ok(!strcmp(buf, profiles_dir), "expected %s, got %s\n", profiles_dir, buf); HeapFree(GetProcessHeap(), 0, buf); diff --git a/dlls/userenv/userenv_main.c b/dlls/userenv/userenv_main.c index ddf0e217332..9247f447295 100644 --- a/dlls/userenv/userenv_main.c +++ b/dlls/userenv/userenv_main.c @@ -114,16 +114,128 @@ BOOL WINAPI GetUserProfileDirectoryW( HANDLE hToken, LPWSTR lpProfileDir, return FALSE; } +static const char ProfileListA[] = "Software\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList"; + BOOL WINAPI GetProfilesDirectoryA( LPSTR lpProfilesDir, LPDWORD lpcchSize ) { - FIXME("%p %p\n", lpProfilesDir, lpcchSize ); - return FALSE; + static const char ProfilesDirectory[] = "ProfilesDirectory"; + LONG l; + HKEY key; + BOOL ret = FALSE; + DWORD len = 0, expanded_len; + LPSTR unexpanded_profiles_dir = NULL; + + TRACE("%p %p\n", lpProfilesDir, lpcchSize ); + + if (!lpProfilesDir || !lpcchSize) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + l = RegOpenKeyExA(HKEY_LOCAL_MACHINE, ProfileListA, 0, KEY_READ, &key); + if (l) + { + SetLastError(l); + return FALSE; + } + l = RegQueryValueExA(key, ProfilesDirectory, NULL, NULL, NULL, &len); + if (l) + { + SetLastError(l); + goto end; + } + unexpanded_profiles_dir = HeapAlloc(GetProcessHeap(), 0, len); + if (!unexpanded_profiles_dir) + { + SetLastError(ERROR_OUTOFMEMORY); + goto end; + } + l = RegQueryValueExA(key, ProfilesDirectory, NULL, NULL, + (BYTE *)unexpanded_profiles_dir, &len); + if (l) + { + SetLastError(l); + goto end; + } + expanded_len = ExpandEnvironmentStringsA(unexpanded_profiles_dir, NULL, 0); + /* The returned length doesn't include the NULL terminator. */ + if (*lpcchSize < expanded_len - 1) + { + *lpcchSize = expanded_len - 1; + SetLastError(ERROR_INSUFFICIENT_BUFFER); + goto end; + } + *lpcchSize = expanded_len - 1; + /* The return value is also the expected length. */ + ret = ExpandEnvironmentStringsA(unexpanded_profiles_dir, lpProfilesDir, + expanded_len) - 1; +end: + HeapFree(GetProcessHeap(), 0, unexpanded_profiles_dir); + RegCloseKey(key); + return ret; } +static const WCHAR ProfileListW[] = {'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','\\','P','r','o','f','i','l','e','L','i','s','t',0}; + BOOL WINAPI GetProfilesDirectoryW( LPWSTR lpProfilesDir, LPDWORD lpcchSize ) { - FIXME("%p %p\n", lpProfilesDir, lpcchSize ); - return FALSE; + static const WCHAR ProfilesDirectory[] = {'P','r','o','f','i','l','e','s','D','i','r','e','c','t','o','r','y',0}; + LONG l; + HKEY key; + BOOL ret = FALSE; + DWORD len = 0, expanded_len; + LPWSTR unexpanded_profiles_dir = NULL; + + TRACE("%p %p\n", lpProfilesDir, lpcchSize ); + + if (!lpProfilesDir || !lpcchSize) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + l = RegOpenKeyExW(HKEY_LOCAL_MACHINE, ProfileListW, 0, KEY_READ, &key); + if (l) + { + SetLastError(l); + return FALSE; + } + l = RegQueryValueExW(key, ProfilesDirectory, NULL, NULL, NULL, &len); + if (l) + { + SetLastError(l); + goto end; + } + unexpanded_profiles_dir = HeapAlloc(GetProcessHeap(), 0, len); + if (!unexpanded_profiles_dir) + { + SetLastError(ERROR_OUTOFMEMORY); + goto end; + } + l = RegQueryValueExW(key, ProfilesDirectory, NULL, NULL, + (BYTE *)unexpanded_profiles_dir, &len); + if (l) + { + SetLastError(l); + goto end; + } + expanded_len = ExpandEnvironmentStringsW(unexpanded_profiles_dir, NULL, 0); + /* The returned length doesn't include the NULL terminator. */ + if (*lpcchSize < expanded_len - 1) + { + *lpcchSize = expanded_len - 1; + SetLastError(ERROR_INSUFFICIENT_BUFFER); + goto end; + } + *lpcchSize = expanded_len - 1; + /* The return value is also the expected length. */ + ret = ExpandEnvironmentStringsW(unexpanded_profiles_dir, lpProfilesDir, + expanded_len) - 1; +end: + HeapFree(GetProcessHeap(), 0, unexpanded_profiles_dir); + RegCloseKey(key); + return ret; } BOOL WINAPI GetAllUsersProfileDirectoryA( LPSTR lpProfileDir, LPDWORD lpcchSize )