From c944f64166fd8e5f0b7a233d76b3e11292ea3870 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 6 Apr 2022 16:09:29 +0200 Subject: [PATCH] ntdll: Support activatableClass activation context elements. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: RĂ©mi Bernon Signed-off-by: Alexandre Julliard --- 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: + +
+ + + --- + + + 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;