diff --git a/CMakeLists.txt b/CMakeLists.txt index c475ce9..56283d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -221,6 +221,17 @@ if(BUILD_TESTING) ${CMAKE_CURRENT_SOURCE_DIR}/test/test_heap.c ${CMAKE_CURRENT_SOURCE_DIR}/test/test_assert.h) + add_custom_command( + OUTPUT ${WIBO_TEST_BIN_DIR}/test_actctx.exe + COMMAND ${WIBO_MINGW_CC} -Wall -Wextra -O2 + -I${CMAKE_CURRENT_SOURCE_DIR}/test + -o test_actctx.exe + ${CMAKE_CURRENT_SOURCE_DIR}/test/test_actctx.c + WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR} + DEPENDS + ${CMAKE_CURRENT_SOURCE_DIR}/test/test_actctx.c + ${CMAKE_CURRENT_SOURCE_DIR}/test/test_assert.h) + add_custom_command( OUTPUT ${WIBO_TEST_BIN_DIR}/test_overlapped_io.exe COMMAND ${WIBO_MINGW_CC} -Wall -Wextra -O2 @@ -310,6 +321,7 @@ if(BUILD_TESTING) ${WIBO_TEST_BIN_DIR}/test_resources.exe ${WIBO_TEST_BIN_DIR}/test_threading.exe ${WIBO_TEST_BIN_DIR}/test_heap.exe + ${WIBO_TEST_BIN_DIR}/test_actctx.exe ${WIBO_TEST_BIN_DIR}/test_overlapped_io.exe ${WIBO_TEST_BIN_DIR}/test_time.exe ${WIBO_TEST_BIN_DIR}/test_virtualalloc.exe @@ -358,6 +370,12 @@ if(BUILD_TESTING) WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR} DEPENDS wibo.build_fixtures) + add_test(NAME wibo.test_actctx + COMMAND $ ${WIBO_TEST_BIN_DIR}/test_actctx.exe) + set_tests_properties(wibo.test_actctx PROPERTIES + WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR} + DEPENDS wibo.build_fixtures) + add_test(NAME wibo.test_overlapped_io COMMAND $ ${WIBO_TEST_BIN_DIR}/test_overlapped_io.exe) set_tests_properties(wibo.test_overlapped_io PROPERTIES diff --git a/dll/kernel32.cpp b/dll/kernel32.cpp index 0ac702e..8063c08 100644 --- a/dll/kernel32.cpp +++ b/dll/kernel32.cpp @@ -246,6 +246,10 @@ void *resolveByName(const char *name) { return (void *)kernel32::GetCurrentDirectoryA; if (strcmp(name, "GetCurrentDirectoryW") == 0) return (void *)kernel32::GetCurrentDirectoryW; + if (strcmp(name, "GetSystemWindowsDirectoryA") == 0) + return (void *)kernel32::GetSystemWindowsDirectoryA; + if (strcmp(name, "GetSystemWindowsDirectoryW") == 0) + return (void *)kernel32::GetSystemWindowsDirectoryW; if (strcmp(name, "SetCurrentDirectoryA") == 0) return (void *)kernel32::SetCurrentDirectoryA; if (strcmp(name, "SetCurrentDirectoryW") == 0) @@ -264,6 +268,10 @@ void *resolveByName(const char *name) { return (void *)kernel32::DecodePointer; if (strcmp(name, "SetDllDirectoryA") == 0) return (void *)kernel32::SetDllDirectoryA; + if (strcmp(name, "FindActCtxSectionStringA") == 0) + return (void *)kernel32::FindActCtxSectionStringA; + if (strcmp(name, "FindActCtxSectionStringW") == 0) + return (void *)kernel32::FindActCtxSectionStringW; if (strcmp(name, "GetLongPathNameA") == 0) return (void *)kernel32::GetLongPathNameA; if (strcmp(name, "GetLongPathNameW") == 0) diff --git a/dll/kernel32/heapapi.cpp b/dll/kernel32/heapapi.cpp index 1f42110..ef74348 100644 --- a/dll/kernel32/heapapi.cpp +++ b/dll/kernel32/heapapi.cpp @@ -312,7 +312,7 @@ BOOL WIN_FUNC HeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem) { return FALSE; } if (!mi_heap_check_owned(record->heap, lpMem)) { - DEBUG_LOG("HeapFree: block %p not owned by heap %p\n", lpMem, record->heap); + DEBUG_LOG("-> INVALID_PARAMETER (not owned)\n"); wibo::lastError = ERROR_INVALID_PARAMETER; return FALSE; } diff --git a/dll/kernel32/winbase.cpp b/dll/kernel32/winbase.cpp index 8605b5a..2f82dad 100644 --- a/dll/kernel32/winbase.cpp +++ b/dll/kernel32/winbase.cpp @@ -13,11 +13,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -298,7 +300,82 @@ constexpr DWORD kComputerNameRequiredSize = kComputerNameLength + 1; constexpr const char kComputerNameAnsi[] = "COMPNAME"; const uint16_t kComputerNameWide[] = {u'C', u'O', u'M', u'P', u'N', u'A', u'M', u'E', 0}; +struct DllRedirectionEntry { + std::string nameLower; + ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION dllData; +}; + +struct ActivationContext { + std::vector dllRedirections; +}; + +ActivationContext g_builtinActCtx; + +ActivationContext *currentActivationContext() { + // TODO: hook into real activation context stack once we have it. + return &g_builtinActCtx; +} + } // namespace +void ensureDefaultActivationContext() { + static std::once_flag initFlag; + std::call_once(initFlag, [] { + ActivationContext *ctx = currentActivationContext(); + auto addDll = [ctx](const std::string &name) { + DllRedirectionEntry entry; + entry.nameLower = stringToLower(name); + entry.dllData.Size = sizeof(entry.dllData); + entry.dllData.Flags = ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_OMITS_ASSEMBLY_ROOT; + entry.dllData.TotalPathLength = 0; + entry.dllData.PathSegmentCount = 0; + entry.dllData.PathSegmentOffset = 0; + ctx->dllRedirections.emplace_back(std::move(entry)); + }; + addDll("msvcr80.dll"); + addDll("msvcp80.dll"); + addDll("mfc80.dll"); + addDll("mfc80u.dll"); + addDll("msvcrt.dll"); + }); +} + +constexpr const char kVc80ManifestName[] = "Microsoft.VC80.CRT.manifest"; + +void ensureVc80ManifestOnDisk(const DllRedirectionEntry &entry) { + static std::once_flag manifestOnce; + if (entry.nameLower != "msvcr80.dll") { + return; + } + std::call_once(manifestOnce, [] { + wibo::ModuleInfo *module = wibo::findLoadedModule("msvcr80.dll"); + if (!module || module->resolvedPath.empty()) { + DEBUG_LOG("VC80 manifest: module not yet loaded, skipping creation\n"); + return; + } + std::filesystem::path manifestPath = module->resolvedPath.parent_path() / kVc80ManifestName; + std::error_code ec; + if (std::filesystem::exists(manifestPath, ec)) { + return; + } + constexpr const char kManifestContents[] = + "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + "\n"; + std::ofstream out(manifestPath, std::ios::binary); + if (!out) { + DEBUG_LOG("VC80 manifest: failed to create %s\n", manifestPath.string().c_str()); + return; + } + out.write(kManifestContents, sizeof(kManifestContents) - 1); + if (!out) { + DEBUG_LOG("VC80 manifest: write error for %s\n", manifestPath.string().c_str()); + } + }); +} namespace kernel32 { @@ -553,6 +630,90 @@ BOOL WIN_FUNC SetDllDirectoryA(LPCSTR lpPathName) { return TRUE; } +BOOL WIN_FUNC FindActCtxSectionStringA(DWORD dwFlags, const GUID *lpExtensionGuid, ULONG ulSectionId, + LPCSTR lpStringToFind, PACTCTX_SECTION_KEYED_DATA ReturnedData) { + DEBUG_LOG("FindActCtxSectionStringA(%#x, %p, %u, %s, %p)\n", dwFlags, lpExtensionGuid, ulSectionId, + lpStringToFind ? lpStringToFind : "", ReturnedData); + std::vector wideStorage; + if (lpStringToFind) { + size_t length = strlen(lpStringToFind); + wideStorage.resize(length + 1); + for (size_t i = 0; i <= length; ++i) { + wideStorage[i] = static_cast(lpStringToFind[i]); + } + } + const uint16_t *widePtr = wideStorage.empty() ? nullptr : wideStorage.data(); + return FindActCtxSectionStringW(dwFlags, lpExtensionGuid, ulSectionId, + reinterpret_cast(widePtr), ReturnedData); +} + +BOOL WIN_FUNC FindActCtxSectionStringW(DWORD dwFlags, const GUID *lpExtensionGuid, ULONG ulSectionId, + LPCWSTR lpStringToFind, PACTCTX_SECTION_KEYED_DATA ReturnedData) { + std::string lookup = lpStringToFind ? wideStringToString(lpStringToFind) : std::string(); + DEBUG_LOG("FindActCtxSectionStringW(%#x, %p, %u, %s, %p)\n", dwFlags, lpExtensionGuid, ulSectionId, + lookup.c_str(), ReturnedData); + + if (lpExtensionGuid) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + + if (!ReturnedData) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + + if (dwFlags & ~FIND_ACTCTX_SECTION_KEY_RETURN_HACTCTX) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + + ULONG originalSize = ReturnedData->cbSize; + if (originalSize < sizeof(ACTCTX_SECTION_KEYED_DATA)) { + wibo::lastError = ERROR_INSUFFICIENT_BUFFER; + return FALSE; + } + + ensureDefaultActivationContext(); + ActivationContext *ctx = currentActivationContext(); + const DllRedirectionEntry *matchedEntry = nullptr; + if (ulSectionId == ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION && !lookup.empty()) { + std::string lowerLookup = stringToLower(lookup); + for (const auto &entry : ctx->dllRedirections) { + if (entry.nameLower == lowerLookup) { + matchedEntry = &entry; + break; + } + } + } + + size_t zeroSize = std::min(static_cast(ReturnedData->cbSize), sizeof(*ReturnedData)); + std::memset(ReturnedData, 0, zeroSize); + ReturnedData->cbSize = originalSize; + ReturnedData->ulDataFormatVersion = 1; + ReturnedData->ulFlags = ACTCTX_SECTION_KEYED_DATA_FLAG_FOUND_IN_ACTCTX; + if (dwFlags & FIND_ACTCTX_SECTION_KEY_RETURN_HACTCTX) { + ReturnedData->hActCtx = reinterpret_cast(&g_builtinActCtx); + } + + if (!matchedEntry) { + wibo::lastError = ERROR_SXS_KEY_NOT_FOUND; + return FALSE; + } + + ensureVc80ManifestOnDisk(*matchedEntry); + + ReturnedData->lpData = const_cast(&matchedEntry->dllData); + ReturnedData->ulLength = matchedEntry->dllData.Size; + ReturnedData->lpSectionBase = const_cast(&matchedEntry->dllData); + ReturnedData->ulSectionTotalLength = matchedEntry->dllData.Size; + ReturnedData->ulAssemblyRosterIndex = 1; + ReturnedData->AssemblyMetadata = {}; + + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + void tryMarkExecutable(void *mem) { if (!mem) { return; @@ -824,6 +985,27 @@ UINT WIN_FUNC GetWindowsDirectoryA(LPSTR lpBuffer, UINT uSize) { return static_cast(len); } +UINT WIN_FUNC GetSystemWindowsDirectoryA(LPSTR lpBuffer, UINT uSize) { + DEBUG_LOG("GetSystemWindowsDirectoryA(%p, %u)\n", lpBuffer, uSize); + return GetWindowsDirectoryA(lpBuffer, uSize); +} + +UINT WIN_FUNC GetSystemWindowsDirectoryW(LPWSTR lpBuffer, UINT uSize) { + DEBUG_LOG("GetSystemWindowsDirectoryW(%p, %u)\n", lpBuffer, uSize); + if (!lpBuffer) { + return 0; + } + + const char *windowsDir = "C:\\Windows"; + auto wide = stringToWideString(windowsDir); + UINT length = static_cast(wide.size() - 1); + if (uSize < length + 1) { + return length + 1; + } + std::memcpy(lpBuffer, wide.data(), (length + 1) * sizeof(uint16_t)); + return length; +} + DWORD WIN_FUNC GetCurrentDirectoryA(DWORD nBufferLength, LPSTR lpBuffer) { HOST_CONTEXT_GUARD(); DEBUG_LOG("GetCurrentDirectoryA(%u, %p)\n", nBufferLength, lpBuffer); diff --git a/dll/kernel32/winbase.h b/dll/kernel32/winbase.h index d761661..b93a168 100644 --- a/dll/kernel32/winbase.h +++ b/dll/kernel32/winbase.h @@ -4,6 +4,61 @@ #include +struct GUID; + +struct ACTCTX_SECTION_KEYED_DATA_ASSEMBLY_METADATA { + PVOID lpInformation; + PVOID lpSectionBase; + ULONG ulSectionLength; + PVOID lpSectionGlobalData; + ULONG ulSectionGlobalDataLength; +}; + +struct ACTCTX_SECTION_KEYED_DATA { + ULONG cbSize; + ULONG ulDataFormatVersion; + PVOID lpData; + ULONG ulLength; + PVOID lpSectionGlobalData; + ULONG ulSectionGlobalDataLength; + PVOID lpSectionBase; + ULONG ulSectionTotalLength; + HANDLE hActCtx; + ULONG ulAssemblyRosterIndex; + ULONG ulFlags; + ACTCTX_SECTION_KEYED_DATA_ASSEMBLY_METADATA AssemblyMetadata; +}; + +using PACTCTX_SECTION_KEYED_DATA = ACTCTX_SECTION_KEYED_DATA *; +using PCACTCTX_SECTION_KEYED_DATA = const ACTCTX_SECTION_KEYED_DATA *; + +constexpr DWORD FIND_ACTCTX_SECTION_KEY_RETURN_HACTCTX = 0x00000001; + +constexpr ULONG ACTIVATION_CONTEXT_SECTION_ASSEMBLY_INFORMATION = 1; +constexpr ULONG ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION = 2; +constexpr ULONG ACTIVATION_CONTEXT_SECTION_WINDOW_CLASS_REDIRECTION = 3; +constexpr ULONG ACTIVATION_CONTEXT_SECTION_COM_PROGID_REDIRECTION = 7; + +constexpr ULONG ACTCTX_SECTION_KEYED_DATA_FLAG_FOUND_IN_ACTCTX = 0x00000001; + +constexpr ULONG ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_INCLUDES_BASE_NAME = 1; +constexpr ULONG ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_OMITS_ASSEMBLY_ROOT = 2; +constexpr ULONG ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_EXPAND = 4; +constexpr ULONG ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_SYSTEM_DEFAULT_REDIRECTED_SYSTEM32_DLL = 8; + +struct ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION { + ULONG Size; + ULONG Flags; + ULONG TotalPathLength; + ULONG PathSegmentCount; + ULONG PathSegmentOffset; +}; + +struct ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_SEGMENT { + ULONG Length; + ULONG Offset; +}; + namespace kernel32 { BOOL WIN_FUNC IsBadReadPtr(LPCVOID lp, UINT_PTR ucb); @@ -21,6 +76,11 @@ PVOID WIN_FUNC EncodePointer(PVOID Ptr); PVOID WIN_FUNC DecodePointer(PVOID Ptr); BOOL WIN_FUNC SetDllDirectoryA(LPCSTR lpPathName); +BOOL WIN_FUNC FindActCtxSectionStringA(DWORD dwFlags, const GUID *lpExtensionGuid, ULONG ulSectionId, + LPCSTR lpStringToFind, PACTCTX_SECTION_KEYED_DATA ReturnedData); +BOOL WIN_FUNC FindActCtxSectionStringW(DWORD dwFlags, const GUID *lpExtensionGuid, ULONG ulSectionId, + LPCWSTR lpStringToFind, PACTCTX_SECTION_KEYED_DATA ReturnedData); + BOOL WIN_FUNC GetComputerNameA(LPSTR lpBuffer, LPDWORD nSize); BOOL WIN_FUNC GetComputerNameW(LPWSTR lpBuffer, LPDWORD nSize); @@ -43,6 +103,8 @@ UINT WIN_FUNC GetSystemDirectoryW(LPWSTR lpBuffer, UINT uSize); UINT WIN_FUNC GetSystemWow64DirectoryA(LPSTR lpBuffer, UINT uSize); UINT WIN_FUNC GetSystemWow64DirectoryW(LPWSTR lpBuffer, UINT uSize); UINT WIN_FUNC GetWindowsDirectoryA(LPSTR lpBuffer, UINT uSize); +UINT WIN_FUNC GetSystemWindowsDirectoryA(LPSTR lpBuffer, UINT uSize); +UINT WIN_FUNC GetSystemWindowsDirectoryW(LPWSTR lpBuffer, UINT uSize); DWORD WIN_FUNC GetCurrentDirectoryA(DWORD nBufferLength, LPSTR lpBuffer); DWORD WIN_FUNC GetCurrentDirectoryW(DWORD nBufferLength, LPWSTR lpBuffer); int WIN_FUNC SetCurrentDirectoryA(LPCSTR lpPathName); diff --git a/dll/user32.cpp b/dll/user32.cpp index f31bbb7..6fa80d8 100644 --- a/dll/user32.cpp +++ b/dll/user32.cpp @@ -5,6 +5,14 @@ namespace user32 { constexpr uint32_t RT_STRING_ID = 6; constexpr uintptr_t kDefaultKeyboardLayout = 0x04090409; + constexpr int UOI_FLAGS = 1; + constexpr DWORD WSF_VISIBLE = 0x0001; + + struct USEROBJECTFLAGS { + BOOL fInherit; + BOOL fReserved; + DWORD dwFlags; + }; int WIN_FUNC LoadStringA(void* hInstance, unsigned int uID, char* lpBuffer, int cchBufferMax) { HOST_CONTEXT_GUARD(); @@ -122,6 +130,48 @@ namespace user32 { wibo::lastError = ERROR_SUCCESS; return reinterpret_cast(kDefaultKeyboardLayout); } + + HWINSTA WIN_FUNC GetProcessWindowStation() { + DEBUG_LOG("GetProcessWindowStation()\n"); + static int kWindowStationStub; + wibo::lastError = ERROR_SUCCESS; + return reinterpret_cast(&kWindowStationStub); + } + + BOOL WIN_FUNC GetUserObjectInformationA(HANDLE hObj, int nIndex, PVOID pvInfo, DWORD nLength, + LPDWORD lpnLengthNeeded) { + DEBUG_LOG("GetUserObjectInformationA(%p, %d, %p, %u, %p)\n", hObj, nIndex, pvInfo, nLength, + lpnLengthNeeded); + (void)hObj; + + if (lpnLengthNeeded) { + *lpnLengthNeeded = sizeof(USEROBJECTFLAGS); + } + + if (nIndex != UOI_FLAGS) { + wibo::lastError = ERROR_CALL_NOT_IMPLEMENTED; + return FALSE; + } + + if (!pvInfo || nLength < sizeof(USEROBJECTFLAGS)) { + wibo::lastError = ERROR_INSUFFICIENT_BUFFER; + return FALSE; + } + + auto *flags = reinterpret_cast(pvInfo); + flags->fInherit = FALSE; + flags->fReserved = FALSE; + flags->dwFlags = WSF_VISIBLE; + + wibo::lastError = ERROR_SUCCESS; + return TRUE; + } + + HWND WIN_FUNC GetActiveWindow() { + DEBUG_LOG("GetActiveWindow()\n"); + wibo::lastError = ERROR_SUCCESS; + return nullptr; + } } @@ -130,6 +180,9 @@ static void *resolveByName(const char *name) { if (strcmp(name, "LoadStringW") == 0) return (void *) user32::LoadStringW; if (strcmp(name, "MessageBoxA") == 0) return (void *) user32::MessageBoxA; if (strcmp(name, "GetKeyboardLayout") == 0) return (void *) user32::GetKeyboardLayout; + if (strcmp(name, "GetProcessWindowStation") == 0) return (void *) user32::GetProcessWindowStation; + if (strcmp(name, "GetUserObjectInformationA") == 0) return (void *) user32::GetUserObjectInformationA; + if (strcmp(name, "GetActiveWindow") == 0) return (void *) user32::GetActiveWindow; return nullptr; } diff --git a/src/common.h b/src/common.h index c479c71..c06dcdc 100644 --- a/src/common.h +++ b/src/common.h @@ -98,6 +98,8 @@ using REGSAM = DWORD; using LSTATUS = LONG; using LCID = DWORD; using LCTYPE = DWORD; +using HWINSTA = HANDLE; +using HWND = HANDLE; constexpr BOOL TRUE = 1; constexpr BOOL FALSE = 0; diff --git a/src/errors.h b/src/errors.h index 16bd9f6..acdc303 100644 --- a/src/errors.h +++ b/src/errors.h @@ -39,6 +39,7 @@ #define ERROR_NOT_OWNER 288 #define ERROR_TOO_MANY_POSTS 298 #define ERROR_SEM_TIMEOUT 121 +#define ERROR_SXS_KEY_NOT_FOUND 14007 #define INVALID_SET_FILE_POINTER ((DWORD) - 1) #define INVALID_HANDLE_VALUE ((HANDLE) - 1) diff --git a/test/test_actctx.c b/test/test_actctx.c new file mode 100644 index 0000000..308cf3b --- /dev/null +++ b/test/test_actctx.c @@ -0,0 +1,110 @@ +#include + +#include "test_assert.h" + +#ifndef ACTCTX_SECTION_KEYED_DATA_FLAG_FOUND_IN_ACTCTX +#define ACTCTX_SECTION_KEYED_DATA_FLAG_FOUND_IN_ACTCTX 0x00000001 +#endif + +#ifndef ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION +typedef struct _ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION { + ULONG Size; + ULONG Flags; + ULONG TotalPathLength; + ULONG PathSegmentCount; + ULONG PathSegmentOffset; +} ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION; +#endif + +static BOOL isRunningUnderWine(void) { + HMODULE hNtdll = GetModuleHandleW(L"ntdll.dll"); + return hNtdll != NULL && GetProcAddress(hNtdll, "wine_get_version") != NULL; +} + +static void check_success_w(void) { + ACTCTX_SECTION_KEYED_DATA data = { 0 }; + data.cbSize = sizeof(data); + SetLastError(0); + BOOL ok = FindActCtxSectionStringW(0, NULL, ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION, + L"msvcr80.dll", &data); + TEST_CHECK(ok); + TEST_CHECK_EQ(ERROR_SUCCESS, GetLastError()); + TEST_CHECK_EQ(1u, data.ulDataFormatVersion); + TEST_CHECK((data.ulFlags & ACTCTX_SECTION_KEYED_DATA_FLAG_FOUND_IN_ACTCTX) != 0); + TEST_CHECK(data.lpData != NULL); + TEST_CHECK(data.ulLength >= sizeof(ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION)); + TEST_CHECK(data.hActCtx == NULL); + TEST_CHECK_EQ(1u, data.ulAssemblyRosterIndex); +} + +static void check_success_a(void) { + ACTCTX_SECTION_KEYED_DATA data = { 0 }; + data.cbSize = sizeof(data); + SetLastError(0); + BOOL ok = FindActCtxSectionStringA(FIND_ACTCTX_SECTION_KEY_RETURN_HACTCTX, NULL, + ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION, + "msvcp80.dll", &data); + TEST_CHECK(ok); + TEST_CHECK_EQ(ERROR_SUCCESS, GetLastError()); + TEST_CHECK((data.ulFlags & ACTCTX_SECTION_KEYED_DATA_FLAG_FOUND_IN_ACTCTX) != 0); + TEST_CHECK(data.lpData != NULL); + TEST_CHECK(data.hActCtx != NULL); + TEST_CHECK_EQ(1u, data.ulAssemblyRosterIndex); +} + +static void check_invalid_parameters(void) { + ACTCTX_SECTION_KEYED_DATA data = { 0 }; + data.cbSize = sizeof(data); + GUID fakeGuid = {0}; + SetLastError(0); + BOOL ok = FindActCtxSectionStringW(0, &fakeGuid, ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION, + L"msvcr80.dll", &data); + TEST_CHECK(!ok); + TEST_CHECK_EQ(ERROR_INVALID_PARAMETER, GetLastError()); + + ACTCTX_SECTION_KEYED_DATA sized = { 0 }; + sized.cbSize = sizeof(data) - 4; + SetLastError(0); + ok = FindActCtxSectionStringW(0, NULL, ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION, + L"msvcr80.dll", &sized); + TEST_CHECK(!ok); + TEST_CHECK_EQ(ERROR_INSUFFICIENT_BUFFER, GetLastError()); + + ACTCTX_SECTION_KEYED_DATA flags = { 0 }; + flags.cbSize = sizeof(flags); + SetLastError(0); + ok = FindActCtxSectionStringW(0x2, NULL, ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION, + L"msvcr80.dll", &flags); + TEST_CHECK(!ok); + TEST_CHECK_EQ(ERROR_INVALID_PARAMETER, GetLastError()); +} + +static void check_missing_entries(void) { + ACTCTX_SECTION_KEYED_DATA data = { 0 }; + data.cbSize = sizeof(data); + SetLastError(0); + BOOL ok = FindActCtxSectionStringW(0, NULL, ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION, + L"totally_missing.dll", &data); + TEST_CHECK(!ok); + TEST_CHECK_EQ(ERROR_SXS_KEY_NOT_FOUND, GetLastError()); + + ACTCTX_SECTION_KEYED_DATA wrongSection = { 0 }; + wrongSection.cbSize = sizeof(wrongSection); + SetLastError(0); + ok = FindActCtxSectionStringW(0, NULL, ACTIVATION_CONTEXT_SECTION_ASSEMBLY_INFORMATION, + L"msvcr80.dll", &wrongSection); + TEST_CHECK(!ok); + TEST_CHECK_EQ(ERROR_SXS_KEY_NOT_FOUND, GetLastError()); +} + +int main(void) { + if (isRunningUnderWine()) { + printf("test_actctx: skipping under wine\n"); + return 0; + } + check_success_w(); + check_success_a(); + check_invalid_parameters(); + check_missing_entries(); + return 0; +}