ntdll: Implement compatible FindActCtxSectionString() for window class section.

oldstable
Nikolay Sivov 2013-08-02 15:45:46 +04:00 committed by Alexandre Julliard
parent c1f9129d7b
commit 5ce01b3597
3 changed files with 317 additions and 50 deletions

View File

@ -839,20 +839,39 @@ if (dlldata->size == data.ulLength)
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 @@ static void test_find_window_class(HANDLE handle, LPCWSTR clsname, ULONG exid, i
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 @@ todo_wine
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 @@ todo_wine
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 @@ static void test_wndclass_section(void)
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);

View File

@ -51,6 +51,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(actctx);
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 @@ struct assembly_identity
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 assembly
struct entity_array entities;
};
enum context_sections
{
WINDOWCLASS_SECTION = 1
};
typedef struct _ACTIVATION_CONTEXT
{
ULONG magic;
@ -172,6 +229,9 @@ typedef struct _ACTIVATION_CONTEXT
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 @@ static void actctx_release( ACTIVATION_CONTEXT *actctx )
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 @@ static BOOL parse_typelib_elem(xmlbuf_t* xmlbuf, struct dll_redirect* dll)
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 @@ static BOOL parse_window_class_elem(xmlbuf_t* xmlbuf, struct dll_redirect* dll)
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 @@ static BOOL parse_noinheritable_elem(xmlbuf_t* xmlbuf)
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 @@ static BOOL parse_file_elem(xmlbuf_t* xmlbuf, struct assembly* assembly)
}
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 @@ static BOOL parse_assembly_elem(xmlbuf_t* xmlbuf, struct actctx_loader* acl,
}
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 @@ static NTSTATUS find_dll_redirection(ACTIVATION_CONTEXT* actctx, const UNICODE_S
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 @@ static NTSTATUS find_window_class(ACTIVATION_CONTEXT* actctx, const UNICODE_STRI
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, &section);
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,

View File

@ -261,4 +261,6 @@ extern mode_t FILE_umask DECLSPEC_HIDDEN;
#define HASH_STRING_ALGORITHM_X65599 1
#define HASH_STRING_ALGORITHM_INVALID 0xffffffff
NTSTATUS WINAPI RtlHashUnicodeString(PCUNICODE_STRING,BOOLEAN,ULONG,ULONG*);
#endif