/* * Unit test suite for virtual substituted drive functions. * * Copyright 2017 Giannis Adamopoulos * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include "precomp.h" #define STRSECTION_MAGIC 0x64487353 /* dHsS */ struct strsection_header { DWORD magic; ULONG size; DWORD unk1[3]; ULONG count; ULONG index_offset; DWORD unk2[2]; ULONG global_offset; ULONG global_len; }; struct wndclass_redirect_data { ULONG size; DWORD res; ULONG name_len; ULONG name_offset; /* versioned name offset */ ULONG module_len; ULONG module_offset; /* container name offset to the section base */ }; struct dllredirect_data { ULONG size; ULONG unk; DWORD res[3]; }; #include struct assemply_data { ULONG size; DWORD ulFlags; DWORD ulEncodedAssemblyIdentityLength; DWORD ulEncodedAssemblyIdentityOffset; /* offset to the section base */ DWORD ulManifestPathType; DWORD ulManifestPathLength; DWORD ulManifestPathOffset; /* offset to the section base */ LARGE_INTEGER liManifestLastWriteTime; DWORD unk3[11]; DWORD ulAssemblyDirectoryNameLength; DWORD ulAssemblyDirectoryNameOffset; /* offset to the section base */ DWORD unk4[3]; /* In win10 there are two more fields */ }; #include HANDLE _CreateActCtxFromFile(LPCWSTR FileName, int line) { ACTCTXW ActCtx = {sizeof(ACTCTX)}; HANDLE h; WCHAR buffer[MAX_PATH] , *separator; ok (GetModuleFileNameW(NULL, buffer, MAX_PATH), "GetModuleFileName failed\n"); separator = wcsrchr(buffer, L'\\'); if (separator) wcscpy(separator + 1, FileName); ActCtx.lpSource = buffer; SetLastError(0xdeaddead); h = CreateActCtxW(&ActCtx); ok_(__FILE__, line)(h != INVALID_HANDLE_VALUE, "CreateActCtx failed for %S\n", FileName); // In win10 last error is unchanged and in win2k3 it is ERROR_BAD_EXE_FORMAT ok_(__FILE__, line)(GetLastError() == ERROR_BAD_EXE_FORMAT || GetLastError() == 0xdeaddead, "Wrong last error %lu\n", GetLastError()); return h; } VOID _ActivateCtx(HANDLE h, ULONG_PTR *cookie, int line) { BOOL res; SetLastError(0xdeaddead); res = ActivateActCtx(h, cookie); ok_(__FILE__, line)(res == TRUE, "ActivateActCtx failed\n"); ok_(__FILE__, line)(GetLastError() == 0xdeaddead, "Wrong last error. Expected %lu, got %lu\n", (DWORD)(0xdeaddead), GetLastError()); } VOID _DeactivateCtx(ULONG_PTR cookie, int line) { BOOL res; SetLastError(0xdeaddead); res = DeactivateActCtx(0, cookie); ok_(__FILE__, line)(res == TRUE, "DeactivateActCtx failed\n"); ok_(__FILE__, line)(GetLastError() == 0xdeaddead, "Wrong last error. Expected %lu, got %lu\n", (DWORD)(0xdeaddead), GetLastError()); } void TestClassRedirection(HANDLE h, LPCWSTR ClassToTest, LPCWSTR ExpectedClassPart, LPCWSTR ExpectedModule, ULONG ExpectedClassCount) { ACTCTX_SECTION_KEYED_DATA KeyedData = { 0 }; BOOL res; struct strsection_header *header; struct wndclass_redirect_data *classData; LPCWSTR VersionedClass, ClassLib; int data_lenght; SetLastError(0xdeaddead); KeyedData.cbSize = sizeof(KeyedData); res = FindActCtxSectionStringW(FIND_ACTCTX_SECTION_KEY_RETURN_HACTCTX, NULL, ACTIVATION_CONTEXT_SECTION_WINDOW_CLASS_REDIRECTION, ClassToTest, &KeyedData); ok(res == TRUE, "FindActCtxSectionString failed\n"); ok(GetLastError() == 0xdeaddead, "Wrong last error. Expected %lu, got %lu\n", (DWORD)(0xdeaddead), GetLastError()); ok(KeyedData.ulDataFormatVersion == 1, "Wrong format version: %lu\n", KeyedData.ulDataFormatVersion); ok(KeyedData.hActCtx == h, "Wrong handle\n"); ok(KeyedData.lpSectionBase != NULL, "Expected non null lpSectionBase\n"); ok(KeyedData.lpData != NULL, "Expected non null lpData\n"); header = (struct strsection_header*)KeyedData.lpSectionBase; classData = (struct wndclass_redirect_data*)KeyedData.lpData; if(res == FALSE || KeyedData.ulDataFormatVersion != 1 || header == NULL || classData == NULL) { skip("Can't read data for class. Skipping\n"); } else { ok(header->magic == STRSECTION_MAGIC, "%lu\n", header->magic ); ok(header->size == sizeof(*header), "Got %lu instead of %d\n", header->size, sizeof(*header)); ok(header->count == ExpectedClassCount, "Expected %lu classes, got %lu\n", ExpectedClassCount, header->count ); VersionedClass = (WCHAR*)((BYTE*)classData + classData->name_offset); ClassLib = (WCHAR*)((BYTE*)header + classData->module_offset); data_lenght = classData->size + classData->name_len + classData->module_len + 2*sizeof(WCHAR); ok(KeyedData.ulLength == data_lenght, "Got lenght %lu instead of %d\n", KeyedData.ulLength, data_lenght); ok(classData->size == sizeof(*classData), "Got %lu instead of %d\n", classData->size, sizeof(*classData)); ok(classData->res == 0, "Got res %lu\n", classData->res); ok(classData->module_len == wcslen(ExpectedModule) * 2, "Got name len %lu, expected %d\n", classData->module_len, wcslen(ExpectedModule) *2); ok(wcscmp(ClassLib, ExpectedModule) == 0, "Got %S, expected %S\n", ClassLib, ExpectedModule); /* compare only if VersionedClass starts with ExpectedClassPart */ ok(memcmp(VersionedClass, ExpectedClassPart, sizeof(WCHAR) * wcslen(ExpectedClassPart)) == 0, "Expected %S to start with %S\n", VersionedClass, ExpectedClassPart); } } VOID TestLibDependency(HANDLE h) { ACTCTX_SECTION_KEYED_DATA KeyedData = { 0 }; BOOL res; struct strsection_header *SectionHeader; struct dllredirect_data *redirData; struct assemply_data *assemplyData; SetLastError(0xdeaddead); KeyedData.cbSize = sizeof(KeyedData); res = FindActCtxSectionStringW(FIND_ACTCTX_SECTION_KEY_RETURN_HACTCTX, NULL, ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION, L"dep1.dll", &KeyedData); ok(res == TRUE, "FindActCtxSectionString failed\n"); ok(GetLastError() == 0xdeaddead, "Wrong last error. Expected %lu, got %lu\n", (DWORD)(0xdeaddead), GetLastError()); ok(KeyedData.ulDataFormatVersion == 1, "Wrong format version: %lu", KeyedData.ulDataFormatVersion); ok(KeyedData.hActCtx == h, "Wrong handle\n"); ok(KeyedData.lpSectionBase != NULL, "Expected non null lpSectionBase\n"); ok(KeyedData.lpData != NULL, "Expected non null lpData\n"); SectionHeader = (struct strsection_header*)KeyedData.lpSectionBase; redirData = (struct dllredirect_data *)KeyedData.lpData; if(res == FALSE || KeyedData.ulDataFormatVersion != 1 || SectionHeader == NULL || redirData == NULL) { skip("Can't read data for dep1.dll. Skipping\n"); } else { ok(SectionHeader->magic == STRSECTION_MAGIC, "%lu\n", SectionHeader->magic ); ok(SectionHeader->size == sizeof(*SectionHeader), "Got %lu instead of %d\n", SectionHeader->size, sizeof(*SectionHeader)); ok(SectionHeader->count == 2, "%lu\n", SectionHeader->count ); /* 2 dlls? */ ok(redirData->size == sizeof(*redirData), "Got %lu instead of %d\n", redirData->size, sizeof(*redirData)); } SetLastError(0xdeaddead); KeyedData.cbSize = sizeof(KeyedData); res = FindActCtxSectionStringW(FIND_ACTCTX_SECTION_KEY_RETURN_HACTCTX, NULL, ACTIVATION_CONTEXT_SECTION_ASSEMBLY_INFORMATION, L"dep1", &KeyedData); ok(res == TRUE, "FindActCtxSectionString failed\n"); ok(GetLastError() == 0xdeaddead, "Wrong last error. Expected %lu, got %lu\n", (DWORD)(0xdeaddead), GetLastError()); ok(KeyedData.ulDataFormatVersion == 1, "Wrong format version: %lu", KeyedData.ulDataFormatVersion); ok(KeyedData.hActCtx == h, "Wrong handle\n"); ok(KeyedData.lpSectionBase != NULL, "Expected non null lpSectionBase\n"); ok(KeyedData.lpData != NULL, "Expected non null lpData\n"); SectionHeader = (struct strsection_header*)KeyedData.lpSectionBase; assemplyData = (struct assemply_data*)KeyedData.lpData;; if(res == FALSE || KeyedData.ulDataFormatVersion != 1 || SectionHeader == NULL || assemplyData == NULL) { skip("Can't read data for dep1. Skipping\n"); } else { LPCWSTR AssemblyIdentity, ManifestPath, AssemblyDirectory; int data_lenght; DWORD buffer[256]; PACTIVATION_CONTEXT_ASSEMBLY_DETAILED_INFORMATION details = (PACTIVATION_CONTEXT_ASSEMBLY_DETAILED_INFORMATION)buffer; ok(SectionHeader->magic == STRSECTION_MAGIC, "%lu\n", SectionHeader->magic ); ok(SectionHeader->size == sizeof(*SectionHeader), "Got %lu instead of %d\n", SectionHeader->size, sizeof(*SectionHeader)); ok(SectionHeader->count == 2, "%lu\n", SectionHeader->count ); /* 2 dlls? */ data_lenght = assemplyData->size + assemplyData->ulEncodedAssemblyIdentityLength + assemplyData->ulManifestPathLength + assemplyData->ulAssemblyDirectoryNameLength + 2 * sizeof(WCHAR); ok(assemplyData->size == sizeof(*assemplyData), "Got %lu instead of %d\n", assemplyData->size, sizeof(*assemplyData)); ok(KeyedData.ulLength == data_lenght, "Got lenght %lu instead of %d\n", KeyedData.ulLength, data_lenght); AssemblyIdentity = (WCHAR*)((BYTE*)SectionHeader + assemplyData->ulEncodedAssemblyIdentityOffset); ManifestPath = (WCHAR*)((BYTE*)SectionHeader + assemplyData->ulManifestPathOffset); AssemblyDirectory = (WCHAR*)((BYTE*)SectionHeader + assemplyData->ulAssemblyDirectoryNameOffset); /* Use AssemblyDetailedInformationInActivationContext so as to infer the contents of assemplyData */ res = QueryActCtxW(0, h, &KeyedData.ulAssemblyRosterIndex, AssemblyDetailedInformationInActivationContext, &buffer, sizeof(buffer), NULL); ok(res == TRUE, "QueryActCtxW failed\n"); ok(assemplyData->ulFlags == details->ulFlags, "\n"); ok(assemplyData->ulEncodedAssemblyIdentityLength == details->ulEncodedAssemblyIdentityLength, "\n"); ok(assemplyData->ulManifestPathType == details->ulManifestPathType, "\n"); ok(assemplyData->ulManifestPathLength == details->ulManifestPathLength, "\n"); ok(assemplyData->ulAssemblyDirectoryNameLength == details->ulAssemblyDirectoryNameLength, "\n"); ok(assemplyData->liManifestLastWriteTime.QuadPart == details->liManifestLastWriteTime.QuadPart, "\n"); ok(wcscmp(ManifestPath, details->lpAssemblyManifestPath) == 0, "Expected path %S, got %S\n", details->lpAssemblyManifestPath, ManifestPath); ok(wcscmp(AssemblyDirectory, details->lpAssemblyDirectoryName) == 0, "Expected path %S, got %S\n", details->lpAssemblyManifestPath, ManifestPath); /* It looks like that AssemblyIdentity isn't null terminated */ ok(memcmp(AssemblyIdentity, details->lpAssemblyEncodedAssemblyIdentity, assemplyData->ulEncodedAssemblyIdentityLength) == 0, "Got wrong AssemblyIdentity\n"); } } START_TEST(FindActCtxSectionStringW) { HANDLE h, h2; ULONG_PTR cookie, cookie2; /*First run the redirection tests without using our own actctx */ TestClassRedirection(NULL, L"Button", L"Button", L"comctl32.dll", 27); /* Something activates an activation context that mentions comctl32 but comctl32 is not loaded */ ok( GetModuleHandleW(L"comctl32.dll") == NULL, "Expected comctl32 not to be loaded\n"); ok( GetModuleHandleW(L"user32.dll") == NULL, "Expected user32 not to be loaded\n"); /* Class redirection tests */ h = _CreateActCtxFromFile(L"classtest.manifest", __LINE__); if (h != INVALID_HANDLE_VALUE) { _ActivateCtx(h, &cookie, __LINE__); TestClassRedirection(h, L"Button", L"2.2.2.2!Button", L"testlib.dll", 5); _ActivateCtx(NULL, &cookie2, __LINE__); TestClassRedirection(NULL, L"Button", L"Button", L"comctl32.dll", 27); _DeactivateCtx(cookie2, __LINE__); _DeactivateCtx(cookie, __LINE__); } else { skip("Failed to create context for classtest.manifest\n"); } /* Class redirection tests with multiple contexts in the activation stack */ h2 = _CreateActCtxFromFile(L"classtest2.manifest", __LINE__); if (h != INVALID_HANDLE_VALUE && h2 != INVALID_HANDLE_VALUE) { _ActivateCtx(h, &cookie, __LINE__); _ActivateCtx(h2, &cookie2, __LINE__); TestClassRedirection(NULL, L"Button", L"Button", L"comctl32.dll", 27); TestClassRedirection(h2, L"MyClass", L"1.1.1.1!MyClass", L"testlib.dll", 5); _DeactivateCtx(cookie2, __LINE__); TestClassRedirection(h, L"Button", L"2.2.2.2!Button", L"testlib.dll", 5); _DeactivateCtx(cookie, __LINE__); } else { skip("Failed to create context for classtest.manifest\n"); } /* Dependency tests */ h = _CreateActCtxFromFile(L"deptest.manifest", __LINE__); if (h != INVALID_HANDLE_VALUE) { _ActivateCtx(h, &cookie, __LINE__); TestLibDependency(h); _DeactivateCtx(cookie, __LINE__); } else { skip("Failed to create context for deptest.manifest\n"); } /* Activate a context that depends on comctl32 v6 and run class tests again */ h = _CreateActCtxFromFile(L"comctl32dep.manifest", __LINE__); if (h != INVALID_HANDLE_VALUE) { _ActivateCtx(h, &cookie, __LINE__); TestClassRedirection(h, L"Button", L"6.0.", L"comctl32.dll", 29); ok( GetModuleHandleW(L"comctl32.dll") == NULL, "Expected comctl32 not to be loaded\n"); ok( GetModuleHandleW(L"user32.dll") == NULL, "Expected user32 not to be loaded\n"); _DeactivateCtx(cookie, __LINE__); } else { skip("Failed to create context for comctl32dep.manifest\n"); } }