From Microsoft documentation, [Using Side-by-Side Assemblies as a Resource][sxs-rsrc] outlines the significance of each reserved manifest resource ID.
Further investigation reveals the following:
1. `CREATEPROCESS_MANIFEST_RESOURCE_ID` (value: `1`): This is used for process-wide initial (`NULL`) activation context. - **Usage documentation**: [Enabling an Assembly in an Application Without Extensions](https://learn.microsoft.com/en-us/windows/win32/sbscs/enabling-an-assembly-i...) - **Intended container module type**: EXE only. - **Lifetime of the associated activation context**: Alive until the process is terminated. Owned by system (NTDLL). - **Isolation-aware**: No.
2. `ISOLATIONAWARE_MANIFEST_RESOURCE_ID` (value: `2`): This is used to specify the assembly manifest that the loader (Ldr) uses to resolve static imports. Also, this is used by isolation-aware wrappers defined in `*.inl` files included in the Windows SDK (`include\um`) when the `ISOLATION_AWARE_ENABLED` macro is defined as a nonzero value. - **Usage documentation**: [Enabling an Assembly in an Application Hosting a DLL, Extension, or Control Panel](https://learn.microsoft.com/en-us/windows/win32/sbscs/enabling-an-assembly-i...) - **Intended container module type**: EXE and DLL. - **Lifetime of the associated activation context**: Alive until the module is unloaded (e.g., via `FreeLibrary`). Owned by system (NTDLL). - **Isolation-aware**: Yes.
3. `ISOLATIONAWARE_NOSTATICIMPORT_MANIFEST_RESOURCE_ID` (value: `3`): Ignored by the loader when resolving static imports. This is used by isolation-aware API wrappers defined in `*.inl` files included in the Windows SDK (`include\um`) when the `ISOLATION_AWARE_ENABLED` macro is defined as a nonzero value. - **Usage documentation**: [IsolationAwareCleanup function][IsolationAwareCleanup] - **Intended container module type**: EXE and DLL. - **Lifetime of the associated activation context**: Alive until the application calls [`IsolationAwareCleanup`][IsolationAwareCleanup][^ia-cleanup-note]. Owned by the isolation-aware API wrapper library (part of the Windows SDK, rather than being a system DLL). - **Isolation-aware**: Yes.
The `ISOLATION_AWARE_ENABLED` macro is documented in [Isolating Components][sxs-ic] as well as [Specifying a Default Activation Context][sxs-def-actctx]. When `ISOLATION_AWARE_ENABLED` macro is defined as a nonzero value, then the isolation-aware API wrapper library is enabled. The isolation-aware API wrapper library is responsible for:
- Creating (lazily) and destroying activation context (in `IsolationAwareCleanup`), if the manifest resource ID is `ISOLATIONAWARE_NOSTATICIMPORT_MANIFEST_RESOURCE_ID`.[^actctx-lifetime-remark] - Note: the isolation-aware API wrapper library does not manage the activation context's lifetime and delegates the responsibility to the system loader if the manifest resource ID is `ISOLATIONAWARE_MANIFEST_RESOURCE_ID`. - Intercepting Win32 API calls for automatic activation context activation. Each API wrapper activates the "isolation-aware" activation context (obtaining one if it did not exist) before calling the original procedure, and deactivates it before returning.
The isolation-aware wrappers may be either be defined inline, or compiled into static libraries that are linked into the final application executable.
From the information above as well as the tests included in this merge request, we can infer the following:
1. Although side-by-side awareness is a cross-cutting concern, the *activation* of activation contexts itself is *not* usually performed automatically by the system for the application (with the sole exception of process-wide activation context associated with `CREATEPROCESS_MANIFEST_RESOURCE_ID`, and static import resolution). 2. `ISOLATIONAWARE_NOSTATICIMPORT_MANIFEST_RESOURCE_ID` is treated like any other resource ID (from `0x0004` to `0xffff`) as far as the loader is concerned. 3. The resource ID of the manifest only concerns how the module containing the manifest intends to use the manifest. In fact, for DLLs that do not *statically* import symbols from side-by-side assemblies, the three resource IDs above are basically equivalent. A DLL can elect not to use manifest resources at all, and manage activation context and library loading by itself.
This is why I believe that this merge request is sufficient in completing the isolation-aware component support in Wine; the heavy lifting is done in the application side, not the system.
**EDIT**: Fix a few typos and unclear wording.
---
[^ia-cleanup-note]: According to the documentation, the application is presumably responsible for calling `IsolationAwareCleanup` from `DllMain` on `fdwReason = DLL_PROCESS_DETACH`. [^actctx-lifetime-remark]: [IsolationAwareCleanup § Remarks](https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/a...), from Microsoft documentation.
[sxs-rsrc]: https://learn.microsoft.com/en-us/windows/win32/sbscs/using-side-by-side-ass... [IsolationAwareCleanup]: https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/a...) [sxs-ic]: https://learn.microsoft.com/en-us/windows/win32/sbscs/isolating-components [sxs-def-actctx]: https://learn.microsoft.com/en-us/windows/win32/sbscs/specifying-a-default-a...
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=18889
-- v7: ntdll: Don't hard-code DLL manifest resource ID when looking up dependency assembly. kernel32/tests: Test loading assembly manifest resource inside dependencies. ntdll: Move ACTCTX lpResourceName validation to RtlCreateActivationContext. kernel32/tests: Test setting lpResourceName to NULL for CreateActCtxW. kernel32/tests: Remove test for ACTCTX_FLAG_HMODULE_VALID with hModule = NULL case.
From: Jinoh Kang jinoh.kang.kr@gmail.com
Today, the test scenario "ACTCTX_FLAG_HMODULE_VALID but hModule if not set" is broken and unreliable. This problem is not evident in WineHQ batch test runs; rather, the test failure seems to only be triggered when the kernel32:actctx test is run in isolation.
When the flag ACTCTX_FLAG_HMODULE_VALID is specified in ACTCTX but hModule is set to NULL, CreateActCtxW() may encounter different failure modes depending on the test executable file. Error codes observed so far include ERROR_SXS_CANT_GEN_ACTCTX and ERROR_SXS_MANIFEST_TOO_BIG.
When the aforementioned test reports ERROR_SXS_CANT_GEN_ACTCTX on Windows, an event is recorded in the Windows Event Log as follows:
- Log Name: Application - Source: SideBySide - Event ID: 59 - Level: Error - Description:
Activation context generation failed for "<path..>\kernel32_test.exe".Error in manifest or policy file "<path..>\kernel32_test.exe" on line 0. Invalid Xml syntax.
It appears that the inconsistent failure was caused by Windows trying to interpret the main executable file of the current process as an XML manifest file. This fails due to one or more of the following reasons:
- ERROR_SXS_CANT_GEN_ACTCTX: A valid PE executable that starts with the "MZ" signature is not a valid XML file.
- ERROR_SXS_MANIFEST_TOO_BIG (or ERROR_NOT_ENOUGH_MEMORY): The executable's size may exceed the limit imposed by the manifest parser. This is much more likely for binaries with debugging symbols.
Meanwhile, winetest.exe bundles a stripped version of the test executable (kernel32_test-stripped.exe), which is often smaller than the original executable (not stripped). This probably explains why the problem was not visible in batch test runs.
Fix this by removing the failing test entirely. --- dlls/kernel32/tests/actctx.c | 11 ----------- 1 file changed, 11 deletions(-)
diff --git a/dlls/kernel32/tests/actctx.c b/dlls/kernel32/tests/actctx.c index 2225215be8a..d64bba5e87d 100644 --- a/dlls/kernel32/tests/actctx.c +++ b/dlls/kernel32/tests/actctx.c @@ -2828,17 +2828,6 @@ todo_wine { delete_manifest_file("testdep1.manifest"); delete_manifest_file("testdep2.manifest");
- /* ACTCTX_FLAG_HMODULE_VALID but hModule is not set */ - memset(&actctx, 0, sizeof(ACTCTXA)); - actctx.cbSize = sizeof(ACTCTXA); - actctx.dwFlags = ACTCTX_FLAG_HMODULE_VALID; - SetLastError(0xdeadbeef); - handle = CreateActCtxA(&actctx); - ok(handle == INVALID_HANDLE_VALUE, "got handle %p\n", handle); - todo_wine - ok(GetLastError() == ERROR_SXS_CANT_GEN_ACTCTX || broken(GetLastError() == ERROR_NOT_ENOUGH_MEMORY) /* XP, win2k3 */, - "got error %ld\n", GetLastError()); - /* create from HMODULE - resource doesn't exist, lpSource is set */ memset(&actctx, 0, sizeof(ACTCTXA)); actctx.cbSize = sizeof(ACTCTXA);
From: Jinoh Kang jinoh.kang.kr@gmail.com
--- dlls/kernel32/tests/actctx.c | 54 ++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+)
diff --git a/dlls/kernel32/tests/actctx.c b/dlls/kernel32/tests/actctx.c index d64bba5e87d..98cb346fded 100644 --- a/dlls/kernel32/tests/actctx.c +++ b/dlls/kernel32/tests/actctx.c @@ -3779,6 +3779,59 @@ static void test_manifest_in_module(void) ReleaseActCtx(handle); }
+static void test_manifest_resource_name_omitted(void) +{ + WCHAR pathbuf[MAX_PATH]; + HANDLE handle; + ACTCTXW ctx; + DWORD err, len; + + memset(&ctx, 0, sizeof(ctx)); + ctx.cbSize = sizeof(ctx); + ctx.dwFlags = ACTCTX_FLAG_HMODULE_VALID; + ctx.hModule = GetModuleHandleW(NULL); + handle = CreateActCtxW(&ctx); + err = GetLastError(); + ok(handle == INVALID_HANDLE_VALUE, "CreateActCtxW shall fail\n"); + todo_wine + ok(err == ERROR_RESOURCE_TYPE_NOT_FOUND, "got %lu\n", err); + + memset(&ctx, 0, sizeof(ctx)); + ctx.cbSize = sizeof(ctx); + ctx.dwFlags = ACTCTX_FLAG_HMODULE_VALID | ACTCTX_FLAG_RESOURCE_NAME_VALID; + ctx.hModule = GetModuleHandleW(NULL); + ctx.lpResourceName = NULL; + handle = CreateActCtxW(&ctx); + err = GetLastError(); + ok(handle == INVALID_HANDLE_VALUE, "CreateActCtxW shall fail\n"); + todo_wine + ok(err == ERROR_INVALID_PARAMETER, "got %lu\n", err); + + len = GetModuleFileNameW(NULL, pathbuf, ARRAY_SIZE(pathbuf)); + ok(len > 0 && len < ARRAY_SIZE(pathbuf), "GetModuleFileNameW returned error %lu\n", GetLastError()); + + memset(&ctx, 0, sizeof(ctx)); + ctx.cbSize = sizeof(ctx); + ctx.lpSource = pathbuf; + ctx.dwFlags = 0; + handle = CreateActCtxW(&ctx); + err = GetLastError(); + ok(handle == INVALID_HANDLE_VALUE, "CreateActCtxW shall fail\n"); + todo_wine + ok(err == ERROR_RESOURCE_TYPE_NOT_FOUND, "got %lu\n", err); + + memset(&ctx, 0, sizeof(ctx)); + ctx.cbSize = sizeof(ctx); + ctx.lpSource = pathbuf; + ctx.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID; + ctx.lpResourceName = NULL; + handle = CreateActCtxW(&ctx); + err = GetLastError(); + ok(handle == INVALID_HANDLE_VALUE, "CreateActCtxW shall fail\n"); + todo_wine + ok(err == ERROR_INVALID_PARAMETER, "got %lu\n", err); +} + START_TEST(actctx) { int argc; @@ -3806,6 +3859,7 @@ START_TEST(actctx) }
test_manifest_in_module(); + test_manifest_resource_name_omitted(); test_actctx(); test_create_fail(); test_CreateActCtx();
From: Jinoh Kang jinoh.kang.kr@gmail.com
This prevents passing NULL resource name to get_manifest_in_module(). --- dlls/ntdll/actctx.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/dlls/ntdll/actctx.c b/dlls/ntdll/actctx.c index 54167a2db67..a2e159c76ae 100644 --- a/dlls/ntdll/actctx.c +++ b/dlls/ntdll/actctx.c @@ -2951,8 +2951,6 @@ static NTSTATUS get_manifest_in_module( struct actctx_loader* acl, struct assemb hModule, debugstr_w(filename) ); }
- if (!resname) return STATUS_INVALID_PARAMETER; - info.Type = RT_MANIFEST; info.Language = lang; if (!((ULONG_PTR)resname >> 16)) @@ -5232,6 +5230,9 @@ NTSTATUS WINAPI RtlCreateActivationContext( HANDLE *handle, const void *ptr ) (pActCtx->dwFlags & ~ACTCTX_FLAGS_ALL)) return STATUS_INVALID_PARAMETER;
+ if ((pActCtx->dwFlags & ACTCTX_FLAG_RESOURCE_NAME_VALID) && !pActCtx->lpResourceName) + return STATUS_INVALID_PARAMETER; + if (!(actctx = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*actctx) ))) return STATUS_NO_MEMORY;
From: Jinoh Kang jinoh.kang.kr@gmail.com
--- dlls/kernel32/tests/actctx.c | 382 +++++++++++++++++++++++++++++++++++ 1 file changed, 382 insertions(+)
diff --git a/dlls/kernel32/tests/actctx.c b/dlls/kernel32/tests/actctx.c index 98cb346fded..ac865268bd6 100644 --- a/dlls/kernel32/tests/actctx.c +++ b/dlls/kernel32/tests/actctx.c @@ -3407,6 +3407,39 @@ typedef struct void (WINAPI *get_path)(char *buffer, int buffer_size); } sxs_info;
+struct manifest_res_spec +{ + const char *name; + LANGID lang; + const char *override_manifest; +}; + +static void add_sxs_dll_manifest(const char *pathname, const struct manifest_res_spec *res_specs, size_t num_specs, const char *manifest) +{ + HANDLE update_h; + BOOL ret; + size_t i; + + update_h = BeginUpdateResourceA(pathname, FALSE); + ok(update_h != NULL, "BeginUpdateResourceA returned error %lu.\n", GetLastError()); + + for (i = 0; i < num_specs; i++) + { + const struct manifest_res_spec *res_spec = &res_specs[i]; + const char *cur_manifest = res_spec->override_manifest ? res_spec->override_manifest : manifest; + ret = UpdateResourceA(update_h, + MAKEINTRESOURCEA(RT_MANIFEST), + res_spec->name, + res_spec->lang, + (void *)cur_manifest, + strlen(cur_manifest)); + ok(ret, "UpdateResourceA returned error %lu.\n", GetLastError()); + } + + ret = EndUpdateResourceA(update_h, FALSE); + ok(ret, "EndUpdateResourceA returned error %lu.\n", GetLastError()); +} + static BOOL fill_sxs_info(sxs_info *info, const char *temp, const char *path_dll, const char *exe_manifest, const char *dll_manifest, BOOL do_load) { BOOL success; @@ -3704,6 +3737,352 @@ cleanup: } }
+#define subtest_manifest_res(d,e,r,n,l) subtest_manifest_res_(__LINE__,d,e,r,n,l) +static DWORD subtest_manifest_res_(int line, const char *manifest_exe, const char *manifest_dll, const struct manifest_res_spec *res_specs, size_t num_specs, LANGID lang) +{ + char path_tmp[MAX_PATH] = "", path_dll[MAX_PATH] = "", path_manifest_exe[MAX_PATH] = ""; + char path_tmp_lang[MAX_PATH] = ""; + static const char path_tmp_suffix[] = "winek32t\"; + WCHAR locale_name[LOCALE_NAME_MAX_LENGTH] = {0}; + DWORD err, prefix_len; + ACTCTXA actctx; + HANDLE handle; + BOOL ret; + int r; + + prefix_len = GetTempPathA(MAX_PATH - ARRAY_SIZE(path_tmp_suffix), path_tmp); + ok_(__FILE__, line)(prefix_len > 0, "GetTempPathA returned error %lu.\n", GetLastError()); + + memcpy(&path_tmp[prefix_len], path_tmp_suffix, sizeof(path_tmp_suffix) - sizeof(*path_tmp_suffix)); + ret = CreateDirectoryA(path_tmp, NULL); + ok_(__FILE__, line)(ret || GetLastError() == ERROR_ALREADY_EXISTS, + "CreateDirectoryA returned error %lu.\n", GetLastError()); + + if (lang) + { + r = LCIDToLocaleName(MAKELCID(lang, SORT_DEFAULT), + locale_name, ARRAY_SIZE(locale_name), LOCALE_ALLOW_NEUTRAL_NAMES); + ok(r > 0, "lang 0x%04x, error %lu.\n", lang, GetLastError()); + } + + if (locale_name[0]) + { + r = snprintf(path_tmp_lang, ARRAY_SIZE(path_tmp_lang), "%s%ls\", path_tmp, locale_name); + ok_(__FILE__, line)(r > 0 && r < ARRAY_SIZE(path_tmp_lang), "got %d\n", r); + + ret = CreateDirectoryA(path_tmp_lang, NULL); + ok_(__FILE__, line)(ret || GetLastError() == ERROR_ALREADY_EXISTS, + "CreateDirectoryA returned error %lu.\n", GetLastError()); + } + else + { + r = snprintf(path_tmp_lang, ARRAY_SIZE(path_tmp_lang), "%s", path_tmp); + ok_(__FILE__, line)(r > 0 && r < ARRAY_SIZE(path_tmp_lang), "got %d\n", r); + } + + r = snprintf(path_dll, ARRAY_SIZE(path_dll), "%s%s", path_tmp_lang, "sxs_dll.dll"); + ok_(__FILE__, line)(r > 0 && r < ARRAY_SIZE(path_dll), "got %d\n", r); + + r = snprintf(path_manifest_exe, ARRAY_SIZE(path_manifest_exe), "%s%s", path_tmp, "exe.manifest"); + ok_(__FILE__, line)(r > 0 && r < ARRAY_SIZE(path_manifest_exe), "got %d\n", r); + create_manifest_file(path_manifest_exe, manifest_exe, -1, NULL, NULL); + + extract_resource("dummy.dll", "TESTDLL", path_dll); + add_sxs_dll_manifest(path_dll, res_specs, num_specs, manifest_dll); + + memset(&actctx, 0, sizeof(actctx)); + actctx.cbSize = sizeof(actctx); + actctx.lpSource = path_manifest_exe; + actctx.lpAssemblyDirectory = path_tmp; + actctx.dwFlags = ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID; + + SetLastError(0xccccccccUL); + handle = CreateActCtxA(&actctx); + if (handle == INVALID_HANDLE_VALUE) + { + err = GetLastError(); + ok_(__FILE__, line)(err != ERROR_SUCCESS, "got %#lx.\n", err); + } + else + { + err = ERROR_SUCCESS; + ok_(__FILE__, line)(handle != NULL, "CreateActCtxA returned %p (error %lu)\n", handle, err); + ReleaseActCtx(handle); + } + + ret = DeleteFileA(path_manifest_exe); + ok_(__FILE__, line)(ret, "DeleteFileA(%s) returned error %lu\n.", debugstr_a(path_manifest_exe), GetLastError()); + + ret = DeleteFileA(path_dll); + ok_(__FILE__, line)(ret, "DeleteFileA(%s) returned error %lu\n.", debugstr_a(path_dll), GetLastError()); + + if (locale_name[0]) + { + ret = RemoveDirectoryA(path_tmp_lang); + ok_(__FILE__, line)(ret, "RemoveDirectoryA(%s) returned error %lu\n.", debugstr_a(path_tmp_lang), GetLastError()); + } + + ret = RemoveDirectoryA(path_tmp); + ok_(__FILE__, line)(ret, "RemoveDirectoryA(%s) returned error %lu %s\n.", debugstr_a(path_tmp), GetLastError(), debugstr_a(path_tmp_lang)); + + return err; +} + +/* Test loading DLL with dependency assembly in valid manifest resource */ +static void test_valid_manifest_resources(void) +{ + static const struct manifest_res_spec valid_specs_basic[] = { + /* Test arbitrary resource ID */ + { (char *)1 }, { (char *)2 }, { (char *)3 }, { (char *)4 }, + { (char *)5 }, { (char *)6 }, { (char *)7 }, { (char *)8 }, + { (char *)9 }, { (char *)10 }, { (char *)11 }, { (char *)12 }, + { (char *)13 }, { (char *)14 }, { (char *)15 }, { (char *)16 }, + { (char *)0x1234 }, { (char *)0x89ab }, { (char *)0xffff }, + + /* Test arbitrary LANGID */ + { (char *)2, MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT) }, + { (char *)2, MAKELANGID(LANG_FRENCH,SUBLANG_DEFAULT) }, + { (char *)2, 0x1234 }, + { (char *)2, 0xffff }, + }; + static const struct manifest_res_spec multiple_resources[] = { + /* Test multiple manifest resources coexisting inside a module */ + { (char *)2, 0 }, + { (char *)2, MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), wrong_manifest1 }, + { (char *)2, MAKELANGID(LANG_FRENCH,SUBLANG_DEFAULT), wrong_manifest1 }, + { (char *)3, 0, wrong_manifest1 }, + { (char *)0x1234, 0, wrong_manifest1 }, + }; + static const struct manifest_res_spec multiple_resources_lang_prio[] = { + /* Test language priority among multiple manifest resources */ + { (char *)2, MAKELANGID(LANG_INVARIANT,SUBLANG_NEUTRAL) }, + { (char *)3, 0, wrong_manifest1 }, + { (char *)3, MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), wrong_manifest1 }, + }; + static const struct manifest_res_spec multiple_resources_named[] = { + /* Test multiple manifest resources (ID / name) coexisting inside a module */ + { (char *)2, 0 }, + { "winetestdummy", 0, wrong_manifest1 }, + }; + DWORD err; + size_t i; + + for (i = 0; i < ARRAY_SIZE(valid_specs_basic); i++) + { + winetest_push_context("valid_specs_basic[%Iu]", i); + err = subtest_manifest_res(two_dll_manifest_exe, two_dll_manifest_dll, &valid_specs_basic[i], 1, 0); + todo_wine_if(valid_specs_basic[i].name != (char *)1 || valid_specs_basic[i].lang != 0) + ok(err == ERROR_SUCCESS, "got error %lu.\n", err); + winetest_pop_context(); + } + + err = subtest_manifest_res(two_dll_manifest_exe, two_dll_manifest_dll, multiple_resources, ARRAY_SIZE(multiple_resources), 0); + todo_wine + ok(err == ERROR_SUCCESS, "got error %lu.\n", err); + + err = subtest_manifest_res(two_dll_manifest_exe, two_dll_manifest_dll, multiple_resources_lang_prio, ARRAY_SIZE(multiple_resources_lang_prio), 0); + todo_wine + ok(err == ERROR_SUCCESS, "got error %lu.\n", err); + + err = subtest_manifest_res(two_dll_manifest_exe, two_dll_manifest_dll, multiple_resources_named, ARRAY_SIZE(multiple_resources_named), 0); + todo_wine + ok(err == ERROR_SUCCESS, "got error %lu.\n", err); +} + +/* Test loading DLL with dependency assembly in invalid manifest resource */ +static void test_invalid_manifest_resources(void) +{ + static const struct manifest_res_spec named_resources[] = { + /* Test name-only RT_MANIFEST resources */ + { "winetestdummy" }, + { "winetestdummy", 0x1234 }, + }; + static const struct manifest_res_spec multiple_resources[] = { + /* Test multiple manifest resources coexisting inside a module */ + { (char *)2, 0, wrong_manifest1 }, + { (char *)2, MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT) }, + { (char *)2, MAKELANGID(LANG_FRENCH,SUBLANG_DEFAULT) }, + { (char *)3, 0 }, + { (char *)0x1234, 0 }, + }; + static const struct manifest_res_spec multiple_resources_lang_prio[] = { + /* Test language priority among multiple manifest resources */ + { (char *)2, MAKELANGID(LANG_INVARIANT,SUBLANG_NEUTRAL), wrong_manifest1 }, + { (char *)3, 0 }, + { (char *)3, MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT) }, + }; + static const struct manifest_res_spec multiple_resources_named[] = { + /* Test multiple manifest resources (ID / name) coexisting inside a module */ + { (char *)2, 0, wrong_manifest1 }, + { "winetestdummy", 0 }, + }; + DWORD err; + size_t i; + + for (i = 0; i < ARRAY_SIZE(named_resources); i++) + { + winetest_push_context("named_resources[%Iu]", i); + err = subtest_manifest_res(two_dll_manifest_exe, two_dll_manifest_dll, &named_resources[i], 1, 0); + ok(err == ERROR_SXS_CANT_GEN_ACTCTX, "got error %lu.\n", err); + winetest_pop_context(); + } + + err = subtest_manifest_res(two_dll_manifest_exe, two_dll_manifest_dll, multiple_resources, ARRAY_SIZE(multiple_resources), 0); + ok(err == ERROR_SXS_CANT_GEN_ACTCTX, "got error %lu.\n", err); + + err = subtest_manifest_res(two_dll_manifest_exe, two_dll_manifest_dll, multiple_resources_lang_prio, ARRAY_SIZE(multiple_resources_lang_prio), 0); + ok(err == ERROR_SXS_CANT_GEN_ACTCTX, "got error %lu.\n", err); + + err = subtest_manifest_res(two_dll_manifest_exe, two_dll_manifest_dll, multiple_resources_named, ARRAY_SIZE(multiple_resources_named), 0); + ok(err == ERROR_SXS_CANT_GEN_ACTCTX, "got error %lu.\n", err); + + err = subtest_manifest_res(two_dll_manifest_exe, two_dll_manifest_dll, NULL, 0, 0); + ok(err == ERROR_SXS_CANT_GEN_ACTCTX, "got error %lu.\n", err); +} + +struct u16_vec +{ + UINT16 *base; + UINT16 *end; + UINT16 *alloc_end; +}; + +static BOOL u16_vec_add_uniq(struct u16_vec *vec, UINT16 item) +{ + UINT16 *p; + + for (p = vec->base; p != vec->end; p++) + { + if (*p == item) return FALSE; + } + + if (vec->end == vec->alloc_end) + { + ok(0, "u16_vec full\n"); + return FALSE; + } + else + { + *vec->end++ = item; + return TRUE; + } +} + +static void subtest_valid_manifest_resources_locale(LANGID actctx_lang) +{ + static const char manifest_exe_fmt[] = + "<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">" + "<dependency>" + "<dependentAssembly>" + "<assemblyIdentity type="win32" name="sxs_dll" version="1.0.0.0"" + " processorArchitecture="" ARCH "" publicKeyToken="0000000000000000" language="%ls"/>" + "</dependentAssembly>" + "</dependency>" + "</assembly>"; + static const char manifest_dll_fmt[] = + "<assembly xmlns="urn:schemas-microsoft-com:asm.v3" manifestVersion="1.0">" + "<assemblyIdentity type="win32" name="sxs_dll" version="1.0.0.0"" + " processorArchitecture="" ARCH "" publicKeyToken="0000000000000000" language="%ls"/>" + "</assembly>"; + static const char manifest_dll_nofmt[] = + "<assembly xmlns="urn:schemas-microsoft-com:asm.v3" manifestVersion="1.0">" + "<assemblyIdentity type="win32" name="sxs_dll" version="1.0.0.0"" + " processorArchitecture="" ARCH "" publicKeyToken="0000000000000000"/>" + "</assembly>"; + char manifest_exe[1024], manifest_dll[1024]; + WCHAR locale_name[LOCALE_NAME_MAX_LENGTH]; + UINT16 langs_arr[5], *it; + struct u16_vec langs; + LANGID user_ui_lang; + int ret; + + if (actctx_lang == MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)) + { + wcscpy(locale_name, L"*"); + strcpy(manifest_dll, manifest_dll_nofmt); + } + else + { + actctx_lang = ConvertDefaultLocale(actctx_lang); + ok(actctx_lang != MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), + "unexpected neutral locale\n"); + ret = LCIDToLocaleName(MAKELCID(actctx_lang, SORT_DEFAULT), + locale_name, ARRAY_SIZE(locale_name), LOCALE_ALLOW_NEUTRAL_NAMES); + ok(ret > 0, "error %lu.\n", GetLastError()); + + ret = snprintf(manifest_dll, ARRAY_SIZE(manifest_dll), manifest_dll_fmt, locale_name); + ok(ret > 0 && ret < ARRAY_SIZE(manifest_dll), "ret %d.\n", ret); + } + + ret = snprintf(manifest_exe, ARRAY_SIZE(manifest_exe), manifest_exe_fmt, locale_name); + ok(ret > 0 && ret < ARRAY_SIZE(manifest_exe), "ret %d.\n", ret); + + langs.base = langs_arr; + langs.end = langs_arr; + langs.alloc_end = langs_arr + ARRAY_SIZE(langs_arr); + + user_ui_lang = GetUserDefaultUILanguage(); + u16_vec_add_uniq(&langs, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)); + u16_vec_add_uniq(&langs, user_ui_lang); + u16_vec_add_uniq(&langs, MAKELANGID(PRIMARYLANGID(user_ui_lang), SUBLANG_NEUTRAL)); + u16_vec_add_uniq(&langs, MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT)); + u16_vec_add_uniq(&langs, 0x1); /* least number that is a valid LANGID */ + + for (it = langs.base; it != langs.end; it++) + { + struct manifest_res_spec specs[ARRAY_SIZE(langs_arr) + 1]; + size_t num_specs; + UINT16 *it2; + DWORD err; + + winetest_push_context("langs[%Id:]", it - langs.base); + + num_specs = 0; + for (it2 = it; it2 != langs.end; it2++) + { + struct manifest_res_spec spec = {(char *)2}; + spec.lang = *it2; + if (it2 != it) spec.override_manifest = wrong_manifest1; + ok(num_specs < ARRAY_SIZE(specs), "overrun\n"); + specs[num_specs++] = spec; + } + + err = subtest_manifest_res(manifest_exe, manifest_dll, specs, num_specs, actctx_lang); + todo_wine + ok(err == ERROR_SUCCESS, "got error %lu.\n", err); + + if (winetest_debug > 1 && err != ERROR_SUCCESS) + { + for (it2 = langs.base; it2 != langs.end; it2++) + { + trace("langs[%Id] = 0x%04x %c\n", it2 - langs.base, *it2, it2 == it ? '<' : ' '); + } + } + + winetest_pop_context(); + } +} + +static void test_valid_manifest_resources_locale(void) +{ + static const LANGID langs[] = { + MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + MAKELANGID(LANG_NEUTRAL, SUBLANG_SYS_DEFAULT), + MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL), + MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), + MAKELANGID(LANG_INVARIANT, SUBLANG_NEUTRAL), + }; + size_t i; + + for (i = 0; i < ARRAY_SIZE(langs); i++) + { + winetest_push_context("[%Iu]lang=0x%04x", i, langs[i]); + subtest_valid_manifest_resources_locale(langs[i]); + winetest_pop_context(); + } +} + static void run_sxs_test(int run) { switch(run) @@ -3860,6 +4239,9 @@ START_TEST(actctx)
test_manifest_in_module(); test_manifest_resource_name_omitted(); + test_valid_manifest_resources(); + test_invalid_manifest_resources(); + test_valid_manifest_resources_locale(); test_actctx(); test_create_fail(); test_CreateActCtx();
From: Jinoh Kang jinoh.kang.kr@gmail.com
This allows any manifest resource IDs (e.g., ISOLATIONAWARE_MANIFEST_RESOURCE_ID) to be recognized when looking up the assembly manifest of a dependency.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=18889 --- dlls/kernel32/tests/actctx.c | 8 +------- dlls/ntdll/actctx.c | 27 +++++++++++++++++++++++++-- 2 files changed, 26 insertions(+), 9 deletions(-)
diff --git a/dlls/kernel32/tests/actctx.c b/dlls/kernel32/tests/actctx.c index ac865268bd6..52d2fc4e4fe 100644 --- a/dlls/kernel32/tests/actctx.c +++ b/dlls/kernel32/tests/actctx.c @@ -3871,21 +3871,17 @@ static void test_valid_manifest_resources(void) { winetest_push_context("valid_specs_basic[%Iu]", i); err = subtest_manifest_res(two_dll_manifest_exe, two_dll_manifest_dll, &valid_specs_basic[i], 1, 0); - todo_wine_if(valid_specs_basic[i].name != (char *)1 || valid_specs_basic[i].lang != 0) ok(err == ERROR_SUCCESS, "got error %lu.\n", err); winetest_pop_context(); }
err = subtest_manifest_res(two_dll_manifest_exe, two_dll_manifest_dll, multiple_resources, ARRAY_SIZE(multiple_resources), 0); - todo_wine ok(err == ERROR_SUCCESS, "got error %lu.\n", err);
err = subtest_manifest_res(two_dll_manifest_exe, two_dll_manifest_dll, multiple_resources_lang_prio, ARRAY_SIZE(multiple_resources_lang_prio), 0); - todo_wine ok(err == ERROR_SUCCESS, "got error %lu.\n", err);
err = subtest_manifest_res(two_dll_manifest_exe, two_dll_manifest_dll, multiple_resources_named, ARRAY_SIZE(multiple_resources_named), 0); - todo_wine ok(err == ERROR_SUCCESS, "got error %lu.\n", err); }
@@ -4048,7 +4044,7 @@ static void subtest_valid_manifest_resources_locale(LANGID actctx_lang) }
err = subtest_manifest_res(manifest_exe, manifest_dll, specs, num_specs, actctx_lang); - todo_wine + todo_wine_if(PRIMARYLANGID(actctx_lang) != LANG_NEUTRAL && PRIMARYLANGID(actctx_lang) != LANG_INVARIANT) ok(err == ERROR_SUCCESS, "got error %lu.\n", err);
if (winetest_debug > 1 && err != ERROR_SUCCESS) @@ -4183,7 +4179,6 @@ static void test_manifest_resource_name_omitted(void) handle = CreateActCtxW(&ctx); err = GetLastError(); ok(handle == INVALID_HANDLE_VALUE, "CreateActCtxW shall fail\n"); - todo_wine ok(err == ERROR_INVALID_PARAMETER, "got %lu\n", err);
len = GetModuleFileNameW(NULL, pathbuf, ARRAY_SIZE(pathbuf)); @@ -4207,7 +4202,6 @@ static void test_manifest_resource_name_omitted(void) handle = CreateActCtxW(&ctx); err = GetLastError(); ok(handle == INVALID_HANDLE_VALUE, "CreateActCtxW shall fail\n"); - todo_wine ok(err == ERROR_INVALID_PARAMETER, "got %lu\n", err); }
diff --git a/dlls/ntdll/actctx.c b/dlls/ntdll/actctx.c index a2e159c76ae..e2adbd3394c 100644 --- a/dlls/ntdll/actctx.c +++ b/dlls/ntdll/actctx.c @@ -2929,6 +2929,24 @@ static NTSTATUS open_nt_file( HANDLE *handle, UNICODE_STRING *name ) FILE_SHARE_READ | FILE_SHARE_DELETE, FILE_SYNCHRONOUS_IO_ALERT ); }
+static NTSTATUS find_first_manifest_resource_in_module( HANDLE hModule, const WCHAR **resname ) +{ + static const LDR_RESOURCE_INFO manifest_res_info = { RT_MANIFEST }; + const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry_base, *entry; + const IMAGE_RESOURCE_DIRECTORY *resdir; + NTSTATUS status; + + status = LdrFindResourceDirectory_U( hModule, &manifest_res_info, 1, &resdir ); + if (status != STATUS_SUCCESS) return status; + + if (!resdir->NumberOfIdEntries) return STATUS_RESOURCE_NAME_NOT_FOUND; + entry_base = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(resdir + 1); + entry = entry_base + resdir->NumberOfNamedEntries; + *resname = (const WCHAR *)(ULONG_PTR)entry->u.Id; + + return STATUS_SUCCESS; +} + static NTSTATUS get_manifest_in_module( struct actctx_loader* acl, struct assembly_identity* ai, LPCWSTR filename, LPCWSTR directory, BOOL shared, HANDLE hModule, LPCWSTR resname, ULONG lang ) @@ -2951,6 +2969,12 @@ static NTSTATUS get_manifest_in_module( struct actctx_loader* acl, struct assemb hModule, debugstr_w(filename) ); }
+ if (!resname) + { + status = find_first_manifest_resource_in_module( hModule, &resname ); + if (status != STATUS_SUCCESS) return status; + } + info.Type = RT_MANIFEST; info.Language = lang; if (!((ULONG_PTR)resname >> 16)) @@ -3330,8 +3354,7 @@ static NTSTATUS lookup_assembly(struct actctx_loader* acl, status = open_nt_file( &file, &nameW ); if (!status) { - status = get_manifest_in_pe_file( acl, ai, nameW.Buffer, directory, FALSE, file, - (LPCWSTR)CREATEPROCESS_MANIFEST_RESOURCE_ID, 0 ); + status = get_manifest_in_pe_file( acl, ai, nameW.Buffer, directory, FALSE, file, NULL, 0 ); NtClose( file ); if (status == STATUS_SUCCESS) break;