ntdll: Support activatableClass activation context elements.

Signed-off-by: Rémi Bernon <rbernon@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Rémi Bernon 2022-04-06 16:09:29 +02:00 committed by Alexandre Julliard
parent c7f72cc400
commit c944f64166
2 changed files with 296 additions and 2 deletions

View file

@ -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)
{

View file

@ -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, &section);
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;