Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/ntdll/tests/env.c | 67 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+)
diff --git a/dlls/ntdll/tests/env.c b/dlls/ntdll/tests/env.c index 48c9ed809e2..98a9f7bb569 100644 --- a/dlls/ntdll/tests/env.c +++ b/dlls/ntdll/tests/env.c @@ -515,6 +515,72 @@ static void test_process_params(void) } }
+static NTSTATUS set_env_var(WCHAR **env, const WCHAR *var, const WCHAR *value) +{ + UNICODE_STRING var_string, value_string; + RtlInitUnicodeString(&var_string, var); + RtlInitUnicodeString(&value_string, value); + return RtlSetEnvironmentVariable(env, &var_string, &value_string); +} + +static void check_env_var_(int line, const char *var, const char *value) +{ + char buffer[20]; + DWORD size = GetEnvironmentVariableA(var, buffer, sizeof(buffer)); + if (value) + { + ok_(__FILE__, line)(size == strlen(value), "wrong size %u\n", size); + ok_(__FILE__, line)(!strcmp(buffer, value), "wrong value %s\n", debugstr_a(buffer)); + } + else + { + ok_(__FILE__, line)(!size, "wrong size %u\n", size); + ok_(__FILE__, line)(GetLastError() == ERROR_ENVVAR_NOT_FOUND, "got error %u\n", GetLastError()); + } +} +#define check_env_var(a, b) check_env_var_(__LINE__, a, b) + +static void test_RtlSetCurrentEnvironment(void) +{ + NTSTATUS status; + WCHAR *old_env, *env, *prev; + BOOL ret; + + status = RtlCreateEnvironment(FALSE, &env); + ok(!status, "got %#x\n", status); + + ret = SetEnvironmentVariableA("testenv1", "heis"); + ok(ret, "got error %u\n", GetLastError()); + ret = SetEnvironmentVariableA("testenv2", "dyo"); + ok(ret, "got error %u\n", GetLastError()); + + status = set_env_var(&env, L"testenv1", L"unus"); + ok(!status, "got %#x\n", status); + status = set_env_var(&env, L"testenv3", L"tres"); + ok(!status, "got %#x\n", status); + + old_env = NtCurrentTeb()->Peb->ProcessParameters->Environment; + + RtlSetCurrentEnvironment(env, &prev); + ok(prev == old_env, "got wrong previous env %p\n", prev); + ok(NtCurrentTeb()->Peb->ProcessParameters->Environment == env, "got wrong current env\n"); + + check_env_var("testenv1", "unus"); + check_env_var("testenv2", NULL); + check_env_var("testenv3", "tres"); + check_env_var("PATH", NULL); + + RtlSetCurrentEnvironment(old_env, NULL); + ok(NtCurrentTeb()->Peb->ProcessParameters->Environment == old_env, "got wrong current env\n"); + + check_env_var("testenv1", "heis"); + check_env_var("testenv2", "dyo"); + check_env_var("testenv3", NULL); + + SetEnvironmentVariableA("testenv1", NULL); + SetEnvironmentVariableA("testenv2", NULL); +} + START_TEST(env) { HMODULE mod = GetModuleHandleA("ntdll.dll"); @@ -535,4 +601,5 @@ START_TEST(env) testSet(); testExpand(); test_process_params(); + test_RtlSetCurrentEnvironment(); }
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/ntdll/env.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/env.c b/dlls/ntdll/env.c index 71ae48681df..3acf7f52d5b 100644 --- a/dlls/ntdll/env.c +++ b/dlls/ntdll/env.c @@ -996,14 +996,21 @@ NTSTATUS WINAPI RtlQueryEnvironmentVariable_U(PWSTR env, */ void WINAPI RtlSetCurrentEnvironment(PWSTR new_env, PWSTR* old_env) { + WCHAR *prev; + TRACE("(%p %p)\n", new_env, old_env);
RtlAcquirePebLock();
- if (old_env) *old_env = NtCurrentTeb()->Peb->ProcessParameters->Environment; + prev = NtCurrentTeb()->Peb->ProcessParameters->Environment; NtCurrentTeb()->Peb->ProcessParameters->Environment = new_env;
RtlReleasePebLock(); + + if (old_env) + *old_env = prev; + else + RtlDestroyEnvironment( prev ); }
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=48308 Signed-off-by: Zebediah Figura z.figura12@gmail.com --- ...ms-win-core-processenvironment-l1-1-0.spec | 2 +- ...ms-win-core-processenvironment-l1-2-0.spec | 2 +- dlls/kernel32/kernel32.spec | 4 +- dlls/kernel32/tests/environ.c | 78 +++++++++++++++++++ dlls/kernelbase/kernelbase.spec | 3 +- dlls/kernelbase/process.c | 66 ++++++++++++++++ include/winbase.h | 3 + 7 files changed, 153 insertions(+), 5 deletions(-)
diff --git a/dlls/api-ms-win-core-processenvironment-l1-1-0/api-ms-win-core-processenvironment-l1-1-0.spec b/dlls/api-ms-win-core-processenvironment-l1-1-0/api-ms-win-core-processenvironment-l1-1-0.spec index e3698d6efd1..b39f8bce57a 100644 --- a/dlls/api-ms-win-core-processenvironment-l1-1-0/api-ms-win-core-processenvironment-l1-1-0.spec +++ b/dlls/api-ms-win-core-processenvironment-l1-1-0/api-ms-win-core-processenvironment-l1-1-0.spec @@ -15,7 +15,7 @@ @ stdcall SearchPathW(wstr wstr wstr long ptr ptr) kernel32.SearchPathW @ stdcall SetCurrentDirectoryA(str) kernel32.SetCurrentDirectoryA @ stdcall SetCurrentDirectoryW(wstr) kernel32.SetCurrentDirectoryW -@ stub SetEnvironmentStringsW +@ stdcall SetEnvironmentStringsW(wstr) kernel32.SetEnvironmentStringsW @ stdcall SetEnvironmentVariableA(str str) kernel32.SetEnvironmentVariableA @ stdcall SetEnvironmentVariableW(wstr wstr) kernel32.SetEnvironmentVariableW @ stdcall SetStdHandle(long long) kernel32.SetStdHandle diff --git a/dlls/api-ms-win-core-processenvironment-l1-2-0/api-ms-win-core-processenvironment-l1-2-0.spec b/dlls/api-ms-win-core-processenvironment-l1-2-0/api-ms-win-core-processenvironment-l1-2-0.spec index 2c25ee1a076..5638fdb7b13 100644 --- a/dlls/api-ms-win-core-processenvironment-l1-2-0/api-ms-win-core-processenvironment-l1-2-0.spec +++ b/dlls/api-ms-win-core-processenvironment-l1-2-0/api-ms-win-core-processenvironment-l1-2-0.spec @@ -17,7 +17,7 @@ @ stdcall SearchPathW(wstr wstr wstr long ptr ptr) kernel32.SearchPathW @ stdcall SetCurrentDirectoryA(str) kernel32.SetCurrentDirectoryA @ stdcall SetCurrentDirectoryW(wstr) kernel32.SetCurrentDirectoryW -@ stub SetEnvironmentStringsW +@ stdcall SetEnvironmentStringsW(wstr) kernel32.SetEnvironmentStringsW @ stdcall SetEnvironmentVariableA(str str) kernel32.SetEnvironmentVariableA @ stdcall SetEnvironmentVariableW(wstr wstr) kernel32.SetEnvironmentVariableW @ stdcall SetStdHandle(long long) kernel32.SetStdHandle diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec index 0dd3813987a..fdd0917f444 100644 --- a/dlls/kernel32/kernel32.spec +++ b/dlls/kernel32/kernel32.spec @@ -1388,8 +1388,8 @@ @ stdcall SetDllDirectoryW(wstr) # @ stub SetDynamicTimeZoneInformation @ stdcall -import SetEndOfFile(long) -# @ stub SetEnvironmentStringsA -# @ stub SetEnvironmentStringsW +@ stdcall -import SetEnvironmentStringsA(str) +@ stdcall -import SetEnvironmentStringsW(wstr) @ stdcall -import SetEnvironmentVariableA(str str) @ stdcall -import SetEnvironmentVariableW(wstr wstr) @ stdcall -import SetErrorMode(long) diff --git a/dlls/kernel32/tests/environ.c b/dlls/kernel32/tests/environ.c index 128a5fdbe52..4e1bc5e32ab 100644 --- a/dlls/kernel32/tests/environ.c +++ b/dlls/kernel32/tests/environ.c @@ -602,6 +602,83 @@ static void test_GetEnvironmentStringsW(void) FreeEnvironmentStringsW(env2); }
+#define copy_string(dst, src) memcpy(dst, src, sizeof(src)) + +static void check_env_var_(int line, const char *var, const char *value) +{ + char buffer[20]; + DWORD size = GetEnvironmentVariableA(var, buffer, sizeof(buffer)); + if (value) + { + ok_(__FILE__, line)(size == strlen(value), "wrong size %u\n", size); + ok_(__FILE__, line)(!strcmp(buffer, value), "wrong value %s\n", debugstr_a(buffer)); + } + else + { + ok_(__FILE__, line)(!size, "wrong size %u\n", size); + ok_(__FILE__, line)(GetLastError() == ERROR_ENVVAR_NOT_FOUND, "got error %u\n", GetLastError()); + } +} +#define check_env_var(a, b) check_env_var_(__LINE__, a, b) + +static void test_SetEnvironmentStrings(void) +{ + static const WCHAR testenv[] = L"testenv1=unus\0testenv3=tres\0"; + WCHAR env[200]; + WCHAR *old_env; + BOOL ret; + + ret = SetEnvironmentVariableA("testenv1", "heis"); + ok(ret, "got error %u\n", GetLastError()); + ret = SetEnvironmentVariableA("testenv2", "dyo"); + ok(ret, "got error %u\n", GetLastError()); + + old_env = GetEnvironmentStringsW(); + + memcpy(env, testenv, sizeof(testenv)); + ret = SetEnvironmentStringsW(env); + ok(ret, "got error %u\n", GetLastError()); + ok(!memcmp(env, testenv, sizeof(testenv)), "input parameter should not be changed\n"); + + check_env_var("testenv1", "unus"); + check_env_var("testenv2", NULL); + check_env_var("testenv3", "tres"); + check_env_var("PATH", NULL); + + ret = SetEnvironmentStringsW(old_env); + ok(ret, "got error %u\n", GetLastError()); + + check_env_var("testenv1", "heis"); + check_env_var("testenv2", "dyo"); + check_env_var("testenv3", NULL); + + SetEnvironmentVariableA("testenv1", NULL); + SetEnvironmentVariableA("testenv2", NULL); + + copy_string(env, L"testenv\0"); + SetLastError(0xdeadbeef); + ret = SetEnvironmentStringsW(env); + ok(!ret, "expected failure\n"); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "got error %u\n", GetLastError()); + + copy_string(env, L"=unus\0"); + SetLastError(0xdeadbeef); + ret = SetEnvironmentStringsW(env); + ok(!ret, "expected failure\n"); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "got error %u\n", GetLastError()); + + copy_string(env, L"one=two=three four=five\0"); + ret = SetEnvironmentStringsW(env); + ok(ret, "got error %u\n", GetLastError()); + + check_env_var("one", "two=three four=five"); + + ret = SetEnvironmentStringsW(old_env); + ok(ret, "got error %u\n", GetLastError()); + ret = FreeEnvironmentStringsW(old_env); + ok(ret, "got error %u\n", GetLastError()); +} + START_TEST(environ) { init_functionpointers(); @@ -614,4 +691,5 @@ START_TEST(environ) test_GetComputerNameExA(); test_GetComputerNameExW(); test_GetEnvironmentStringsW(); + test_SetEnvironmentStrings(); } diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec index e73682af0f4..d50d50c2b55 100644 --- a/dlls/kernelbase/kernelbase.spec +++ b/dlls/kernelbase/kernelbase.spec @@ -1422,7 +1422,8 @@ @ stdcall SetDefaultDllDirectories(long) # @ stub SetDynamicTimeZoneInformation @ stdcall SetEndOfFile(long) -@ stub SetEnvironmentStringsW +@ stdcall SetEnvironmentStringsA(str) +@ stdcall SetEnvironmentStringsW(wstr) @ stdcall SetEnvironmentVariableA(str str) @ stdcall SetEnvironmentVariableW(wstr wstr) @ stdcall SetErrorMode(long) diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c index acef2c55a17..c426978114f 100644 --- a/dlls/kernelbase/process.c +++ b/dlls/kernelbase/process.c @@ -1274,6 +1274,72 @@ LPWSTR WINAPI DECLSPEC_HOTPATCH GetEnvironmentStringsW(void) }
+/*********************************************************************** + * SetEnvironmentStringsA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetEnvironmentStringsA( char *env ) +{ + WCHAR *envW; + const char *p = env; + DWORD len; + BOOL ret; + + for (p = env; *p; p += strlen( p ) + 1); + + len = MultiByteToWideChar( CP_ACP, 0, env, p - env, NULL, 0 ); + if (!(envW = HeapAlloc( GetProcessHeap(), 0, len ))) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return FALSE; + } + MultiByteToWideChar( CP_ACP, 0, env, p - env, envW, len ); + ret = SetEnvironmentStringsW( envW ); + HeapFree( GetProcessHeap(), 0, envW ); + return ret; +} + + +/*********************************************************************** + * SetEnvironmentStringsW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetEnvironmentStringsW( WCHAR *env ) +{ + WCHAR *p; + WCHAR *new_env; + NTSTATUS status; + + for (p = env; *p; p += wcslen( p ) + 1) + { + const WCHAR *eq = wcschr( p, '=' ); + if (!eq || eq == p) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + } + + if ((status = RtlCreateEnvironment( FALSE, &new_env ))) + return set_ntstatus( status ); + + for (p = env; *p; p += wcslen( p ) + 1) + { + const WCHAR *eq = wcschr( p, '=' ); + UNICODE_STRING var, value; + var.Buffer = p; + var.Length = (eq - p) * sizeof(WCHAR); + RtlInitUnicodeString( &value, eq + 1 ); + if ((status = RtlSetEnvironmentVariable( &new_env, &var, &value ))) + { + RtlDestroyEnvironment( new_env ); + return set_ntstatus( status ); + } + } + + RtlSetCurrentEnvironment( new_env, NULL ); + return TRUE; +} + + /*********************************************************************** * GetEnvironmentVariableA (kernelbase.@) */ diff --git a/include/winbase.h b/include/winbase.h index f92d864c781..78ed660cc72 100644 --- a/include/winbase.h +++ b/include/winbase.h @@ -2619,6 +2619,9 @@ WINBASEAPI BOOL WINAPI SetDllDirectoryA(LPCSTR); WINBASEAPI BOOL WINAPI SetDllDirectoryW(LPCWSTR); #define SetDllDirectory WINELIB_NAME_AW(SetDllDirectory) WINBASEAPI BOOL WINAPI SetEndOfFile(HANDLE); +WINBASEAPI BOOL WINAPI SetEnvironmentStringsA(char *); +WINBASEAPI BOOL WINAPI SetEnvironmentStringsW(WCHAR *); +#define SetEnvironmentStrings WINELIB_NAME_AW(SetEnvironmentStrings) WINBASEAPI BOOL WINAPI SetEnvironmentVariableA(LPCSTR,LPCSTR); WINBASEAPI BOOL WINAPI SetEnvironmentVariableW(LPCWSTR,LPCWSTR); #define SetEnvironmentVariable WINELIB_NAME_AW(SetEnvironmentVariable)
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=71257
Your paranoid android.
=== wxppro (32 bit report) ===
kernel32: environ: Timeout
=== wxppro (testbot log) ===
The task timed out
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- I'm not actually sure if this patch is worth having. It's not needed for GOG Galaxy 2, which calls SetEnvironmentStringsW() with an environment already containing these variables (so I'm not quite sure what the point is in the first place...)
Broadly, I'm not sure to what degree arbitrary Win32 API functions should be expected to work when the entire environment is wiped. For example, GetTempPath() predictably falls back to C:\windows\system32, but GetUserName() works fine (and would not in Wine without this patch).
dlls/ntdll/env.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/env.c b/dlls/ntdll/env.c index 3acf7f52d5b..452b10b0582 100644 --- a/dlls/ntdll/env.c +++ b/dlls/ntdll/env.c @@ -996,10 +996,26 @@ NTSTATUS WINAPI RtlQueryEnvironmentVariable_U(PWSTR env, */ void WINAPI RtlSetCurrentEnvironment(PWSTR new_env, PWSTR* old_env) { - WCHAR *prev; + static const WCHAR wineW[] = {'W','I','N','E'}; + WCHAR *prev, *p;
TRACE("(%p %p)\n", new_env, old_env);
+ /* ensure that special Wine-internal variables are still defined */ + + for (p = new_env; *p; p += wcslen( p ) + 1) + { + if (!wcsncmp( p, wineW, 4 )) + { + const WCHAR *value = wcschr( p, '=' ) + 1; + UNICODE_STRING var_string, value_string; + var_string.Buffer = p; + var_string.Length = (value - 1 - p) * sizeof(WCHAR); + RtlInitUnicodeString( &value_string, value ); + RtlSetEnvironmentVariable( &new_env, &var_string, &value_string ); + } + } + RtlAcquirePebLock();
prev = NtCurrentTeb()->Peb->ProcessParameters->Environment;
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=71258
Your paranoid android.
=== debiant (32 bit Chinese:China report) ===
ntdll: pipe.c:1557: Test failed: pipe is not signaled