[PATCH 3/3] ntdll: Support activatableClass activation context elements.

Rémi Bernon rbernon at codeweavers.com
Wed Apr 6 09:09:29 CDT 2022


Signed-off-by: Rémi Bernon <rbernon at 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, &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;
-- 
2.35.1




More information about the wine-devel mailing list