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 0a6dd97f6bb..3de11ae6785 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..26f24caad2b 100644 --- a/dlls/kernel32/tests/environ.c +++ b/dlls/kernel32/tests/environ.c @@ -36,6 +36,7 @@ static BOOL (WINAPI *pGetComputerNameExA)(COMPUTER_NAME_FORMAT,LPSTR,LPDWORD); static BOOL (WINAPI *pGetComputerNameExW)(COMPUTER_NAME_FORMAT,LPWSTR,LPDWORD); static BOOL (WINAPI *pOpenProcessToken)(HANDLE,DWORD,PHANDLE); static BOOL (WINAPI *pGetUserProfileDirectoryA)(HANDLE,LPSTR,LPDWORD); +static BOOL (WINAPI *pSetEnvironmentStringsW)(WCHAR *); static void init_functionpointers(void) { @@ -45,6 +46,7 @@ static void init_functionpointers(void) pGetComputerNameExA = (void *)GetProcAddress(hkernel32, "GetComputerNameExA"); pGetComputerNameExW = (void *)GetProcAddress(hkernel32, "GetComputerNameExW"); + pSetEnvironmentStringsW = (void *)GetProcAddress(hkernel32, "SetEnvironmentStringsW"); pOpenProcessToken = (void *)GetProcAddress(hadvapi32, "OpenProcessToken"); pGetUserProfileDirectoryA = (void *)GetProcAddress(huserenv, "GetUserProfileDirectoryA"); @@ -602,6 +604,89 @@ 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; + + if (!pSetEnvironmentStringsW) + { + win_skip("SetEnvironmentStringsW() is not available\n"); + return; + } + + 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 = pSetEnvironmentStringsW(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 = pSetEnvironmentStringsW(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 = pSetEnvironmentStringsW(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 = pSetEnvironmentStringsW(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 = pSetEnvironmentStringsW(env); + ok(ret, "got error %u\n", GetLastError()); + + check_env_var("one", "two=three four=five"); + + ret = pSetEnvironmentStringsW(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 +699,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 739e7040b72..7d90ce9e5b8 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)