diff --git a/modules/rostests/apitests/ntdll/CMakeLists.txt b/modules/rostests/apitests/ntdll/CMakeLists.txt index 71f8ec4af7e..223d7d5276f 100644 --- a/modules/rostests/apitests/ntdll/CMakeLists.txt +++ b/modules/rostests/apitests/ntdll/CMakeLists.txt @@ -129,6 +129,8 @@ list(APPEND SOURCE RtlNtPathNameToDosPathName.c RtlpApplyLengthFunction.c RtlpEnsureBufferSize.c + RtlQueryEnvironmentVariable.c + RtlQueryEnvironmentVariable_U.c RtlQueryTimeZoneInfo.c RtlReAllocateHeap.c RtlRemovePrivileges.c diff --git a/modules/rostests/apitests/ntdll/RtlQueryEnvironmentVariable.c b/modules/rostests/apitests/ntdll/RtlQueryEnvironmentVariable.c new file mode 100644 index 00000000000..2d34d4d5777 --- /dev/null +++ b/modules/rostests/apitests/ntdll/RtlQueryEnvironmentVariable.c @@ -0,0 +1,98 @@ +/* + * PROJECT: ReactOS API tests + * LICENSE: MIT (https://spdx.org/licenses/MIT) + * PURPOSE: Tests for RtlQueryEnvironmentVariable + * COPYRIGHT: Copyright 2026 Timo Kreuzer + */ + +#include "precomp.h" + +typedef +NTSTATUS +NTAPI +FN_RtlQueryEnvironmentVariable( + _In_opt_ PVOID Environment, + _In_reads_(NameLength) PWSTR Name, + _In_ SIZE_T NameLength, + _Out_writes_(ValueLength) PWSTR Value, + _In_ SIZE_T ValueLength, + _Out_ PSIZE_T ReturnLength); + +static FN_RtlQueryEnvironmentVariable* pRtlQueryEnvironmentVariable; + +START_TEST(RtlQueryEnvironmentVariable) +{ + static WCHAR TestEnv[] = L"TestVar=TestVal\0Foo=4\0EmptyVar\0Bar=8\0\0"; + WCHAR Buffer[32]; + SIZE_T ReturnLength; + NTSTATUS Status; + + HINSTANCE hinst = GetModuleHandleA("ntdll.dll"); + pRtlQueryEnvironmentVariable = (FN_RtlQueryEnvironmentVariable*) + GetProcAddress(hinst, "RtlQueryEnvironmentVariable"); + if (pRtlQueryEnvironmentVariable == NULL) + { + ok(GetNTVersion() < _WIN32_WINNT_VISTA, "RtlQueryEnvironmentVariable not available on NT6+\n"); + skip("RtlQueryEnvironmentVariable is not available\n"); + return; + } + + /* Query the TestVar environment variable length */ + Status = pRtlQueryEnvironmentVariable(TestEnv, L"TestVar", 7, NULL, 0, &ReturnLength); + ok_ntstatus(Status, STATUS_BUFFER_TOO_SMALL); + ok_eq_size(ReturnLength, 8); + + /* Test a buffer size that is too small */ + memset(Buffer, 0xAB, sizeof(Buffer)); + Status = pRtlQueryEnvironmentVariable(TestEnv, L"TestVar", 7, Buffer, 2, &ReturnLength); + ok_ntstatus(Status, STATUS_BUFFER_TOO_SMALL); + ok_eq_size(ReturnLength, 8); + ok_eq_wchar(Buffer[0], UNICODE_NULL); + ok_eq_wchar(Buffer[1], 0xABAB); + + /* Test a buffer size that doesn't fit the terminating NULL char */ + memset(Buffer, 0xAB, sizeof(Buffer)); + Status = pRtlQueryEnvironmentVariable(TestEnv, L"TestVar", 7, Buffer, 7, &ReturnLength); + ok_ntstatus(Status, STATUS_BUFFER_TOO_SMALL); + ok_eq_size(ReturnLength, 8); + ok_eq_wchar(Buffer[0], UNICODE_NULL); + ok_eq_wchar(Buffer[1], 0xABAB); + + /* Test a buffer size that is just large enough */ + memset(Buffer, 0xAB, sizeof(Buffer)); + Status = pRtlQueryEnvironmentVariable(TestEnv, L"TestVar", 7, Buffer, 8, &ReturnLength); + ok_ntstatus(Status, STATUS_SUCCESS); + ok_eq_size(ReturnLength, 7); + ok_eq_wstr(Buffer, L"TestVal"); + ok_eq_wchar(Buffer[7], UNICODE_NULL); + ok_eq_wchar(Buffer[8], 0xABAB); + + /* Test a buffer size that is larger than needed */ + memset(Buffer, 0xAB, sizeof(Buffer)); + Status = pRtlQueryEnvironmentVariable(TestEnv, L"TestVar", 7, Buffer, ARRAYSIZE(Buffer), &ReturnLength); + ok_ntstatus(Status, STATUS_SUCCESS); + ok_eq_size(ReturnLength, 7); + ok_eq_wstr(Buffer, L"TestVal"); + ok_eq_wchar(Buffer[7], UNICODE_NULL); + ok_eq_wchar(Buffer[8], 0xABAB); + + /* Test a too big variable Length */ + memset(Buffer, 0xAB, sizeof(Buffer)); + Status = pRtlQueryEnvironmentVariable(TestEnv, L"TestVar", 8, Buffer, 8, &ReturnLength); + ok_ntstatus(Status, STATUS_VARIABLE_NOT_FOUND); + ok_eq_wchar(Buffer[0], 0xABAB); + + /* Query the EmptyVar environment variable */ + memset(Buffer, 0xAB, sizeof(Buffer)); + Status = pRtlQueryEnvironmentVariable(TestEnv, L"EmptyVar", 8, Buffer, ARRAYSIZE(Buffer), &ReturnLength); + ok_ntstatus(Status, STATUS_VARIABLE_NOT_FOUND); + ok_eq_wchar(Buffer[0], 0xABAB); + + /* Query the Bar environment variable */ + memset(Buffer, 0xAB, sizeof(Buffer)); + Status = pRtlQueryEnvironmentVariable(TestEnv, L"Bar", 3, Buffer, 8, &ReturnLength); + ok_ntstatus(Status, STATUS_SUCCESS); + ok_eq_wchar(Buffer[0], L'8'); + ok_eq_wchar(Buffer[1], UNICODE_NULL); + ok_eq_wchar(Buffer[2], 0xABAB); +} diff --git a/modules/rostests/apitests/ntdll/RtlQueryEnvironmentVariable_U.c b/modules/rostests/apitests/ntdll/RtlQueryEnvironmentVariable_U.c new file mode 100644 index 00000000000..974556ef3c9 --- /dev/null +++ b/modules/rostests/apitests/ntdll/RtlQueryEnvironmentVariable_U.c @@ -0,0 +1,128 @@ +/* + * PROJECT: ReactOS API tests + * LICENSE: MIT (https://spdx.org/licenses/MIT) + * PURPOSE: Tests for RtlQueryEnvironmentVariable_U + * COPYRIGHT: Copyright 2026 Timo Kreuzer + */ + +#include "precomp.h" + +START_TEST(RtlQueryEnvironmentVariable_U) +{ + static WCHAR TestEnvironment[] = L"TestVar=TestVal\0Foo=4\0EmptyVar\0Bar=8\0\0"; + WCHAR Buffer[32]; + UNICODE_STRING NameString, ValueString; + NTSTATUS Status; + + /* Query the TestVar environment variable length */ + RtlInitUnicodeString(&NameString, L"TestVar"); + RtlInitEmptyUnicodeString(&ValueString, NULL, 0); + Status = RtlQueryEnvironmentVariable_U(TestEnvironment, &NameString, &ValueString); + ok_ntstatus(Status, STATUS_BUFFER_TOO_SMALL); + ok_eq_pointer(ValueString.Buffer, NULL); + ok_eq_ulong(ValueString.Length, 7 * sizeof(WCHAR)); + ok_eq_ulong(ValueString.MaximumLength, 0); + + /* Test passing an empty sized buffer */ + memset(Buffer, 0xAB, sizeof(Buffer)); + RtlInitEmptyUnicodeString(&ValueString, Buffer, 0); + Status = RtlQueryEnvironmentVariable_U(TestEnvironment, &NameString, &ValueString); + ok_ntstatus(Status, STATUS_BUFFER_TOO_SMALL); + ok_eq_pointer(ValueString.Buffer, Buffer); + ok_eq_ulong(ValueString.Length, 7 * sizeof(WCHAR)); + ok_eq_ulong(ValueString.MaximumLength, 0); + ok_eq_wchar(Buffer[0], 0xABAB); + + /* Test a buffer size that is too small */ + memset(Buffer, 0xAB, sizeof(Buffer)); + RtlInitEmptyUnicodeString(&ValueString, Buffer, 2 * sizeof(WCHAR)); + Status = RtlQueryEnvironmentVariable_U(TestEnvironment, &NameString, &ValueString); + ok_ntstatus(Status, STATUS_BUFFER_TOO_SMALL); + ok_eq_pointer(ValueString.Buffer, Buffer); + ok_eq_ulong(ValueString.Length, 7 * sizeof(WCHAR)); + ok_eq_ulong(ValueString.MaximumLength, 2 * sizeof(WCHAR)); + ok(Buffer[0] == UNICODE_NULL || broken(/* Windows 2003 */ Buffer[0] == 0xABAB), "Buffer[0] = 0x%x\n", Buffer[0]); + ok_eq_wchar(Buffer[1], 0xABAB); + + /* Test a buffer size that doesn't fit the terminating NULL char */ + memset(Buffer, 0xAB, sizeof(Buffer)); + RtlInitEmptyUnicodeString(&ValueString, Buffer, 7 * sizeof(WCHAR)); + Status = RtlQueryEnvironmentVariable_U(TestEnvironment, &NameString, &ValueString); + ok(Status == STATUS_BUFFER_TOO_SMALL || broken(/* Windows 2003 */ Status == STATUS_SUCCESS), "Status = 0x%lx\n", Status); + ok_eq_pointer(ValueString.Buffer, Buffer); + ok_eq_ulong(ValueString.Length, 7 * sizeof(WCHAR)); + ok_eq_ulong(ValueString.MaximumLength, 7 * sizeof(WCHAR)); + if (Status == STATUS_BUFFER_TOO_SMALL) + { + ok_eq_wchar(Buffer[0], UNICODE_NULL); + ok_eq_wchar(Buffer[1], 0xABAB); + } + + /* Test a buffer size that is just large enough */ + memset(Buffer, 0xAB, sizeof(Buffer)); + RtlInitEmptyUnicodeString(&ValueString, Buffer, 8 * sizeof(WCHAR)); + Status = RtlQueryEnvironmentVariable_U(TestEnvironment, &NameString, &ValueString); + ok_ntstatus(Status, STATUS_SUCCESS); + ok_eq_pointer(ValueString.Buffer, Buffer); + ok_eq_ulong(ValueString.Length, 7 * sizeof(WCHAR)); + ok_eq_ulong(ValueString.MaximumLength, 8 * sizeof(WCHAR)); + ok_eq_wstr(Buffer, L"TestVal"); + ok_eq_wchar(Buffer[7], UNICODE_NULL); + ok_eq_wchar(Buffer[8], 0xABAB); + + /* Test a buffer size that is larger than needed */ + memset(Buffer, 0xAB, sizeof(Buffer)); + RtlInitEmptyUnicodeString(&ValueString, Buffer, sizeof(Buffer)); + Status = RtlQueryEnvironmentVariable_U(TestEnvironment, &NameString, &ValueString); + ok_ntstatus(Status, STATUS_SUCCESS); + ok_eq_pointer(ValueString.Buffer, Buffer); + ok_eq_ulong(ValueString.Length, 7 * sizeof(WCHAR)); + ok_eq_ulong(ValueString.MaximumLength, sizeof(Buffer)); + ok_eq_wstr(Buffer, L"TestVal"); + ok_eq_wchar(Buffer[7], UNICODE_NULL); + ok_eq_wchar(Buffer[8], 0xABAB); + + /* Test a too big variable Length */ + memset(Buffer, 0xAB, sizeof(Buffer)); + NameString.Length += sizeof(WCHAR); + RtlInitEmptyUnicodeString(&ValueString, Buffer, sizeof(Buffer)); + Status = RtlQueryEnvironmentVariable_U(TestEnvironment, &NameString, &ValueString); + ok_ntstatus(Status, STATUS_VARIABLE_NOT_FOUND); + ok_eq_wchar(Buffer[0], 0xABAB); + + /* Query the EmptyVar environment variable */ + memset(Buffer, 0xAB, sizeof(Buffer)); + RtlInitUnicodeString(&NameString, L"EmptyVar"); + RtlInitEmptyUnicodeString(&ValueString, Buffer, sizeof(Buffer)); + Status = RtlQueryEnvironmentVariable_U(TestEnvironment, &NameString, &ValueString); + ok_ntstatus(Status, STATUS_VARIABLE_NOT_FOUND); + ok_eq_pointer(ValueString.Buffer, Buffer); + ok_eq_ulong(ValueString.Length, 0); + ok_eq_ulong(ValueString.MaximumLength, sizeof(Buffer)); + ok_eq_wchar(Buffer[0], 0xABAB); + + /* Query the Bar environment variable */ + memset(Buffer, 0xAB, sizeof(Buffer)); + RtlInitUnicodeString(&NameString, L"Bar"); + RtlInitEmptyUnicodeString(&ValueString, Buffer, sizeof(Buffer)); + Status = RtlQueryEnvironmentVariable_U(TestEnvironment, &NameString, &ValueString); + ok_ntstatus(Status, STATUS_SUCCESS); + ok_eq_pointer(ValueString.Buffer, Buffer); + ok_eq_ulong(ValueString.Length, 1 * sizeof(WCHAR)); + ok_eq_ulong(ValueString.MaximumLength, sizeof(Buffer)); + ok_eq_wstr(Buffer, L"8"); + ok_eq_wchar(Buffer[1], UNICODE_NULL); + ok_eq_wchar(Buffer[2], 0xABAB); + + /* Test NULL Variable name */ + memset(Buffer, 0xAB, sizeof(Buffer)); + RtlInitEmptyUnicodeString(&NameString, NULL, 0); + RtlInitEmptyUnicodeString(&ValueString, Buffer, sizeof(Buffer)); + Status = RtlQueryEnvironmentVariable_U(TestEnvironment, &NameString, &ValueString); + ok_ntstatus(Status, STATUS_VARIABLE_NOT_FOUND); + ok_eq_pointer(ValueString.Buffer, Buffer); + ok_eq_ulong(ValueString.Length, 0); + ok_eq_ulong(ValueString.MaximumLength, sizeof(Buffer)); + ok_eq_wchar(Buffer[0], 0xABAB); + +} diff --git a/modules/rostests/apitests/ntdll/testlist.c b/modules/rostests/apitests/ntdll/testlist.c index 6584eaf0f9b..c404ac2aa27 100644 --- a/modules/rostests/apitests/ntdll/testlist.c +++ b/modules/rostests/apitests/ntdll/testlist.c @@ -117,6 +117,8 @@ extern void func_RtlMultipleAllocateHeap(void); extern void func_RtlNtPathNameToDosPathName(void); extern void func_RtlpApplyLengthFunction(void); extern void func_RtlpEnsureBufferSize(void); +extern void func_RtlQueryEnvironmentVariable(void); +extern void func_RtlQueryEnvironmentVariable_U(void); extern void func_RtlQueryTimeZoneInformation(void); extern void func_RtlReAllocateHeap(void); extern void func_RtlRemovePrivileges(void); @@ -250,6 +252,8 @@ const struct test winetest_testlist[] = { "RtlNtPathNameToDosPathName", func_RtlNtPathNameToDosPathName }, { "RtlpApplyLengthFunction", func_RtlpApplyLengthFunction }, { "RtlpEnsureBufferSize", func_RtlpEnsureBufferSize }, + { "RtlQueryEnvironmentVariable", func_RtlQueryEnvironmentVariable }, + { "RtlQueryEnvironmentVariable_U", func_RtlQueryEnvironmentVariable_U }, { "RtlQueryTimeZoneInformation", func_RtlQueryTimeZoneInformation }, { "RtlReAllocateHeap", func_RtlReAllocateHeap }, { "RtlRemovePrivileges", func_RtlRemovePrivileges },