ntdll: Implement compatible FindActCtxSectionString() for window class section.
diff --git a/dlls/kernel32/tests/actctx.c b/dlls/kernel32/tests/actctx.c
index 0b56c25..ae82a07 100644
--- a/dlls/kernel32/tests/actctx.c
+++ b/dlls/kernel32/tests/actctx.c
@@ -839,20 +839,39 @@
pReleaseActCtx(handle);
}
-struct wndclass_keyed_data
+struct wndclass_header
{
- DWORD size;
- DWORD reserved;
- DWORD classname_len;
- DWORD classname_offset;
- DWORD modulename_len;
- DWORD modulename_offset; /* offset relative to section base */
- WCHAR strdata[1];
+ DWORD magic;
+ DWORD unk1[4];
+ ULONG count;
+ ULONG index_offset;
+ DWORD unk2[4];
+};
+
+struct wndclass_index
+{
+ ULONG hash;
+ ULONG name_offset;
+ ULONG name_len;
+ ULONG data_offset;
+ ULONG data_len;
+ ULONG rosterindex;
+};
+
+struct wndclass_redirect_data
+{
+ ULONG size;
+ DWORD res;
+ ULONG name_len;
+ ULONG name_offset; /* versioned name offset */
+ ULONG module_len;
+ ULONG module_offset;/* container name offset */
};
static void test_find_window_class(HANDLE handle, LPCWSTR clsname, ULONG exid, int line)
{
- struct wndclass_keyed_data *wnddata;
+ struct wndclass_redirect_data *wnddata;
+ struct wndclass_header *header;
ACTCTX_SECTION_KEYED_DATA data;
BOOL ret;
@@ -862,51 +881,50 @@
ret = pFindActCtxSectionStringW(0, NULL,
ACTIVATION_CONTEXT_SECTION_WINDOW_CLASS_REDIRECTION,
clsname, &data);
- ok_(__FILE__, line)(ret, "FindActCtxSectionStringW failed: %u\n", GetLastError());
- if(!ret)
- {
- skip("couldn't find\n");
- return;
- }
+ ok_(__FILE__, line)(ret, "FindActCtxSectionStringW failed: %u, class %s\n", GetLastError(),
+ wine_dbgstr_w(clsname));
+ if (!ret) return;
- wnddata = (struct wndclass_keyed_data*)data.lpData;
+ header = (struct wndclass_header*)data.lpSectionBase;
+ wnddata = (struct wndclass_redirect_data*)data.lpData;
+ ok_(__FILE__, line)(header->magic == 0x64487353, "got wrong magic 0x%08x\n", header->magic);
+ ok_(__FILE__, line)(header->count > 0, "got count %d\n", header->count);
ok_(__FILE__, line)(data.cbSize == sizeof(data), "data.cbSize=%u\n", data.cbSize);
ok_(__FILE__, line)(data.ulDataFormatVersion == 1, "data.ulDataFormatVersion=%u\n", data.ulDataFormatVersion);
ok_(__FILE__, line)(data.lpData != NULL, "data.lpData == NULL\n");
-todo_wine
- ok_(__FILE__, line)(wnddata->size == FIELD_OFFSET(struct wndclass_keyed_data, strdata), "got %d for header size\n", wnddata->size);
- if (data.lpData && wnddata->size == FIELD_OFFSET(struct wndclass_keyed_data, strdata))
+ ok_(__FILE__, line)(wnddata->size == sizeof(*wnddata), "got %d for header size\n", wnddata->size);
+ if (data.lpData && wnddata->size == sizeof(*wnddata))
{
static const WCHAR verW[] = {'6','.','5','.','4','.','3','!',0};
WCHAR buff[50];
WCHAR *ptr;
ULONG len;
- ok_(__FILE__, line)(wnddata->reserved == 0, "got reserved as %d\n", wnddata->reserved);
+ ok_(__FILE__, line)(wnddata->res == 0, "got reserved as %d\n", wnddata->res);
/* redirect class name (versioned or not) is stored just after header data */
- ok_(__FILE__, line)(wnddata->classname_offset == wnddata->size, "got name offset as %d\n", wnddata->classname_offset);
- ok_(__FILE__, line)(wnddata->modulename_len > 0, "got module name length as %d\n", wnddata->modulename_len);
+ ok_(__FILE__, line)(wnddata->name_offset == wnddata->size, "got name offset as %d\n", wnddata->name_offset);
+ ok_(__FILE__, line)(wnddata->module_len > 0, "got module name length as %d\n", wnddata->module_len);
/* expected versioned name */
lstrcpyW(buff, verW);
lstrcatW(buff, clsname);
- ptr = (WCHAR*)((BYTE*)wnddata + wnddata->size);
+ ptr = (WCHAR*)((BYTE*)wnddata + wnddata->name_offset);
ok_(__FILE__, line)(!lstrcmpW(ptr, buff), "got wrong class name %s, expected %s\n", wine_dbgstr_w(ptr), wine_dbgstr_w(buff));
- ok_(__FILE__, line)(lstrlenW(ptr)*sizeof(WCHAR) == wnddata->classname_len,
- "got wrong class name length %d, expected %d\n", wnddata->classname_len, lstrlenW(ptr));
+ ok_(__FILE__, line)(lstrlenW(ptr)*sizeof(WCHAR) == wnddata->name_len,
+ "got wrong class name length %d, expected %d\n", wnddata->name_len, lstrlenW(ptr));
/* data length is simply header length + string data length including nulls */
- len = wnddata->size + wnddata->classname_len + wnddata->modulename_len + 2*sizeof(WCHAR);
+ len = wnddata->size + wnddata->name_len + wnddata->module_len + 2*sizeof(WCHAR);
ok_(__FILE__, line)(data.ulLength == len, "got wrong data length %d, expected %d\n", data.ulLength, len);
- if (data.ulSectionTotalLength > wnddata->modulename_offset)
+ if (data.ulSectionTotalLength > wnddata->module_offset)
{
WCHAR *modulename, *sectionptr;
/* just compare pointers */
- modulename = (WCHAR*)((BYTE*)wnddata + wnddata->size + wnddata->classname_len + sizeof(WCHAR));
- sectionptr = (WCHAR*)((BYTE*)data.lpSectionBase + wnddata->modulename_offset);
+ modulename = (WCHAR*)((BYTE*)wnddata + wnddata->size + wnddata->name_len + sizeof(WCHAR));
+ sectionptr = (WCHAR*)((BYTE*)data.lpSectionBase + wnddata->module_offset);
ok_(__FILE__, line)(modulename == sectionptr, "got wrong name offset %p, expected %p\n", sectionptr, modulename);
}
}
@@ -915,7 +933,6 @@
ok_(__FILE__, line)(data.ulSectionGlobalDataLength == 0, "data.ulSectionGlobalDataLength=%u\n",
data.ulSectionGlobalDataLength);
ok_(__FILE__, line)(data.lpSectionBase != NULL, "data.lpSectionBase == NULL\n");
-todo_wine
ok_(__FILE__, line)(data.ulSectionTotalLength > 0, "data.ulSectionTotalLength=%u\n",
data.ulSectionTotalLength);
ok_(__FILE__, line)(data.hActCtx == NULL, "data.hActCtx=%p\n", data.hActCtx);
@@ -928,23 +945,19 @@
ret = pFindActCtxSectionStringW(FIND_ACTCTX_SECTION_KEY_RETURN_HACTCTX, NULL,
ACTIVATION_CONTEXT_SECTION_WINDOW_CLASS_REDIRECTION,
clsname, &data);
- ok_(__FILE__, line)(ret, "FindActCtxSectionStringW failed: %u\n", GetLastError());
- if(!ret)
- {
- skip("couldn't find\n");
- return;
- }
+ ok_(__FILE__, line)(ret, "FindActCtxSectionStringW failed: %u, class %s\n", GetLastError(),
+ wine_dbgstr_w(clsname));
+ if (!ret) return;
ok_(__FILE__, line)(data.cbSize == sizeof(data), "data.cbSize=%u\n", data.cbSize);
ok_(__FILE__, line)(data.ulDataFormatVersion == 1, "data.ulDataFormatVersion=%u\n", data.ulDataFormatVersion);
ok_(__FILE__, line)(data.lpData != NULL, "data.lpData == NULL\n");
- /* ok_(__FILE__, line)(data.ulLength == ??, "data.ulLength=%u\n", data.ulLength); FIXME */
+ ok_(__FILE__, line)(data.ulLength > 0, "data.ulLength=%u\n", data.ulLength);
ok_(__FILE__, line)(data.lpSectionGlobalData == NULL, "data.lpSectionGlobalData != NULL\n");
ok_(__FILE__, line)(data.ulSectionGlobalDataLength == 0, "data.ulSectionGlobalDataLength=%u\n",
data.ulSectionGlobalDataLength);
ok_(__FILE__, line)(data.lpSectionBase != NULL, "data.lpSectionBase == NULL\n");
- /* ok_(__FILE__, line)(data.ulSectionTotalLength == 0, "data.ulSectionTotalLength=%u\n",
- data.ulSectionTotalLength); FIXME */
+ ok_(__FILE__, line)(data.ulSectionTotalLength > 0, "data.ulSectionTotalLength=%u\n", data.ulSectionTotalLength);
ok_(__FILE__, line)(data.hActCtx == handle, "data.hActCtx=%p\n", data.hActCtx);
ok_(__FILE__, line)(data.ulAssemblyRosterIndex == exid, "data.ulAssemblyRosterIndex=%u, expected %u\n",
data.ulAssemblyRosterIndex, exid);
@@ -1161,7 +1174,6 @@
ok(ret, "got %d\n", ret);
/* For both string same section is returned, meaning it's one wndclass section per context */
-todo_wine
ok(data.lpSectionBase == data2.lpSectionBase, "got %p, %p\n", data.lpSectionBase, data2.lpSectionBase);
ok(data.ulSectionTotalLength == data2.ulSectionTotalLength, "got %u, %u\n", data.ulSectionTotalLength,
data2.ulSectionTotalLength);
diff --git a/dlls/ntdll/actctx.c b/dlls/ntdll/actctx.c
index f2a94c7..1561db8 100644
--- a/dlls/ntdll/actctx.c
+++ b/dlls/ntdll/actctx.c
@@ -51,6 +51,7 @@
ACTCTX_FLAG_HMODULE_VALID )
#define ACTCTX_MAGIC 0xC07E3E11
+#define SECTION_MAGIC 0x64487353
/* we don't want to include winuser.h */
#define RT_MANIFEST ((ULONG_PTR)24)
@@ -93,6 +94,57 @@
BOOL optional;
};
+struct wndclass_header
+{
+ DWORD magic;
+ DWORD unk1[4];
+ ULONG count;
+ ULONG index_offset;
+ DWORD unk2[4];
+};
+
+struct wndclass_index
+{
+ ULONG hash; /* original class name hash */
+ ULONG name_offset; /* original class name offset */
+ ULONG name_len;
+ ULONG data_offset; /* redirect data offset */
+ ULONG data_len;
+ ULONG rosterindex;
+};
+
+struct wndclass_redirect_data
+{
+ ULONG size;
+ DWORD res;
+ ULONG name_len;
+ ULONG name_offset; /* versioned name offset */
+ ULONG module_len;
+ ULONG module_offset;/* container name offset */
+};
+
+/*
+ Window class redirection section is a plain buffer with following format:
+
+ <section header>
+ <index[]>
+ <data[]> --- <original name>
+ <redirect data>
+ <versioned name>
+ <module name>
+
+ Header is fixed length structure - struct wndclass_header,
+ contains redirected classes count;
+
+ Index is an array of fixed length index records, each record is
+ struct wndclass_index.
+
+ All strings in data itself are WCHAR, null terminated, 4-bytes aligned.
+
+ Versioned name offset is relative to redirect data structure (struct wndclass_redirect_data),
+ others are relative to section itself.
+*/
+
struct entity
{
DWORD kind;
@@ -163,6 +215,11 @@
struct entity_array entities;
};
+enum context_sections
+{
+ WINDOWCLASS_SECTION = 1
+};
+
typedef struct _ACTIVATION_CONTEXT
{
ULONG magic;
@@ -172,6 +229,9 @@
struct assembly *assemblies;
unsigned int num_assemblies;
unsigned int allocated_assemblies;
+ /* section data */
+ DWORD sections;
+ struct wndclass_header *wndclass_section;
} ACTIVATION_CONTEXT;
struct actctx_loader
@@ -640,6 +700,7 @@
RtlFreeHeap( GetProcessHeap(), 0, actctx->config.info );
RtlFreeHeap( GetProcessHeap(), 0, actctx->appdir.info );
RtlFreeHeap( GetProcessHeap(), 0, actctx->assemblies );
+ RtlFreeHeap( GetProcessHeap(), 0, actctx->wndclass_section );
actctx->magic = 0;
RtlFreeHeap( GetProcessHeap(), 0, actctx );
}
@@ -1015,7 +1076,22 @@
return parse_expect_end_elem(xmlbuf, typelibW, asmv1W);
}
-static BOOL parse_window_class_elem(xmlbuf_t* xmlbuf, struct dll_redirect* dll)
+static inline int aligned_string_len(int len)
+{
+ return (len + 3) & ~3;
+}
+
+static int get_assembly_version(struct assembly *assembly, WCHAR *ret)
+{
+ static const WCHAR fmtW[] = {'%','u','.','%','u','.','%','u','.','%','u',0};
+ struct assembly_version *ver = &assembly->id.version;
+ WCHAR buff[25];
+
+ if (!ret) ret = buff;
+ return sprintfW(ret, fmtW, ver->major, ver->minor, ver->build, ver->revision);
+}
+
+static BOOL parse_window_class_elem(xmlbuf_t* xmlbuf, struct dll_redirect* dll, struct actctx_loader* acl)
{
xmlstr_t elem, content;
BOOL end = FALSE, ret = TRUE;
@@ -1031,6 +1107,8 @@
if (!(entity->u.class.name = xmlstrdupW(&content))) return FALSE;
+ acl->actctx->sections |= WINDOWCLASS_SECTION;
+
while (ret && (ret = next_xml_elem(xmlbuf, &elem)))
{
if (xmlstr_cmp_end(&elem, windowClassW))
@@ -1287,7 +1365,7 @@
return end || parse_expect_end_elem(xmlbuf, noInheritableW, asmv1W);
}
-static BOOL parse_file_elem(xmlbuf_t* xmlbuf, struct assembly* assembly)
+static BOOL parse_file_elem(xmlbuf_t* xmlbuf, struct assembly* assembly, struct actctx_loader* acl)
{
xmlstr_t attr_name, attr_value, elem;
BOOL end = FALSE, error, ret = TRUE;
@@ -1347,7 +1425,7 @@
}
else if (xmlstr_cmp(&elem, windowClassW))
{
- ret = parse_window_class_elem(xmlbuf, dll);
+ ret = parse_window_class_elem(xmlbuf, dll, acl);
}
else
{
@@ -1435,7 +1513,7 @@
}
else if (xml_elem_cmp(&elem, fileW, asmv1W))
{
- ret = parse_file_elem(xmlbuf, assembly);
+ ret = parse_file_elem(xmlbuf, assembly, acl);
}
else if (xml_elem_cmp(&elem, clrClassW, asmv1W))
{
@@ -2146,10 +2224,71 @@
return STATUS_SXS_KEY_NOT_FOUND;
}
-static NTSTATUS find_window_class(ACTIVATION_CONTEXT* actctx, const UNICODE_STRING *section_name,
- PACTCTX_SECTION_KEYED_DATA data)
+static inline struct wndclass_index *get_wndclass_first_index(ACTIVATION_CONTEXT *actctx)
{
- unsigned int i, j, k, snlen = section_name->Length / sizeof(WCHAR);
+ return (struct wndclass_index*)((BYTE*)actctx->wndclass_section + actctx->wndclass_section->index_offset);
+}
+
+static inline struct wndclass_redirect_data *get_wndclass_data(ACTIVATION_CONTEXT *ctxt, struct wndclass_index *index)
+{
+ return (struct wndclass_redirect_data*)((BYTE*)ctxt->wndclass_section + index->data_offset);
+}
+
+static inline ULONG get_assembly_rosterindex(ACTIVATION_CONTEXT *actctx, const struct assembly* assembly)
+{
+ return (assembly - actctx->assemblies) + 1;
+}
+
+static NTSTATUS build_wndclass_section(ACTIVATION_CONTEXT* actctx, struct wndclass_header **section)
+{
+ unsigned int i, j, k, total_len = 0, class_count = 0;
+ struct wndclass_redirect_data *data;
+ struct wndclass_header *header;
+ struct wndclass_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];
+ for (k = 0; k < dll->entities.num; k++)
+ {
+ struct entity *entity = &dll->entities.base[k];
+ if (entity->kind == ACTIVATION_CONTEXT_SECTION_WINDOW_CLASS_REDIRECTION)
+ {
+ int class_len = strlenW(entity->u.class.name);
+ int len;
+
+ /* each class entry needs index, data and string data */
+ total_len += sizeof(struct wndclass_index);
+ total_len += sizeof(struct wndclass_redirect_data);
+ /* original name is stored separately */
+ total_len += aligned_string_len((class_len+1)*sizeof(WCHAR));
+ /* versioned name and module name are stored one after another */
+ len = get_assembly_version(assembly, NULL) + class_len + 2 /* null terminator and '!' separator */;
+ len += strlenW(dll->name) + 1;
+ total_len += aligned_string_len(len*sizeof(WCHAR));
+
+ class_count++;
+ }
+ }
+ }
+ }
+
+ total_len += sizeof(*header);
+
+ header = RtlAllocateHeap(GetProcessHeap(), 0, total_len);
+ if (!header) return STATUS_NO_MEMORY;
+
+ memset(header, 0, sizeof(*header));
+ header->magic = SECTION_MAGIC;
+ header->count = class_count;
+ header->index_offset = sizeof(*header);
+ index = (struct wndclass_index*)((BYTE*)header + header->index_offset);
+ name_offset = header->index_offset + header->count*sizeof(*index);
for (i = 0; i < actctx->num_assemblies; i++)
{
@@ -2162,13 +2301,127 @@
struct entity *entity = &dll->entities.base[k];
if (entity->kind == ACTIVATION_CONTEXT_SECTION_WINDOW_CLASS_REDIRECTION)
{
- if (!strncmpiW(section_name->Buffer, entity->u.class.name, snlen) && !entity->u.class.name[snlen])
- return fill_keyed_data(data, entity, dll, i);
+ static const WCHAR exclW[] = {'!',0};
+ ULONG versioned_len, module_len;
+ UNICODE_STRING str;
+ WCHAR *ptrW;
+
+ /* setup new index entry */
+ str.Buffer = entity->u.class.name;
+ str.Length = strlenW(entity->u.class.name)*sizeof(WCHAR);
+ str.MaximumLength = str.Length + sizeof(WCHAR);
+ /* hash original class name */
+ RtlHashUnicodeString(&str, TRUE, HASH_STRING_ALGORITHM_X65599, &index->hash);
+
+ /* include '!' separator too */
+ versioned_len = (get_assembly_version(assembly, NULL) + 1)*sizeof(WCHAR) + str.Length;
+ module_len = strlenW(dll->name)*sizeof(WCHAR);
+
+ 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) + versioned_len + module_len + 2*sizeof(WCHAR) /* two nulls */;
+ index->rosterindex = get_assembly_rosterindex(actctx, assembly);
+
+ /* setup data */
+ data = (struct wndclass_redirect_data*)((BYTE*)header + index->data_offset);
+ data->size = sizeof(*data);
+ data->res = 0;
+ data->name_len = versioned_len;
+ data->name_offset = sizeof(*data);
+ data->module_len = module_len;
+ data->module_offset = index->data_offset + data->name_offset + data->name_len + sizeof(WCHAR);
+
+ /* original class name */
+ ptrW = (WCHAR*)((BYTE*)header + index->name_offset);
+ memcpy(ptrW, entity->u.class.name, index->name_len);
+ ptrW[index->name_len/sizeof(WCHAR)] = 0;
+
+ /* module name */
+ ptrW = (WCHAR*)((BYTE*)header + data->module_offset);
+ memcpy(ptrW, dll->name, data->module_len);
+ ptrW[data->module_len/sizeof(WCHAR)] = 0;
+
+ /* versioned name */
+ ptrW = (WCHAR*)((BYTE*)data + data->name_offset);
+ get_assembly_version(assembly, ptrW);
+ strcatW(ptrW, exclW);
+ strcatW(ptrW, entity->u.class.name);
+
+ name_offset += sizeof(*data);
+ name_offset += aligned_string_len(str.MaximumLength) + aligned_string_len(versioned_len + module_len + 2*sizeof(WCHAR));
+
+ index++;
}
}
}
}
- return STATUS_SXS_KEY_NOT_FOUND;
+
+ *section = header;
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS find_window_class(ACTIVATION_CONTEXT* actctx, const UNICODE_STRING *name,
+ PACTCTX_SECTION_KEYED_DATA data)
+{
+ struct wndclass_index *iter, *index = NULL;
+ struct wndclass_redirect_data *class;
+ ULONG hash;
+ int i;
+
+ if (!(actctx->sections & WINDOWCLASS_SECTION)) return STATUS_SXS_KEY_NOT_FOUND;
+
+ if (!actctx->wndclass_section)
+ {
+ struct wndclass_header *section;
+
+ NTSTATUS status = build_wndclass_section(actctx, §ion);
+ if (status) return status;
+
+ if (interlocked_cmpxchg_ptr((void**)&actctx->wndclass_section, section, NULL))
+ RtlFreeHeap(GetProcessHeap(), 0, section);
+ }
+
+ hash = 0;
+ RtlHashUnicodeString(name, TRUE, HASH_STRING_ALGORITHM_X65599, &hash);
+ iter = get_wndclass_first_index(actctx);
+
+ for (i = 0; i < actctx->wndclass_section->count; i++)
+ {
+ if (iter->hash == hash)
+ {
+ const WCHAR *nameW = (WCHAR*)((BYTE*)actctx->wndclass_section + iter->name_offset);
+
+ if (!strcmpW(nameW, name->Buffer))
+ {
+ index = iter;
+ break;
+ }
+ else
+ WARN("hash collision 0x%08x, %s, %s\n", hash, debugstr_us(name), debugstr_w(nameW));
+ }
+ iter++;
+ }
+
+ if (!index) return STATUS_SXS_KEY_NOT_FOUND;
+
+ class = get_wndclass_data(actctx, index);
+
+ data->ulDataFormatVersion = 1;
+ data->lpData = class;
+ /* full length includes string length with nulls */
+ data->ulLength = class->size + class->name_len + class->module_len + 2*sizeof(WCHAR);
+ data->lpSectionGlobalData = NULL;
+ data->ulSectionGlobalDataLength = 0;
+ data->lpSectionBase = actctx->wndclass_section;
+ data->ulSectionTotalLength = RtlSizeHeap( GetProcessHeap(), 0, actctx->wndclass_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 find_string(ACTIVATION_CONTEXT* actctx, ULONG section_kind,
diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h
index d2d0764..7a0d925 100644
--- a/dlls/ntdll/ntdll_misc.h
+++ b/dlls/ntdll/ntdll_misc.h
@@ -261,4 +261,6 @@
#define HASH_STRING_ALGORITHM_X65599 1
#define HASH_STRING_ALGORITHM_INVALID 0xffffffff
+NTSTATUS WINAPI RtlHashUnicodeString(PCUNICODE_STRING,BOOLEAN,ULONG,ULONG*);
+
#endif