Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- include/winnt.h | 1 + 1 file changed, 1 insertion(+)
diff --git a/include/winnt.h b/include/winnt.h index 079858d2f7d..31c6b7d69e9 100644 --- a/include/winnt.h +++ b/include/winnt.h @@ -5861,6 +5861,7 @@ typedef enum _ACTIVATION_CONTEXT_INFO_CLASS { #define ACTIVATION_CONTEXT_SECTION_CLR_SURROGATES 9 #define ACTIVATION_CONTEXT_SECTION_APPLICATION_SETTINGS 10 #define ACTIVATION_CONTEXT_SECTION_COMPATIBILITY_INFO 11 +#define ACTIVATION_CONTEXT_SECTION_WINRT_ACTIVATABLE_CLASSES 12
typedef enum _JOBOBJECTINFOCLASS {
And the matching ACTIVATION_CONTEXT_SECTION_WINRT_ACTIVATABLE_CLASSES activation context section.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/kernel32/tests/actctx.c | 85 ++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+)
diff --git a/dlls/kernel32/tests/actctx.c b/dlls/kernel32/tests/actctx.c index 8f925b0500f..e296ac40ced 100644 --- a/dlls/kernel32/tests/actctx.c +++ b/dlls/kernel32/tests/actctx.c @@ -120,6 +120,12 @@ static const char manifest3[] = " numMethods="10"" " baseInterface="{66666666-8888-7777-6666-555555555557}"" " />" +" <activatableClass name="Wine.Test.Class1" threadingModel="mta" xmlns="urn:schemas-microsoft-com:winrt.v1"/>" +" <activatableClass name="Wine.Test.Class2" threadingModel="both" xmlns="urn:schemas-microsoft-com:winrt.v1"/>" +" <activatableClass name="Wine.Test.Class3" threadingModel="sta" xmlns="urn:schemas-microsoft-com:winrt.v1"/>" +"</file>" +"<file name="Wine.Test.dll">" +" <activatableClass name="Wine.Test.Class4" threadingModel="sta" xmlns="urn:schemas-microsoft-com:winrt.v1"/>" "</file>" " <comInterfaceExternalProxyStub " " name="Iifaceps2"" @@ -395,6 +401,15 @@ static const char wrong_manifest10[] = "</trustInfo>" "</assembly>";
+/* activatableClass with the wrong xmlns is invalid */ +static const char wrong_manifest11[] = +"<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">" +"<assemblyIdentity version="1.0.0.0" name="Wine.Test" type="win32" />" +"<file name="testlib.dll">" +" <activatableClass name="Wine.Test.Class1" threadingModel="both" />" +"</file>" +"</assembly>"; + static const char wrong_depmanifest1[] = "<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">" "<assemblyIdentity type="win32" name="testdep" version="6.5.4.4" processorArchitecture="" ARCH "" />" @@ -1092,6 +1107,8 @@ static void test_create_fail(void) test_create_and_fail(wrong_manifest9, NULL, 0, TRUE /* WinXP */); trace("wrong_manifest10\n"); test_create_and_fail(wrong_manifest10, NULL, 0, TRUE /* WinXP */); + trace("wrong_manifest11\n"); + test_create_and_fail(wrong_manifest11, NULL, 1, FALSE); trace("UTF-16 manifest1 without BOM\n"); test_create_wide_and_fail(manifest1, FALSE ); trace("manifest2\n"); @@ -1352,6 +1369,70 @@ static void test_find_window_class(HANDLE handle, LPCWSTR clsname, ULONG exid, i ReleaseActCtx(handle); }
+enum winrt_threading_model +{ + WINRT_THREADING_MODEL_BOTH = 0, + WINRT_THREADING_MODEL_STA = 1, + WINRT_THREADING_MODEL_MTA = 2, +}; + +struct activatable_class_data +{ + ULONG size; + DWORD unk; + DWORD module_len; + DWORD module_offset; + DWORD threading_model; +}; + +static void test_find_activatable_class(HANDLE handle, const WCHAR *classid, enum winrt_threading_model threading_model, + const WCHAR *file, ULONG exid, int line) +{ + struct activatable_class_data *activatable_class; + struct strsection_header *header; + ACTCTX_SECTION_KEYED_DATA data; + void *ptr; + BOOL ret; + + memset(&data, 0xfe, sizeof(data)); + data.cbSize = sizeof(data); + + ret = FindActCtxSectionStringW(0, NULL, ACTIVATION_CONTEXT_SECTION_WINRT_ACTIVATABLE_CLASSES, classid, &data); + todo_wine + ok_(__FILE__, line)(ret || broken(!ret) /* <= Win10 v1809 */, "FindActCtxSectionStringW failed, error %lu\n", GetLastError()); + if (!ret) + { + ok_(__FILE__, line)(GetLastError() == ERROR_SXS_SECTION_NOT_FOUND, "got error %lu\n", GetLastError()); + return; + } + + ok_(__FILE__, line)(data.cbSize == sizeof(data), "got cbSize %lu\n", data.cbSize); + ok_(__FILE__, line)(data.ulDataFormatVersion == 1, "got ulDataFormatVersion %lu\n", data.ulDataFormatVersion); + ok_(__FILE__, line)(data.lpData != NULL, "got lpData %p\n", data.lpData); + + header = (struct strsection_header *)data.lpSectionBase; + ok_(__FILE__, line)(header->magic == 0x64487353, "got wrong magic 0x%08lx\n", header->magic); + ok_(__FILE__, line)(data.lpSectionBase != NULL, "got lpSectionBase %p\n", data.lpSectionBase); + ok_(__FILE__, line)(data.ulSectionTotalLength > 0, "got ulSectionTotalLength %lu\n", data.ulSectionTotalLength); + ok_(__FILE__, line)(data.lpSectionGlobalData == (BYTE *)header + header->global_offset, + "got lpSectionGlobalData %p\n", data.lpSectionGlobalData); + ok_(__FILE__, line)(data.ulSectionGlobalDataLength == header->global_len, + "got ulSectionGlobalDataLength %lu\n", data.ulSectionGlobalDataLength); + ok_(__FILE__, line)(data.hActCtx == NULL, "got hActCtx %p\n", data.hActCtx); + ok_(__FILE__, line)(data.ulAssemblyRosterIndex == exid, "got ulAssemblyRosterIndex %lu\n", data.ulAssemblyRosterIndex); + + activatable_class = (struct activatable_class_data *)data.lpData; + ok_(__FILE__, line)(activatable_class->size == sizeof(*activatable_class), "got size %lu\n", + activatable_class->size); + ok_(__FILE__, line)(activatable_class->threading_model == threading_model, "got threading_model %lu\n", + activatable_class->threading_model); + + ptr = (BYTE *)header + activatable_class->module_offset; + ok_(__FILE__, line)(wcslen(ptr) * sizeof(WCHAR) == activatable_class->module_len, + "got module_len %lu\n", activatable_class->module_len); + ok_(__FILE__, line)(!wcscmp(ptr, file), "got data.lpSectionGlobalData %s\n", debugstr_w(ptr)); +} + static void test_find_string_fail(void) { ACTCTX_SECTION_KEYED_DATA data = {sizeof(data)}; @@ -2180,6 +2261,10 @@ static void test_actctx(void) test_find_ifaceps_redirection(handle, &IID_Iifaceps2, &IID_TlibTest4, &IID_Ibifaceps, &IID_PS32, 1, __LINE__); test_find_ifaceps_redirection(handle, &IID_Iifaceps3, &IID_TlibTest4, &IID_Ibifaceps, NULL, 1, __LINE__); test_find_string_fail(); + test_find_activatable_class(handle, L"Wine.Test.Class1", WINRT_THREADING_MODEL_MTA, L"testlib.dll", 1, __LINE__); + test_find_activatable_class(handle, L"Wine.Test.Class2", WINRT_THREADING_MODEL_BOTH, L"testlib.dll", 1, __LINE__); + test_find_activatable_class(handle, L"Wine.Test.Class3", WINRT_THREADING_MODEL_STA, L"testlib.dll", 1, __LINE__); + test_find_activatable_class(handle, L"Wine.Test.Class4", WINRT_THREADING_MODEL_STA, L"Wine.Test.dll", 1, __LINE__);
b = DeactivateActCtx(0, cookie); ok(b, "DeactivateActCtx failed: %lu\n", GetLastError());
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/kernel32/tests/actctx.c | 1 - dlls/ntdll/actctx.c | 297 ++++++++++++++++++++++++++++++++++- 2 files changed, 296 insertions(+), 2 deletions(-)
diff --git a/dlls/kernel32/tests/actctx.c b/dlls/kernel32/tests/actctx.c index e296ac40ced..a91c46ddf89 100644 --- a/dlls/kernel32/tests/actctx.c +++ b/dlls/kernel32/tests/actctx.c @@ -1398,7 +1398,6 @@ static void test_find_activatable_class(HANDLE handle, const WCHAR *classid, enu data.cbSize = sizeof(data);
ret = FindActCtxSectionStringW(0, NULL, ACTIVATION_CONTEXT_SECTION_WINRT_ACTIVATABLE_CLASSES, classid, &data); - todo_wine ok_(__FILE__, line)(ret || broken(!ret) /* <= Win10 v1809 */, "FindActCtxSectionStringW failed, error %lu\n", GetLastError()); if (!ret) { diff --git a/dlls/ntdll/actctx.c b/dlls/ntdll/actctx.c index 9fa93a3b5e3..92f2f0148b2 100644 --- a/dlls/ntdll/actctx.c +++ b/dlls/ntdll/actctx.c @@ -301,6 +301,15 @@ struct progidredirect_data ULONG clsid_offset; };
+struct activatable_class_data +{ + ULONG size; + DWORD unk; + DWORD module_len; + DWORD module_offset; + DWORD threading_model; +}; + /*
Sections structure. @@ -413,6 +422,25 @@ struct progidredirect_data This sections uses generated alias guids from COM server section. This way ProgID -> CLSID mapping returns generated guid, not the real one. ProgID string is stored too, aligned. + + - WinRT activatable class section is a plain buffer with following format: + + <section header> + <module names[]> + <index[]> + <data[]> --- <class name> + <data> + + Header is fixed length structure - struct strsection_header, + contains classes count; + + Index is an array of fixed length index records, each record is + struct string_index. + + All strings in data itself are WCHAR, null terminated, 4-bytes aligned. + + All offsets are relative to section itself. + */
struct progids @@ -476,6 +504,11 @@ struct entity WCHAR *value; WCHAR *ns; } settings; + struct + { + WCHAR *name; + DWORD threading_model; + } activatable_class; } u; };
@@ -526,7 +559,8 @@ enum context_sections SERVERREDIRECT_SECTION = 8, IFACEREDIRECT_SECTION = 16, CLRSURROGATES_SECTION = 32, - PROGIDREDIRECT_SECTION = 64 + PROGIDREDIRECT_SECTION = 64, + ACTIVATABLE_CLASS_SECTION = 128, };
typedef struct _ACTIVATION_CONTEXT @@ -543,6 +577,7 @@ typedef struct _ACTIVATION_CONTEXT struct strsection_header *wndclass_section; struct strsection_header *dllredirect_section; struct strsection_header *progid_section; + struct strsection_header *activatable_class_section; struct guidsection_header *tlib_section; struct guidsection_header *comserver_section; struct guidsection_header *ifaceps_section; @@ -574,6 +609,7 @@ static const WCHAR current_archW[] = L"none"; static const WCHAR asmv1W[] = L"urn:schemas-microsoft-com:asm.v1"; static const WCHAR asmv2W[] = L"urn:schemas-microsoft-com:asm.v2"; static const WCHAR asmv3W[] = L"urn:schemas-microsoft-com:asm.v3"; +static const WCHAR winrtv1W[] = L"urn:schemas-microsoft-com:winrt.v1"; static const WCHAR compatibilityNSW[] = L"urn:schemas-microsoft-com:compatibility.v1"; static const WCHAR windowsSettings2005NSW[] = L"http://schemas.microsoft.com/SMI/2005/WindowsSettings"; static const WCHAR windowsSettings2011NSW[] = L"http://schemas.microsoft.com/SMI/2011/WindowsSettings"; @@ -876,6 +912,9 @@ static void free_entity_array(struct entity_array *array) RtlFreeHeap(GetProcessHeap(), 0, entity->u.settings.value); RtlFreeHeap(GetProcessHeap(), 0, entity->u.settings.ns); break; + case ACTIVATION_CONTEXT_SECTION_WINRT_ACTIVATABLE_CLASSES: + RtlFreeHeap(GetProcessHeap(), 0, entity->u.activatable_class.name); + break; default: FIXME("Unknown entity kind %d\n", entity->kind); } @@ -1088,6 +1127,7 @@ static void actctx_release( ACTIVATION_CONTEXT *actctx ) RtlFreeHeap( GetProcessHeap(), 0, actctx->ifaceps_section ); RtlFreeHeap( GetProcessHeap(), 0, actctx->clrsurrogate_section ); RtlFreeHeap( GetProcessHeap(), 0, actctx->progid_section ); + RtlFreeHeap( GetProcessHeap(), 0, actctx->activatable_class_section ); actctx->magic = 0; RtlFreeHeap( GetProcessHeap(), 0, actctx ); } @@ -2209,6 +2249,54 @@ static void parse_noinheritable_elem( xmlbuf_t *xmlbuf, const struct xml_elem *p if (!end) parse_expect_end_elem(xmlbuf, parent); }
+static void parse_activatable_class_elem( xmlbuf_t *xmlbuf, struct dll_redirect *dll, + struct actctx_loader *acl, const struct xml_elem *parent ) +{ + struct xml_elem elem; + struct xml_attr attr; + BOOL end = FALSE; + struct entity *entity; + + if (!(entity = add_entity(&dll->entities, ACTIVATION_CONTEXT_SECTION_WINRT_ACTIVATABLE_CLASSES))) + { + set_error( xmlbuf ); + return; + } + while (next_xml_attr(xmlbuf, &attr, &end)) + { + if (xml_attr_cmp(&attr, L"name")) + { + if (!(entity->u.activatable_class.name = xmlstrdupW(&attr.value))) + set_error( xmlbuf ); + } + else if (xml_attr_cmp(&attr, L"threadingModel")) + { + if (xmlstr_cmpi(&attr.value, L"both")) + entity->u.activatable_class.threading_model = 0; + else if (xmlstr_cmpi(&attr.value, L"sta")) + entity->u.activatable_class.threading_model = 1; + else if (xmlstr_cmpi(&attr.value, L"mta")) + entity->u.activatable_class.threading_model = 2; + else + set_error( xmlbuf ); + } + else if (!is_xmlns_attr( &attr )) + { + WARN("unknown attr %s\n", debugstr_xml_attr(&attr)); + } + } + + acl->actctx->sections |= ACTIVATABLE_CLASS_SECTION; + + if (end) return; + + while (next_xml_elem(xmlbuf, &elem, parent)) + { + WARN("unknown elem %s\n", debugstr_xml_elem(&elem)); + parse_unknown_elem(xmlbuf, &elem); + } +} + static void parse_file_elem( xmlbuf_t* xmlbuf, struct assembly* assembly, struct actctx_loader* acl, const struct xml_elem *parent ) { @@ -2278,6 +2366,10 @@ static void parse_file_elem( xmlbuf_t* xmlbuf, struct assembly* assembly, { parse_window_class_elem(xmlbuf, dll, acl, &elem); } + else if (xml_elem_cmp(&elem, L"activatableClass", winrtv1W)) + { + parse_activatable_class_elem(xmlbuf, dll, acl, &elem); + } else { WARN("unknown elem %s\n", debugstr_xml_elem(&elem)); @@ -3738,6 +3830,206 @@ static NTSTATUS find_window_class(ACTIVATION_CONTEXT* actctx, const UNICODE_STRI return STATUS_SUCCESS; }
+static inline struct string_index *get_activatable_class_first_index(ACTIVATION_CONTEXT *actctx) +{ + return (struct string_index *)((BYTE *)actctx->activatable_class_section + actctx->activatable_class_section->index_offset); +} + +static inline struct activatable_class_data *get_activatable_class_data(ACTIVATION_CONTEXT *ctxt, struct string_index *index) +{ + return (struct activatable_class_data *)((BYTE *)ctxt->activatable_class_section + index->data_offset); +} + +static NTSTATUS build_activatable_class_section(ACTIVATION_CONTEXT *actctx, struct strsection_header **section) +{ + unsigned int i, j, k, total_len = 0, class_count = 0, global_offset = 0, global_len = 0; + struct activatable_class_data *data; + struct strsection_header *header; + struct string_index *index; + ULONG name_offset; + + /* compute section length */ + for (i = 0; i < actctx->num_assemblies; i++) + { + struct assembly *assembly = &actctx->assemblies[i]; + for (j = 0; j < assembly->num_dlls; j++) + { + struct dll_redirect *dll = &assembly->dlls[j]; + BOOL has_class = FALSE; + + for (k = 0; k < dll->entities.num; k++) + { + struct entity *entity = &dll->entities.base[k]; + if (entity->kind == ACTIVATION_CONTEXT_SECTION_WINRT_ACTIVATABLE_CLASSES) + { + int class_len = wcslen(entity->u.activatable_class.name) + 1; + + /* each class entry needs index, data and string data */ + total_len += sizeof(*index); + total_len += aligned_string_len(class_len * sizeof(WCHAR)); + total_len += sizeof(*data); + + class_count++; + has_class = TRUE; + } + } + + if (has_class) + { + int module_len = wcslen(dll->name) + 1; + global_len += aligned_string_len(module_len * sizeof(WCHAR)); + } + } + } + + total_len += sizeof(*header) + global_len; + + header = RtlAllocateHeap(GetProcessHeap(), 0, total_len); + if (!header) return STATUS_NO_MEMORY; + + memset(header, 0, sizeof(*header)); + header->magic = STRSECTION_MAGIC; + header->size = sizeof(*header); + header->count = class_count; + header->global_offset = header->size; + header->global_len = global_len; + header->index_offset = header->global_offset + header->global_len; + index = (struct string_index *)((BYTE *)header + header->index_offset); + name_offset = header->index_offset + header->count * sizeof(*index); + + global_offset = header->size; + for (i = 0; i < actctx->num_assemblies; i++) + { + struct assembly *assembly = &actctx->assemblies[i]; + for (j = 0; j < assembly->num_dlls; j++) + { + struct dll_redirect *dll = &assembly->dlls[j]; + int module_len = wcslen(dll->name) * sizeof(WCHAR); + BOOL has_class = FALSE; + + for (k = 0; k < dll->entities.num; k++) + { + struct entity *entity = &dll->entities.base[k]; + + if (entity->kind == ACTIVATION_CONTEXT_SECTION_WINRT_ACTIVATABLE_CLASSES) + { + UNICODE_STRING str; + WCHAR *ptrW; + + /* setup new index entry */ + str.Buffer = entity->u.activatable_class.name; + str.Length = wcslen(entity->u.activatable_class.name) * sizeof(WCHAR); + str.MaximumLength = str.Length + sizeof(WCHAR); + /* hash class name */ + RtlHashUnicodeString(&str, TRUE, HASH_STRING_ALGORITHM_X65599, &index->hash); + + index->name_offset = name_offset; + index->name_len = str.Length; + index->data_offset = index->name_offset + aligned_string_len(str.MaximumLength); + index->data_len = sizeof(*data); + index->rosterindex = i + 1; + + /* class name */ + ptrW = (WCHAR *)((BYTE *)header + index->name_offset); + memcpy(ptrW, entity->u.activatable_class.name, index->name_len); + ptrW[index->name_len / sizeof(WCHAR)] = 0; + + /* class data */ + data = (struct activatable_class_data *)((BYTE *)header + index->data_offset); + data->size = sizeof(*data); + data->threading_model = entity->u.activatable_class.threading_model; + data->module_len = module_len; + data->module_offset = global_offset; + + name_offset += aligned_string_len(str.MaximumLength); + name_offset += sizeof(*data); + + index++; + has_class = TRUE; + } + } + + if (has_class) + { + WCHAR *ptrW = (WCHAR *)((BYTE *)header + global_offset); + memcpy(ptrW, dll->name, module_len); + ptrW[module_len / sizeof(WCHAR)] = 0; + global_offset += aligned_string_len(module_len + sizeof(WCHAR)); + } + } + } + + *section = header; + + return STATUS_SUCCESS; +} + +static NTSTATUS find_activatable_class(ACTIVATION_CONTEXT* actctx, const UNICODE_STRING *name, + PACTCTX_SECTION_KEYED_DATA data) +{ + struct string_index *iter, *index = NULL; + struct activatable_class_data *class; + UNICODE_STRING str; + ULONG hash; + int i; + + if (!(actctx->sections & ACTIVATABLE_CLASS_SECTION)) return STATUS_SXS_KEY_NOT_FOUND; + + if (!actctx->activatable_class_section) + { + struct strsection_header *section; + + NTSTATUS status = build_activatable_class_section(actctx, §ion); + if (status) return status; + + if (InterlockedCompareExchangePointer((void**)&actctx->activatable_class_section, section, NULL)) + RtlFreeHeap(GetProcessHeap(), 0, section); + } + + hash = 0; + RtlHashUnicodeString(name, TRUE, HASH_STRING_ALGORITHM_X65599, &hash); + iter = get_activatable_class_first_index(actctx); + + for (i = 0; i < actctx->activatable_class_section->count; i++) + { + if (iter->hash == hash) + { + str.Buffer = (WCHAR *)((BYTE *)actctx->activatable_class_section + iter->name_offset); + str.Length = iter->name_len; + if (RtlEqualUnicodeString( &str, name, TRUE )) + { + index = iter; + break; + } + else + WARN("hash collision 0x%08x, %s, %s\n", hash, debugstr_us(name), debugstr_us(&str)); + } + iter++; + } + + if (!index) return STATUS_SXS_KEY_NOT_FOUND; + + if (data) + { + class = get_activatable_class_data(actctx, index); + + data->ulDataFormatVersion = 1; + data->lpData = class; + /* full length includes string length with nulls */ + data->ulLength = class->size + class->module_len + sizeof(WCHAR); + data->lpSectionGlobalData = (BYTE *)actctx->activatable_class_section + actctx->activatable_class_section->global_offset; + data->ulSectionGlobalDataLength = actctx->activatable_class_section->global_len; + data->lpSectionBase = actctx->activatable_class_section; + data->ulSectionTotalLength = RtlSizeHeap( GetProcessHeap(), 0, actctx->activatable_class_section ); + data->hActCtx = NULL; + + if (data->cbSize >= FIELD_OFFSET(ACTCTX_SECTION_KEYED_DATA, ulAssemblyRosterIndex) + sizeof(ULONG)) + data->ulAssemblyRosterIndex = index->rosterindex; + } + + return STATUS_SUCCESS; +} + static NTSTATUS build_tlib_section(ACTIVATION_CONTEXT* actctx, struct guidsection_header **section) { unsigned int i, j, k, total_len = 0, tlib_count = 0, names_len = 0; @@ -4813,6 +5105,9 @@ static NTSTATUS find_string(ACTIVATION_CONTEXT* actctx, ULONG section_kind, case ACTIVATION_CONTEXT_SECTION_GLOBAL_OBJECT_RENAME_TABLE: FIXME("Unsupported yet section_kind %x\n", section_kind); return STATUS_SXS_SECTION_NOT_FOUND; + case ACTIVATION_CONTEXT_SECTION_WINRT_ACTIVATABLE_CLASSES: + status = find_activatable_class(actctx, section_name, data); + break; default: WARN("Unknown section_kind %x\n", section_kind); return STATUS_SXS_SECTION_NOT_FOUND;