wine/dlls/ntdll/actctx.c
2013-08-29 11:20:43 +02:00

3767 lines
127 KiB
C

/*
* Activation contexts
*
* Copyright 2004 Jon Griffiths
* Copyright 2007 Eric Pouech
* Copyright 2007 Jacek Caban for CodeWeavers
* Copyright 2007 Alexandre Julliard
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "config.h"
#include "wine/port.h"
#include <stdarg.h>
#include <stdio.h>
#define NONAMELESSUNION
#define NONAMELESSSTRUCT
#include "ntstatus.h"
#define WIN32_NO_STATUS
#include "winternl.h"
#include "ddk/wdm.h"
#include "ntdll_misc.h"
#include "wine/exception.h"
#include "wine/debug.h"
#include "wine/unicode.h"
WINE_DEFAULT_DEBUG_CHANNEL(actctx);
#define ACTCTX_FLAGS_ALL (\
ACTCTX_FLAG_PROCESSOR_ARCHITECTURE_VALID |\
ACTCTX_FLAG_LANGID_VALID |\
ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID |\
ACTCTX_FLAG_RESOURCE_NAME_VALID |\
ACTCTX_FLAG_SET_PROCESS_DEFAULT |\
ACTCTX_FLAG_APPLICATION_NAME_VALID |\
ACTCTX_FLAG_SOURCE_IS_ASSEMBLYREF |\
ACTCTX_FLAG_HMODULE_VALID )
#define ACTCTX_MAGIC 0xC07E3E11
#define STRSECTION_MAGIC 0x64487353 /* dHsS */
#define GUIDSECTION_MAGIC 0x64487347 /* dHsG */
/* we don't want to include winuser.h */
#define RT_MANIFEST ((ULONG_PTR)24)
#define CREATEPROCESS_MANIFEST_RESOURCE_ID ((ULONG_PTR)1)
/* from oaidl.h */
typedef enum tagLIBFLAGS {
LIBFLAG_FRESTRICTED = 0x1,
LIBFLAG_FCONTROL = 0x2,
LIBFLAG_FHIDDEN = 0x4,
LIBFLAG_FHASDISKIMAGE = 0x8
} LIBFLAGS;
/* from oleidl.idl */
typedef enum tagOLEMISC
{
OLEMISC_RECOMPOSEONRESIZE = 0x1,
OLEMISC_ONLYICONIC = 0x2,
OLEMISC_INSERTNOTREPLACE = 0x4,
OLEMISC_STATIC = 0x8,
OLEMISC_CANTLINKINSIDE = 0x10,
OLEMISC_CANLINKBYOLE1 = 0x20,
OLEMISC_ISLINKOBJECT = 0x40,
OLEMISC_INSIDEOUT = 0x80,
OLEMISC_ACTIVATEWHENVISIBLE = 0x100,
OLEMISC_RENDERINGISDEVICEINDEPENDENT = 0x200,
OLEMISC_INVISIBLEATRUNTIME = 0x400,
OLEMISC_ALWAYSRUN = 0x800,
OLEMISC_ACTSLIKEBUTTON = 0x1000,
OLEMISC_ACTSLIKELABEL = 0x2000,
OLEMISC_NOUIACTIVATE = 0x4000,
OLEMISC_ALIGNABLE = 0x8000,
OLEMISC_SIMPLEFRAME = 0x10000,
OLEMISC_SETCLIENTSITEFIRST = 0x20000,
OLEMISC_IMEMODE = 0x40000,
OLEMISC_IGNOREACTIVATEWHENVISIBLE = 0x80000,
OLEMISC_WANTSTOMENUMERGE = 0x100000,
OLEMISC_SUPPORTSMULTILEVELUNDO = 0x200000
} OLEMISC;
typedef struct
{
const WCHAR *ptr;
unsigned int len;
} xmlstr_t;
typedef struct
{
const WCHAR *ptr;
const WCHAR *end;
} xmlbuf_t;
struct file_info
{
ULONG type;
WCHAR *info;
};
struct assembly_version
{
USHORT major;
USHORT minor;
USHORT build;
USHORT revision;
};
struct assembly_identity
{
WCHAR *name;
WCHAR *arch;
WCHAR *public_key;
WCHAR *language;
WCHAR *type;
struct assembly_version version;
BOOL optional;
};
struct strsection_header
{
DWORD magic;
ULONG size;
DWORD unk1[3];
ULONG count;
ULONG index_offset;
DWORD unk2[4];
};
struct string_index
{
ULONG hash; /* key string hash */
ULONG name_offset;
ULONG name_len;
ULONG data_offset; /* redirect data offset */
ULONG data_len;
ULONG rosterindex;
};
struct guidsection_header
{
DWORD magic;
ULONG size;
DWORD unk[3];
ULONG count;
ULONG index_offset;
DWORD unk2;
ULONG names_offset;
ULONG names_len;
};
struct guid_index
{
GUID guid;
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 */
};
struct dllredirect_data
{
ULONG size;
ULONG unk;
DWORD res[3];
};
struct tlibredirect_data
{
ULONG size;
DWORD res;
ULONG name_len;
ULONG name_offset;
LANGID langid;
WORD flags;
ULONG help_len;
ULONG help_offset;
WORD major_version;
WORD minor_version;
};
enum comclass_threadingmodel
{
ThreadingModel_Apartment = 1,
ThreadingModel_Free = 2,
ThreadingModel_No = 3,
ThreadingModel_Both = 4,
ThreadingModel_Neutral = 5
};
enum comclass_miscfields
{
MiscStatus = 1,
MiscStatusIcon = 2,
MiscStatusContent = 4,
MiscStatusThumbnail = 8,
MiscStatusDocPrint = 16
};
/*
Sections structure.
Sections are accessible by string or guid key, that defines two types of sections.
All sections of each type have same magic value and header structure, index
data could be of two possible types too. So every string based section uses
the same index format, same applies to guid sections - they share same guid index
format.
- 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 strsection_header,
contains redirected 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.
Versioned name offset is relative to redirect data structure (struct wndclass_redirect_data),
others are relative to section itself.
- dll redirect section format:
<section header>
<index[]>
<data[]> --- <dll name>
<data>
This section doesn't seem to carry any payload data except dll names.
- typelib section format:
<section header>
<module names[]>
<index[]>
<data[]> --- <data>
<helpstring>
Header is fixed length, index is an array of fixed length 'struct guid_index'.
All strings are WCHAR, null terminated, 4-bytes aligned. Module names part is
4-bytes aligned as a whole.
Module name offsets are relative to section, helpstring offset is relative to data
structure itself.
*/
struct entity
{
DWORD kind;
union
{
struct
{
WCHAR *tlbid;
WCHAR *helpdir;
WORD flags;
WORD major;
WORD minor;
} typelib;
struct
{
WCHAR *clsid;
WCHAR *tlbid;
WCHAR *progid;
DWORD model;
DWORD miscstatus;
DWORD miscstatuscontent;
DWORD miscstatusthumbnail;
DWORD miscstatusicon;
DWORD miscstatusdocprint;
} comclass;
struct {
WCHAR *iid;
WCHAR *name;
} proxy;
struct
{
WCHAR *name;
BOOL versioned;
} class;
struct
{
WCHAR *name;
WCHAR *clsid;
} clrclass;
struct
{
WCHAR *name;
WCHAR *clsid;
} clrsurrogate;
} u;
};
struct entity_array
{
struct entity *base;
unsigned int num;
unsigned int allocated;
};
struct dll_redirect
{
WCHAR *name;
WCHAR *hash;
struct entity_array entities;
};
enum assembly_type
{
APPLICATION_MANIFEST,
ASSEMBLY_MANIFEST,
ASSEMBLY_SHARED_MANIFEST,
};
struct assembly
{
enum assembly_type type;
struct assembly_identity id;
struct file_info manifest;
WCHAR *directory;
BOOL no_inherit;
struct dll_redirect *dlls;
unsigned int num_dlls;
unsigned int allocated_dlls;
struct entity_array entities;
};
enum context_sections
{
WINDOWCLASS_SECTION = 1,
DLLREDIRECT_SECTION = 2,
TLIBREDIRECT_SECTION = 4
};
typedef struct _ACTIVATION_CONTEXT
{
ULONG magic;
int ref_count;
struct file_info config;
struct file_info appdir;
struct assembly *assemblies;
unsigned int num_assemblies;
unsigned int allocated_assemblies;
/* section data */
DWORD sections;
struct strsection_header *wndclass_section;
struct strsection_header *dllredirect_section;
struct guidsection_header *tlib_section;
} ACTIVATION_CONTEXT;
struct actctx_loader
{
ACTIVATION_CONTEXT *actctx;
struct assembly_identity *dependencies;
unsigned int num_dependencies;
unsigned int allocated_dependencies;
};
static const WCHAR asmv1W[] = {'a','s','m','v','1',':',0};
static const WCHAR asmv2W[] = {'a','s','m','v','2',':',0};
static const WCHAR assemblyW[] = {'a','s','s','e','m','b','l','y',0};
static const WCHAR assemblyIdentityW[] = {'a','s','s','e','m','b','l','y','I','d','e','n','t','i','t','y',0};
static const WCHAR bindingRedirectW[] = {'b','i','n','d','i','n','g','R','e','d','i','r','e','c','t',0};
static const WCHAR clrClassW[] = {'c','l','r','C','l','a','s','s',0};
static const WCHAR clrSurrogateW[] = {'c','l','r','S','u','r','r','o','g','a','t','e',0};
static const WCHAR comClassW[] = {'c','o','m','C','l','a','s','s',0};
static const WCHAR comInterfaceExternalProxyStubW[] = {'c','o','m','I','n','t','e','r','f','a','c','e','E','x','t','e','r','n','a','l','P','r','o','x','y','S','t','u','b',0};
static const WCHAR comInterfaceProxyStubW[] = {'c','o','m','I','n','t','e','r','f','a','c','e','P','r','o','x','y','S','t','u','b',0};
static const WCHAR dependencyW[] = {'d','e','p','e','n','d','e','n','c','y',0};
static const WCHAR dependentAssemblyW[] = {'d','e','p','e','n','d','e','n','t','A','s','s','e','m','b','l','y',0};
static const WCHAR descriptionW[] = {'d','e','s','c','r','i','p','t','i','o','n',0};
static const WCHAR fileW[] = {'f','i','l','e',0};
static const WCHAR hashW[] = {'h','a','s','h',0};
static const WCHAR noInheritW[] = {'n','o','I','n','h','e','r','i','t',0};
static const WCHAR noInheritableW[] = {'n','o','I','n','h','e','r','i','t','a','b','l','e',0};
static const WCHAR typelibW[] = {'t','y','p','e','l','i','b',0};
static const WCHAR windowClassW[] = {'w','i','n','d','o','w','C','l','a','s','s',0};
static const WCHAR clsidW[] = {'c','l','s','i','d',0};
static const WCHAR hashalgW[] = {'h','a','s','h','a','l','g',0};
static const WCHAR helpdirW[] = {'h','e','l','p','d','i','r',0};
static const WCHAR iidW[] = {'i','i','d',0};
static const WCHAR languageW[] = {'l','a','n','g','u','a','g','e',0};
static const WCHAR manifestVersionW[] = {'m','a','n','i','f','e','s','t','V','e','r','s','i','o','n',0};
static const WCHAR nameW[] = {'n','a','m','e',0};
static const WCHAR neutralW[] = {'n','e','u','t','r','a','l',0};
static const WCHAR newVersionW[] = {'n','e','w','V','e','r','s','i','o','n',0};
static const WCHAR oldVersionW[] = {'o','l','d','V','e','r','s','i','o','n',0};
static const WCHAR optionalW[] = {'o','p','t','i','o','n','a','l',0};
static const WCHAR processorArchitectureW[] = {'p','r','o','c','e','s','s','o','r','A','r','c','h','i','t','e','c','t','u','r','e',0};
static const WCHAR progidW[] = {'p','r','o','g','i','d',0};
static const WCHAR publicKeyTokenW[] = {'p','u','b','l','i','c','K','e','y','T','o','k','e','n',0};
static const WCHAR threadingmodelW[] = {'t','h','r','e','a','d','i','n','g','M','o','d','e','l',0};
static const WCHAR tlbidW[] = {'t','l','b','i','d',0};
static const WCHAR typeW[] = {'t','y','p','e',0};
static const WCHAR versionW[] = {'v','e','r','s','i','o','n',0};
static const WCHAR xmlnsW[] = {'x','m','l','n','s',0};
static const WCHAR versionedW[] = {'v','e','r','s','i','o','n','e','d',0};
static const WCHAR yesW[] = {'y','e','s',0};
static const WCHAR noW[] = {'n','o',0};
static const WCHAR restrictedW[] = {'R','E','S','T','R','I','C','T','E','D',0};
static const WCHAR controlW[] = {'C','O','N','T','R','O','L',0};
static const WCHAR hiddenW[] = {'H','I','D','D','E','N',0};
static const WCHAR hasdiskimageW[] = {'H','A','S','D','I','S','K','I','M','A','G','E',0};
static const WCHAR flagsW[] = {'f','l','a','g','s',0};
static const WCHAR miscstatusW[] = {'m','i','s','c','S','t','a','t','u','s',0};
static const WCHAR miscstatusiconW[] = {'m','i','s','c','S','t','a','t','u','s','I','c','o','n',0};
static const WCHAR miscstatuscontentW[] = {'m','i','s','c','S','t','a','t','u','s','C','o','n','t','e','n','t',0};
static const WCHAR miscstatusthumbnailW[] = {'m','i','s','c','S','t','a','t','u','s','T','h','u','m','b','n','a','i','l',0};
static const WCHAR miscstatusdocprintW[] = {'m','i','s','c','S','t','a','t','u','s','D','o','c','P','r','i','n','t',0};
static const WCHAR activatewhenvisibleW[] = {'a','c','t','i','v','a','t','e','w','h','e','n','v','i','s','i','b','l','e',0};
static const WCHAR actslikebuttonW[] = {'a','c','t','s','l','i','k','e','b','u','t','t','o','n',0};
static const WCHAR actslikelabelW[] = {'a','c','t','s','l','i','k','e','l','a','b','e','l',0};
static const WCHAR alignableW[] = {'a','l','i','g','n','a','b','l','e',0};
static const WCHAR alwaysrunW[] = {'a','l','w','a','y','s','r','u','n',0};
static const WCHAR canlinkbyole1W[] = {'c','a','n','l','i','n','k','b','y','o','l','e','1',0};
static const WCHAR cantlinkinsideW[] = {'c','a','n','t','l','i','n','k','i','n','s','i','d','e',0};
static const WCHAR ignoreactivatewhenvisibleW[] = {'i','g','n','o','r','e','a','c','t','i','v','a','t','e','w','h','e','n','v','i','s','i','b','l','e',0};
static const WCHAR imemodeW[] = {'i','m','e','m','o','d','e',0};
static const WCHAR insertnotreplaceW[] = {'i','n','s','e','r','t','n','o','t','r','e','p','l','a','c','e',0};
static const WCHAR insideoutW[] = {'i','n','s','i','d','e','o','u','t',0};
static const WCHAR invisibleatruntimeW[] = {'i','n','v','i','s','i','b','l','e','a','t','r','u','n','t','i','m','e',0};
static const WCHAR islinkobjectW[] = {'i','s','l','i','n','k','o','b','j','e','c','t',0};
static const WCHAR nouiactivateW[] = {'n','o','u','i','a','c','t','i','v','a','t','e',0};
static const WCHAR onlyiconicW[] = {'o','n','l','y','i','c','o','n','i','c',0};
static const WCHAR recomposeonresizeW[] = {'r','e','c','o','m','p','o','s','e','o','n','r','e','s','i','z','e',0};
static const WCHAR renderingisdeviceindependentW[] = {'r','e','n','d','e','r','i','n','g','i','s','d','e','v','i','c','e','i','n','d','e','p','e','n','d','e','n','t',0};
static const WCHAR setclientsitefirstW[] = {'s','e','t','c','l','i','e','n','t','s','i','t','e','f','i','r','s','t',0};
static const WCHAR simpleframeW[] = {'s','i','m','p','l','e','f','r','a','m','e',0};
static const WCHAR staticW[] = {'s','t','a','t','i','c',0};
static const WCHAR supportsmultilevelundoW[] = {'s','u','p','p','o','r','t','s','m','u','l','t','i','l','e','v','e','l','u','n','d','o',0};
static const WCHAR wantstomenumergeW[] = {'w','a','n','t','s','t','o','m','e','n','u','m','e','r','g','e',0};
struct olemisc_entry
{
const WCHAR *name;
OLEMISC value;
};
static const struct olemisc_entry olemisc_values[] =
{
{ activatewhenvisibleW, OLEMISC_ACTIVATEWHENVISIBLE },
{ actslikebuttonW, OLEMISC_ACTSLIKEBUTTON },
{ actslikelabelW, OLEMISC_ACTSLIKELABEL },
{ alignableW, OLEMISC_ALIGNABLE },
{ alwaysrunW, OLEMISC_ALWAYSRUN },
{ canlinkbyole1W, OLEMISC_CANLINKBYOLE1 },
{ cantlinkinsideW, OLEMISC_CANTLINKINSIDE },
{ ignoreactivatewhenvisibleW, OLEMISC_IGNOREACTIVATEWHENVISIBLE },
{ imemodeW, OLEMISC_IMEMODE },
{ insertnotreplaceW, OLEMISC_INSERTNOTREPLACE },
{ insideoutW, OLEMISC_INSIDEOUT },
{ invisibleatruntimeW, OLEMISC_INVISIBLEATRUNTIME },
{ islinkobjectW, OLEMISC_ISLINKOBJECT },
{ nouiactivateW, OLEMISC_NOUIACTIVATE },
{ onlyiconicW, OLEMISC_ONLYICONIC },
{ recomposeonresizeW, OLEMISC_RECOMPOSEONRESIZE },
{ renderingisdeviceindependentW, OLEMISC_RENDERINGISDEVICEINDEPENDENT },
{ setclientsitefirstW, OLEMISC_SETCLIENTSITEFIRST },
{ simpleframeW, OLEMISC_SIMPLEFRAME },
{ staticW, OLEMISC_STATIC },
{ supportsmultilevelundoW, OLEMISC_SUPPORTSMULTILEVELUNDO },
{ wantstomenumergeW, OLEMISC_WANTSTOMENUMERGE }
};
static const WCHAR xmlW[] = {'?','x','m','l',0};
static const WCHAR manifestv1W[] = {'u','r','n',':','s','c','h','e','m','a','s','-','m','i','c','r','o','s','o','f','t','-','c','o','m',':','a','s','m','.','v','1',0};
static const WCHAR manifestv3W[] = {'u','r','n',':','s','c','h','e','m','a','s','-','m','i','c','r','o','s','o','f','t','-','c','o','m',':','a','s','m','.','v','3',0};
static const WCHAR dotManifestW[] = {'.','m','a','n','i','f','e','s','t',0};
static const WCHAR version_formatW[] = {'%','u','.','%','u','.','%','u','.','%','u',0};
static const WCHAR wildcardW[] = {'*',0};
static ACTIVATION_CONTEXT system_actctx = { ACTCTX_MAGIC, 1 };
static ACTIVATION_CONTEXT *process_actctx = &system_actctx;
static WCHAR *strdupW(const WCHAR* str)
{
WCHAR* ptr;
if (!(ptr = RtlAllocateHeap(GetProcessHeap(), 0, (strlenW(str) + 1) * sizeof(WCHAR))))
return NULL;
return strcpyW(ptr, str);
}
static WCHAR *xmlstrdupW(const xmlstr_t* str)
{
WCHAR *strW;
if ((strW = RtlAllocateHeap(GetProcessHeap(), 0, (str->len + 1) * sizeof(WCHAR))))
{
memcpy( strW, str->ptr, str->len * sizeof(WCHAR) );
strW[str->len] = 0;
}
return strW;
}
static inline BOOL xmlstr_cmp(const xmlstr_t* xmlstr, const WCHAR *str)
{
return !strncmpW(xmlstr->ptr, str, xmlstr->len) && !str[xmlstr->len];
}
static inline BOOL xmlstr_cmpi(const xmlstr_t* xmlstr, const WCHAR *str)
{
return !strncmpiW(xmlstr->ptr, str, xmlstr->len) && !str[xmlstr->len];
}
static inline BOOL xmlstr_cmp_end(const xmlstr_t* xmlstr, const WCHAR *str)
{
return (xmlstr->len && xmlstr->ptr[0] == '/' &&
!strncmpW(xmlstr->ptr + 1, str, xmlstr->len - 1) && !str[xmlstr->len - 1]);
}
static inline BOOL xml_elem_cmp(const xmlstr_t *elem, const WCHAR *str, const WCHAR *namespace)
{
UINT len = strlenW( namespace );
if (!strncmpW(elem->ptr, str, elem->len) && !str[elem->len]) return TRUE;
return (elem->len > len && !strncmpW(elem->ptr, namespace, len) &&
!strncmpW(elem->ptr + len, str, elem->len - len) && !str[elem->len - len]);
}
static inline BOOL xml_elem_cmp_end(const xmlstr_t *elem, const WCHAR *str, const WCHAR *namespace)
{
if (elem->len && elem->ptr[0] == '/')
{
xmlstr_t elem_end;
elem_end.ptr = elem->ptr + 1;
elem_end.len = elem->len - 1;
return xml_elem_cmp( &elem_end, str, namespace );
}
return FALSE;
}
static inline BOOL isxmlspace( WCHAR ch )
{
return (ch == ' ' || ch == '\r' || ch == '\n' || ch == '\t');
}
static inline const char* debugstr_xmlstr(const xmlstr_t* str)
{
return debugstr_wn(str->ptr, str->len);
}
static inline const char* debugstr_version(const struct assembly_version *ver)
{
return wine_dbg_sprintf("%u.%u.%u.%u", ver->major, ver->minor, ver->build, ver->revision);
}
static struct assembly *add_assembly(ACTIVATION_CONTEXT *actctx, enum assembly_type at)
{
struct assembly *assembly;
if (actctx->num_assemblies == actctx->allocated_assemblies)
{
void *ptr;
unsigned int new_count;
if (actctx->assemblies)
{
new_count = actctx->allocated_assemblies * 2;
ptr = RtlReAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY,
actctx->assemblies, new_count * sizeof(*assembly) );
}
else
{
new_count = 4;
ptr = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, new_count * sizeof(*assembly) );
}
if (!ptr) return NULL;
actctx->assemblies = ptr;
actctx->allocated_assemblies = new_count;
}
assembly = &actctx->assemblies[actctx->num_assemblies++];
assembly->type = at;
return assembly;
}
static struct dll_redirect* add_dll_redirect(struct assembly* assembly)
{
if (assembly->num_dlls == assembly->allocated_dlls)
{
void *ptr;
unsigned int new_count;
if (assembly->dlls)
{
new_count = assembly->allocated_dlls * 2;
ptr = RtlReAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY,
assembly->dlls, new_count * sizeof(*assembly->dlls) );
}
else
{
new_count = 4;
ptr = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, new_count * sizeof(*assembly->dlls) );
}
if (!ptr) return NULL;
assembly->dlls = ptr;
assembly->allocated_dlls = new_count;
}
return &assembly->dlls[assembly->num_dlls++];
}
static void free_assembly_identity(struct assembly_identity *ai)
{
RtlFreeHeap( GetProcessHeap(), 0, ai->name );
RtlFreeHeap( GetProcessHeap(), 0, ai->arch );
RtlFreeHeap( GetProcessHeap(), 0, ai->public_key );
RtlFreeHeap( GetProcessHeap(), 0, ai->language );
RtlFreeHeap( GetProcessHeap(), 0, ai->type );
}
static struct entity* add_entity(struct entity_array *array, DWORD kind)
{
struct entity* entity;
if (array->num == array->allocated)
{
void *ptr;
unsigned int new_count;
if (array->base)
{
new_count = array->allocated * 2;
ptr = RtlReAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY,
array->base, new_count * sizeof(*array->base) );
}
else
{
new_count = 4;
ptr = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, new_count * sizeof(*array->base) );
}
if (!ptr) return NULL;
array->base = ptr;
array->allocated = new_count;
}
entity = &array->base[array->num++];
entity->kind = kind;
return entity;
}
static void free_entity_array(struct entity_array *array)
{
unsigned int i;
for (i = 0; i < array->num; i++)
{
struct entity *entity = &array->base[i];
switch (entity->kind)
{
case ACTIVATION_CONTEXT_SECTION_COM_SERVER_REDIRECTION:
RtlFreeHeap(GetProcessHeap(), 0, entity->u.comclass.clsid);
RtlFreeHeap(GetProcessHeap(), 0, entity->u.comclass.tlbid);
RtlFreeHeap(GetProcessHeap(), 0, entity->u.comclass.progid);
break;
case ACTIVATION_CONTEXT_SECTION_COM_INTERFACE_REDIRECTION:
RtlFreeHeap(GetProcessHeap(), 0, entity->u.proxy.iid);
RtlFreeHeap(GetProcessHeap(), 0, entity->u.proxy.name);
break;
case ACTIVATION_CONTEXT_SECTION_COM_TYPE_LIBRARY_REDIRECTION:
RtlFreeHeap(GetProcessHeap(), 0, entity->u.typelib.tlbid);
RtlFreeHeap(GetProcessHeap(), 0, entity->u.typelib.helpdir);
break;
case ACTIVATION_CONTEXT_SECTION_WINDOW_CLASS_REDIRECTION:
RtlFreeHeap(GetProcessHeap(), 0, entity->u.class.name);
break;
case ACTIVATION_CONTEXT_SECTION_COM_PROGID_REDIRECTION:
RtlFreeHeap(GetProcessHeap(), 0, entity->u.clrclass.name);
RtlFreeHeap(GetProcessHeap(), 0, entity->u.clrclass.clsid);
break;
case ACTIVATION_CONTEXT_SECTION_CLR_SURROGATES:
RtlFreeHeap(GetProcessHeap(), 0, entity->u.clrsurrogate.name);
RtlFreeHeap(GetProcessHeap(), 0, entity->u.clrsurrogate.clsid);
break;
default:
FIXME("Unknown entity kind %d\n", entity->kind);
}
}
RtlFreeHeap( GetProcessHeap(), 0, array->base );
}
static BOOL is_matching_string( const WCHAR *str1, const WCHAR *str2 )
{
if (!str1) return !str2;
return str2 && !strcmpiW( str1, str2 );
}
static BOOL is_matching_identity( const struct assembly_identity *id1,
const struct assembly_identity *id2 )
{
if (!is_matching_string( id1->name, id2->name )) return FALSE;
if (!is_matching_string( id1->arch, id2->arch )) return FALSE;
if (!is_matching_string( id1->public_key, id2->public_key )) return FALSE;
if (id1->language && id2->language && strcmpiW( id1->language, id2->language ))
{
if (strcmpW( wildcardW, id1->language ) && strcmpW( wildcardW, id2->language ))
return FALSE;
}
if (id1->version.major != id2->version.major) return FALSE;
if (id1->version.minor != id2->version.minor) return FALSE;
if (id1->version.build > id2->version.build) return FALSE;
if (id1->version.build == id2->version.build &&
id1->version.revision > id2->version.revision) return FALSE;
return TRUE;
}
static BOOL add_dependent_assembly_id(struct actctx_loader* acl,
struct assembly_identity* ai)
{
unsigned int i;
/* check if we already have that assembly */
for (i = 0; i < acl->actctx->num_assemblies; i++)
if (is_matching_identity( ai, &acl->actctx->assemblies[i].id ))
{
TRACE( "reusing existing assembly for %s arch %s version %u.%u.%u.%u\n",
debugstr_w(ai->name), debugstr_w(ai->arch), ai->version.major, ai->version.minor,
ai->version.build, ai->version.revision );
return TRUE;
}
for (i = 0; i < acl->num_dependencies; i++)
if (is_matching_identity( ai, &acl->dependencies[i] ))
{
TRACE( "reusing existing dependency for %s arch %s version %u.%u.%u.%u\n",
debugstr_w(ai->name), debugstr_w(ai->arch), ai->version.major, ai->version.minor,
ai->version.build, ai->version.revision );
return TRUE;
}
if (acl->num_dependencies == acl->allocated_dependencies)
{
void *ptr;
unsigned int new_count;
if (acl->dependencies)
{
new_count = acl->allocated_dependencies * 2;
ptr = RtlReAllocateHeap(GetProcessHeap(), 0, acl->dependencies,
new_count * sizeof(acl->dependencies[0]));
}
else
{
new_count = 4;
ptr = RtlAllocateHeap(GetProcessHeap(), 0, new_count * sizeof(acl->dependencies[0]));
}
if (!ptr) return FALSE;
acl->dependencies = ptr;
acl->allocated_dependencies = new_count;
}
acl->dependencies[acl->num_dependencies++] = *ai;
return TRUE;
}
static void free_depend_manifests(struct actctx_loader* acl)
{
unsigned int i;
for (i = 0; i < acl->num_dependencies; i++)
free_assembly_identity(&acl->dependencies[i]);
RtlFreeHeap(GetProcessHeap(), 0, acl->dependencies);
}
static WCHAR *build_assembly_dir(struct assembly_identity* ai)
{
static const WCHAR undW[] = {'_',0};
static const WCHAR noneW[] = {'n','o','n','e',0};
static const WCHAR mskeyW[] = {'d','e','a','d','b','e','e','f',0};
const WCHAR *arch = ai->arch ? ai->arch : noneW;
const WCHAR *key = ai->public_key ? ai->public_key : noneW;
const WCHAR *lang = ai->language ? ai->language : noneW;
const WCHAR *name = ai->name ? ai->name : noneW;
SIZE_T size = (strlenW(arch) + 1 + strlenW(name) + 1 + strlenW(key) + 24 + 1 +
strlenW(lang) + 1) * sizeof(WCHAR) + sizeof(mskeyW);
WCHAR *ret;
if (!(ret = RtlAllocateHeap( GetProcessHeap(), 0, size ))) return NULL;
strcpyW( ret, arch );
strcatW( ret, undW );
strcatW( ret, name );
strcatW( ret, undW );
strcatW( ret, key );
strcatW( ret, undW );
sprintfW( ret + strlenW(ret), version_formatW,
ai->version.major, ai->version.minor, ai->version.build, ai->version.revision );
strcatW( ret, undW );
strcatW( ret, lang );
strcatW( ret, undW );
strcatW( ret, mskeyW );
return ret;
}
static inline void append_string( WCHAR *buffer, const WCHAR *prefix, const WCHAR *str )
{
WCHAR *p = buffer;
if (!str) return;
strcatW( buffer, prefix );
p += strlenW(p);
*p++ = '"';
strcpyW( p, str );
p += strlenW(p);
*p++ = '"';
*p = 0;
}
static WCHAR *build_assembly_id( const struct assembly_identity *ai )
{
static const WCHAR archW[] =
{',','p','r','o','c','e','s','s','o','r','A','r','c','h','i','t','e','c','t','u','r','e','=',0};
static const WCHAR public_keyW[] =
{',','p','u','b','l','i','c','K','e','y','T','o','k','e','n','=',0};
static const WCHAR typeW[] =
{',','t','y','p','e','=',0};
static const WCHAR versionW[] =
{',','v','e','r','s','i','o','n','=',0};
WCHAR version[64], *ret;
SIZE_T size = 0;
sprintfW( version, version_formatW,
ai->version.major, ai->version.minor, ai->version.build, ai->version.revision );
if (ai->name) size += strlenW(ai->name) * sizeof(WCHAR);
if (ai->arch) size += strlenW(archW) + strlenW(ai->arch) + 2;
if (ai->public_key) size += strlenW(public_keyW) + strlenW(ai->public_key) + 2;
if (ai->type) size += strlenW(typeW) + strlenW(ai->type) + 2;
size += strlenW(versionW) + strlenW(version) + 2;
if (!(ret = RtlAllocateHeap( GetProcessHeap(), 0, (size + 1) * sizeof(WCHAR) )))
return NULL;
if (ai->name) strcpyW( ret, ai->name );
else *ret = 0;
append_string( ret, archW, ai->arch );
append_string( ret, public_keyW, ai->public_key );
append_string( ret, typeW, ai->type );
append_string( ret, versionW, version );
return ret;
}
static ACTIVATION_CONTEXT *check_actctx( HANDLE h )
{
ACTIVATION_CONTEXT *ret = NULL, *actctx = h;
if (!h || h == INVALID_HANDLE_VALUE) return NULL;
__TRY
{
if (actctx->magic == ACTCTX_MAGIC) ret = actctx;
}
__EXCEPT_PAGE_FAULT
{
}
__ENDTRY
return ret;
}
static inline void actctx_addref( ACTIVATION_CONTEXT *actctx )
{
interlocked_xchg_add( &actctx->ref_count, 1 );
}
static void actctx_release( ACTIVATION_CONTEXT *actctx )
{
if (interlocked_xchg_add( &actctx->ref_count, -1 ) == 1)
{
unsigned int i, j;
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];
free_entity_array( &dll->entities );
RtlFreeHeap( GetProcessHeap(), 0, dll->name );
RtlFreeHeap( GetProcessHeap(), 0, dll->hash );
}
RtlFreeHeap( GetProcessHeap(), 0, assembly->dlls );
RtlFreeHeap( GetProcessHeap(), 0, assembly->manifest.info );
RtlFreeHeap( GetProcessHeap(), 0, assembly->directory );
free_entity_array( &assembly->entities );
free_assembly_identity(&assembly->id);
}
RtlFreeHeap( GetProcessHeap(), 0, actctx->config.info );
RtlFreeHeap( GetProcessHeap(), 0, actctx->appdir.info );
RtlFreeHeap( GetProcessHeap(), 0, actctx->assemblies );
RtlFreeHeap( GetProcessHeap(), 0, actctx->dllredirect_section );
RtlFreeHeap( GetProcessHeap(), 0, actctx->wndclass_section );
RtlFreeHeap( GetProcessHeap(), 0, actctx->tlib_section );
actctx->magic = 0;
RtlFreeHeap( GetProcessHeap(), 0, actctx );
}
}
static BOOL next_xml_attr(xmlbuf_t* xmlbuf, xmlstr_t* name, xmlstr_t* value,
BOOL* error, BOOL* end)
{
const WCHAR* ptr;
*error = TRUE;
while (xmlbuf->ptr < xmlbuf->end && isxmlspace(*xmlbuf->ptr))
xmlbuf->ptr++;
if (xmlbuf->ptr == xmlbuf->end) return FALSE;
if (*xmlbuf->ptr == '/')
{
xmlbuf->ptr++;
if (xmlbuf->ptr == xmlbuf->end || *xmlbuf->ptr != '>')
return FALSE;
xmlbuf->ptr++;
*end = TRUE;
*error = FALSE;
return FALSE;
}
if (*xmlbuf->ptr == '>')
{
xmlbuf->ptr++;
*error = FALSE;
return FALSE;
}
ptr = xmlbuf->ptr;
while (ptr < xmlbuf->end && *ptr != '=' && *ptr != '>' && !isxmlspace(*ptr)) ptr++;
if (ptr == xmlbuf->end || *ptr != '=') return FALSE;
name->ptr = xmlbuf->ptr;
name->len = ptr-xmlbuf->ptr;
xmlbuf->ptr = ptr;
ptr++;
if (ptr == xmlbuf->end || (*ptr != '"' && *ptr != '\'')) return FALSE;
value->ptr = ++ptr;
if (ptr == xmlbuf->end) return FALSE;
ptr = memchrW(ptr, ptr[-1], xmlbuf->end - ptr);
if (!ptr)
{
xmlbuf->ptr = xmlbuf->end;
return FALSE;
}
value->len = ptr - value->ptr;
xmlbuf->ptr = ptr + 1;
if (xmlbuf->ptr == xmlbuf->end) return FALSE;
*error = FALSE;
return TRUE;
}
static BOOL next_xml_elem(xmlbuf_t* xmlbuf, xmlstr_t* elem)
{
const WCHAR* ptr;
for (;;)
{
ptr = memchrW(xmlbuf->ptr, '<', xmlbuf->end - xmlbuf->ptr);
if (!ptr)
{
xmlbuf->ptr = xmlbuf->end;
return FALSE;
}
ptr++;
if (ptr + 3 < xmlbuf->end && ptr[0] == '!' && ptr[1] == '-' && ptr[2] == '-') /* skip comment */
{
for (ptr += 3; ptr + 3 <= xmlbuf->end; ptr++)
if (ptr[0] == '-' && ptr[1] == '-' && ptr[2] == '>') break;
if (ptr + 3 > xmlbuf->end)
{
xmlbuf->ptr = xmlbuf->end;
return FALSE;
}
xmlbuf->ptr = ptr + 3;
}
else break;
}
xmlbuf->ptr = ptr;
while (ptr < xmlbuf->end && !isxmlspace(*ptr) && *ptr != '>' && (*ptr != '/' || ptr == xmlbuf->ptr))
ptr++;
elem->ptr = xmlbuf->ptr;
elem->len = ptr - xmlbuf->ptr;
xmlbuf->ptr = ptr;
return xmlbuf->ptr != xmlbuf->end;
}
static BOOL parse_xml_header(xmlbuf_t* xmlbuf)
{
/* FIXME: parse attributes */
const WCHAR *ptr;
for (ptr = xmlbuf->ptr; ptr < xmlbuf->end - 1; ptr++)
{
if (ptr[0] == '?' && ptr[1] == '>')
{
xmlbuf->ptr = ptr + 2;
return TRUE;
}
}
return FALSE;
}
static BOOL parse_text_content(xmlbuf_t* xmlbuf, xmlstr_t* content)
{
const WCHAR *ptr = memchrW(xmlbuf->ptr, '<', xmlbuf->end - xmlbuf->ptr);
if (!ptr) return FALSE;
content->ptr = xmlbuf->ptr;
content->len = ptr - xmlbuf->ptr;
xmlbuf->ptr = ptr;
return TRUE;
}
static BOOL parse_version(const xmlstr_t *str, struct assembly_version *version)
{
unsigned int ver[4];
unsigned int pos;
const WCHAR *curr;
/* major.minor.build.revision */
ver[0] = ver[1] = ver[2] = ver[3] = pos = 0;
for (curr = str->ptr; curr < str->ptr + str->len; curr++)
{
if (*curr >= '0' && *curr <= '9')
{
ver[pos] = ver[pos] * 10 + *curr - '0';
if (ver[pos] >= 0x10000) goto error;
}
else if (*curr == '.')
{
if (++pos >= 4) goto error;
}
else goto error;
}
version->major = ver[0];
version->minor = ver[1];
version->build = ver[2];
version->revision = ver[3];
return TRUE;
error:
FIXME( "Wrong version definition in manifest file (%s)\n", debugstr_xmlstr(str) );
return FALSE;
}
static BOOL parse_expect_elem(xmlbuf_t* xmlbuf, const WCHAR* name, const WCHAR *namespace)
{
xmlstr_t elem;
if (!next_xml_elem(xmlbuf, &elem)) return FALSE;
if (xml_elem_cmp(&elem, name, namespace)) return TRUE;
FIXME( "unexpected element %s\n", debugstr_xmlstr(&elem) );
return FALSE;
}
static BOOL parse_expect_no_attr(xmlbuf_t* xmlbuf, BOOL* end)
{
xmlstr_t attr_name, attr_value;
BOOL error;
while (next_xml_attr(xmlbuf, &attr_name, &attr_value, &error, end))
{
WARN("unexpected attr %s=%s\n", debugstr_xmlstr(&attr_name),
debugstr_xmlstr(&attr_value));
}
return !error;
}
static BOOL parse_end_element(xmlbuf_t *xmlbuf)
{
BOOL end = FALSE;
return parse_expect_no_attr(xmlbuf, &end) && !end;
}
static BOOL parse_expect_end_elem(xmlbuf_t *xmlbuf, const WCHAR *name, const WCHAR *namespace)
{
xmlstr_t elem;
if (!next_xml_elem(xmlbuf, &elem)) return FALSE;
if (!xml_elem_cmp_end(&elem, name, namespace))
{
FIXME( "unexpected element %s\n", debugstr_xmlstr(&elem) );
return FALSE;
}
return parse_end_element(xmlbuf);
}
static BOOL parse_unknown_elem(xmlbuf_t *xmlbuf, const xmlstr_t *unknown_elem)
{
xmlstr_t attr_name, attr_value, elem;
BOOL end = FALSE, error, ret = TRUE;
while(next_xml_attr(xmlbuf, &attr_name, &attr_value, &error, &end));
if(error || end) return end;
while(ret && (ret = next_xml_elem(xmlbuf, &elem)))
{
if(*elem.ptr == '/' && elem.len - 1 == unknown_elem->len &&
!strncmpW(elem.ptr+1, unknown_elem->ptr, unknown_elem->len))
break;
else
ret = parse_unknown_elem(xmlbuf, &elem);
}
return ret && parse_end_element(xmlbuf);
}
static BOOL parse_assembly_identity_elem(xmlbuf_t* xmlbuf, ACTIVATION_CONTEXT* actctx,
struct assembly_identity* ai)
{
xmlstr_t attr_name, attr_value;
BOOL end = FALSE, error;
while (next_xml_attr(xmlbuf, &attr_name, &attr_value, &error, &end))
{
if (xmlstr_cmp(&attr_name, nameW))
{
if (!(ai->name = xmlstrdupW(&attr_value))) return FALSE;
}
else if (xmlstr_cmp(&attr_name, typeW))
{
if (!(ai->type = xmlstrdupW(&attr_value))) return FALSE;
}
else if (xmlstr_cmp(&attr_name, versionW))
{
if (!parse_version(&attr_value, &ai->version)) return FALSE;
}
else if (xmlstr_cmp(&attr_name, processorArchitectureW))
{
if (!(ai->arch = xmlstrdupW(&attr_value))) return FALSE;
}
else if (xmlstr_cmp(&attr_name, publicKeyTokenW))
{
if (!(ai->public_key = xmlstrdupW(&attr_value))) return FALSE;
}
else if (xmlstr_cmp(&attr_name, languageW))
{
WARN("Unsupported yet language attribute (%s)\n",
debugstr_xmlstr(&attr_value));
if (!(ai->language = xmlstrdupW(&attr_value))) return FALSE;
}
else
{
WARN("unknown attr %s=%s\n", debugstr_xmlstr(&attr_name),
debugstr_xmlstr(&attr_value));
}
}
TRACE( "name=%s version=%s arch=%s\n",
debugstr_w(ai->name), debugstr_version(&ai->version), debugstr_w(ai->arch) );
if (error || end) return end;
return parse_expect_end_elem(xmlbuf, assemblyIdentityW, asmv1W);
}
static enum comclass_threadingmodel parse_com_class_threadingmodel(xmlstr_t *value)
{
static const WCHAR apartW[] = {'A','p','a','r','t','m','e','n','t',0};
static const WCHAR neutralW[] = {'N','e','u','t','r','a','l',0};
static const WCHAR freeW[] = {'F','r','e','e',0};
static const WCHAR bothW[] = {'B','o','t','h',0};
if (value->len == 0) return ThreadingModel_No;
if (xmlstr_cmp(value, apartW))
return ThreadingModel_Apartment;
else if (xmlstr_cmp(value, freeW))
return ThreadingModel_Free;
else if (xmlstr_cmp(value, bothW))
return ThreadingModel_Both;
else if (xmlstr_cmp(value, neutralW))
return ThreadingModel_Neutral;
else
return ThreadingModel_No;
};
static OLEMISC get_olemisc_value(const WCHAR *str, int len)
{
int min, max;
min = 0;
max = sizeof(olemisc_values)/sizeof(struct olemisc_entry) - 1;
while (min <= max)
{
int n, c;
n = (min+max)/2;
c = strncmpW(olemisc_values[n].name, str, len);
if (!c && !olemisc_values[n].name[len])
return olemisc_values[n].value;
if (c >= 0)
max = n-1;
else
min = n+1;
}
WARN("unknown flag %s\n", debugstr_wn(str, len));
return 0;
}
static DWORD parse_com_class_misc(const xmlstr_t *value)
{
const WCHAR *str = value->ptr, *start;
DWORD flags = 0;
int i = 0;
/* it's comma separated list of flags */
while (i < value->len)
{
start = str;
while (*str != ',' && (i++ < value->len)) str++;
flags |= get_olemisc_value(start, str-start);
/* skip separator */
str++;
i++;
}
return flags;
}
static BOOL parse_com_class_elem(xmlbuf_t* xmlbuf, struct dll_redirect* dll)
{
xmlstr_t elem, attr_name, attr_value;
BOOL ret, end = FALSE, error;
struct entity* entity;
if (!(entity = add_entity(&dll->entities, ACTIVATION_CONTEXT_SECTION_COM_SERVER_REDIRECTION)))
return FALSE;
while (next_xml_attr(xmlbuf, &attr_name, &attr_value, &error, &end))
{
if (xmlstr_cmp(&attr_name, clsidW))
{
if (!(entity->u.comclass.clsid = xmlstrdupW(&attr_value))) return FALSE;
}
else if (xmlstr_cmp(&attr_name, progidW))
{
if (!(entity->u.comclass.progid = xmlstrdupW(&attr_value))) return FALSE;
}
else if (xmlstr_cmp(&attr_name, tlbidW))
{
if (!(entity->u.comclass.tlbid = xmlstrdupW(&attr_value))) return FALSE;
}
else if (xmlstr_cmp(&attr_name, threadingmodelW))
{
entity->u.comclass.model = parse_com_class_threadingmodel(&attr_value);
}
else if (xmlstr_cmp(&attr_name, miscstatusW))
{
entity->u.comclass.miscstatus = parse_com_class_misc(&attr_value);
}
else if (xmlstr_cmp(&attr_name, miscstatuscontentW))
{
entity->u.comclass.miscstatuscontent = parse_com_class_misc(&attr_value);
}
else if (xmlstr_cmp(&attr_name, miscstatusthumbnailW))
{
entity->u.comclass.miscstatusthumbnail = parse_com_class_misc(&attr_value);
}
else if (xmlstr_cmp(&attr_name, miscstatusiconW))
{
entity->u.comclass.miscstatusicon = parse_com_class_misc(&attr_value);
}
else if (xmlstr_cmp(&attr_name, miscstatusdocprintW))
{
entity->u.comclass.miscstatusdocprint = parse_com_class_misc(&attr_value);
}
else
{
WARN("unknown attr %s=%s\n", debugstr_xmlstr(&attr_name), debugstr_xmlstr(&attr_value));
}
}
if (error || end) return end;
while ((ret = next_xml_elem(xmlbuf, &elem)))
{
if (xmlstr_cmp_end(&elem, comClassW))
{
ret = parse_end_element(xmlbuf);
break;
}
else
{
WARN("unknown elem %s\n", debugstr_xmlstr(&elem));
ret = parse_unknown_elem(xmlbuf, &elem);
}
}
return ret;
}
static BOOL parse_cominterface_proxy_stub_elem(xmlbuf_t* xmlbuf, struct dll_redirect* dll)
{
xmlstr_t attr_name, attr_value;
BOOL end = FALSE, error;
struct entity* entity;
if (!(entity = add_entity(&dll->entities, ACTIVATION_CONTEXT_SECTION_COM_INTERFACE_REDIRECTION)))
return FALSE;
while (next_xml_attr(xmlbuf, &attr_name, &attr_value, &error, &end))
{
if (xmlstr_cmp(&attr_name, iidW))
{
if (!(entity->u.proxy.iid = xmlstrdupW(&attr_value))) return FALSE;
}
if (xmlstr_cmp(&attr_name, nameW))
{
if (!(entity->u.proxy.name = xmlstrdupW(&attr_value))) return FALSE;
}
else
{
WARN("unknown attr %s=%s\n", debugstr_xmlstr(&attr_name), debugstr_xmlstr(&attr_value));
}
}
if (error || end) return end;
return parse_expect_end_elem(xmlbuf, comInterfaceProxyStubW, asmv1W);
}
static BOOL parse_typelib_flags(const xmlstr_t *value, struct entity *entity)
{
WORD *flags = &entity->u.typelib.flags;
const WCHAR *str = value->ptr, *start;
int i = 0;
*flags = 0;
/* it's comma separated list of flags */
while (i < value->len)
{
start = str;
while (*str != ',' && (i++ < value->len)) str++;
if (!strncmpW(start, restrictedW, str-start))
*flags |= LIBFLAG_FRESTRICTED;
else if (!strncmpW(start, controlW, str-start))
*flags |= LIBFLAG_FCONTROL;
else if (!strncmpW(start, hiddenW, str-start))
*flags |= LIBFLAG_FHIDDEN;
else if (!strncmpW(start, hasdiskimageW, str-start))
*flags |= LIBFLAG_FHASDISKIMAGE;
else
{
WARN("unknown flags value %s\n", debugstr_xmlstr(value));
return FALSE;
}
/* skip separator */
str++;
i++;
}
return TRUE;
}
static BOOL parse_typelib_version(const xmlstr_t *str, struct entity *entity)
{
unsigned int ver[2];
unsigned int pos;
const WCHAR *curr;
/* major.minor */
ver[0] = ver[1] = pos = 0;
for (curr = str->ptr; curr < str->ptr + str->len; curr++)
{
if (*curr >= '0' && *curr <= '9')
{
ver[pos] = ver[pos] * 10 + *curr - '0';
if (ver[pos] >= 0x10000) goto error;
}
else if (*curr == '.')
{
if (++pos >= 2) goto error;
}
else goto error;
}
entity->u.typelib.major = ver[0];
entity->u.typelib.minor = ver[1];
return TRUE;
error:
FIXME("wrong typelib version value (%s)\n", debugstr_xmlstr(str));
return FALSE;
}
static BOOL parse_typelib_elem(xmlbuf_t* xmlbuf, struct dll_redirect* dll, struct actctx_loader* acl)
{
xmlstr_t attr_name, attr_value;
BOOL end = FALSE, error;
struct entity* entity;
if (!(entity = add_entity(&dll->entities, ACTIVATION_CONTEXT_SECTION_COM_TYPE_LIBRARY_REDIRECTION)))
return FALSE;
while (next_xml_attr(xmlbuf, &attr_name, &attr_value, &error, &end))
{
if (xmlstr_cmp(&attr_name, tlbidW))
{
if (!(entity->u.typelib.tlbid = xmlstrdupW(&attr_value))) return FALSE;
}
else if (xmlstr_cmp(&attr_name, versionW))
{
if (!parse_typelib_version(&attr_value, entity)) return FALSE;
}
else if (xmlstr_cmp(&attr_name, helpdirW))
{
if (!(entity->u.typelib.helpdir = xmlstrdupW(&attr_value))) return FALSE;
}
else if (xmlstr_cmp(&attr_name, flagsW))
{
if (!parse_typelib_flags(&attr_value, entity)) return FALSE;
}
else
{
WARN("unknown attr %s=%s\n", debugstr_xmlstr(&attr_name), debugstr_xmlstr(&attr_value));
}
}
if (error) return FALSE;
acl->actctx->sections |= TLIBREDIRECT_SECTION;
if (end) return TRUE;
return parse_expect_end_elem(xmlbuf, typelibW, asmv1W);
}
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, attr_name, attr_value;
BOOL end = FALSE, ret = TRUE, error;
struct entity* entity;
if (!(entity = add_entity(&dll->entities, ACTIVATION_CONTEXT_SECTION_WINDOW_CLASS_REDIRECTION)))
return FALSE;
entity->u.class.versioned = TRUE;
while (next_xml_attr(xmlbuf, &attr_name, &attr_value, &error, &end))
{
if (xmlstr_cmp(&attr_name, versionedW))
{
if (xmlstr_cmpi(&attr_value, noW))
entity->u.class.versioned = FALSE;
else if (!xmlstr_cmpi(&attr_value, yesW))
return FALSE;
}
else
{
WARN("unknown attr %s=%s\n", debugstr_xmlstr(&attr_name), debugstr_xmlstr(&attr_value));
}
}
if (error || end) return end;
if (!parse_text_content(xmlbuf, &content)) return FALSE;
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))
{
ret = parse_end_element(xmlbuf);
break;
}
else
{
WARN("unknown elem %s\n", debugstr_xmlstr(&elem));
ret = parse_unknown_elem(xmlbuf, &elem);
}
}
return ret;
}
static BOOL parse_binding_redirect_elem(xmlbuf_t* xmlbuf)
{
xmlstr_t attr_name, attr_value;
BOOL end = FALSE, error;
while (next_xml_attr(xmlbuf, &attr_name, &attr_value, &error, &end))
{
if (xmlstr_cmp(&attr_name, oldVersionW))
{
FIXME("Not stored yet oldVersion=%s\n", debugstr_xmlstr(&attr_value));
}
else if (xmlstr_cmp(&attr_name, newVersionW))
{
FIXME("Not stored yet newVersion=%s\n", debugstr_xmlstr(&attr_value));
}
else
{
WARN("unknown attr %s=%s\n", debugstr_xmlstr(&attr_name), debugstr_xmlstr(&attr_value));
}
}
if (error || end) return end;
return parse_expect_end_elem(xmlbuf, bindingRedirectW, asmv1W);
}
static BOOL parse_description_elem(xmlbuf_t* xmlbuf)
{
xmlstr_t elem, content;
BOOL end = FALSE, ret = TRUE;
if (!parse_expect_no_attr(xmlbuf, &end) || end ||
!parse_text_content(xmlbuf, &content))
return FALSE;
TRACE("Got description %s\n", debugstr_xmlstr(&content));
while (ret && (ret = next_xml_elem(xmlbuf, &elem)))
{
if (xmlstr_cmp_end(&elem, descriptionW))
{
ret = parse_end_element(xmlbuf);
break;
}
else
{
WARN("unknown elem %s\n", debugstr_xmlstr(&elem));
ret = parse_unknown_elem(xmlbuf, &elem);
}
}
return ret;
}
static BOOL parse_com_interface_external_proxy_stub_elem(xmlbuf_t* xmlbuf,
struct assembly* assembly)
{
xmlstr_t attr_name, attr_value;
BOOL end = FALSE, error;
struct entity* entity;
entity = add_entity(&assembly->entities, ACTIVATION_CONTEXT_SECTION_COM_INTERFACE_REDIRECTION);
if (!entity) return FALSE;
while (next_xml_attr(xmlbuf, &attr_name, &attr_value, &error, &end))
{
if (xmlstr_cmp(&attr_name, iidW))
{
if (!(entity->u.proxy.iid = xmlstrdupW(&attr_value))) return FALSE;
}
if (xmlstr_cmp(&attr_name, nameW))
{
if (!(entity->u.proxy.name = xmlstrdupW(&attr_value))) return FALSE;
}
else
{
WARN("unknown attr %s=%s\n", debugstr_xmlstr(&attr_name), debugstr_xmlstr(&attr_value));
}
}
if (error || end) return end;
return parse_expect_end_elem(xmlbuf, comInterfaceExternalProxyStubW, asmv1W);
}
static BOOL parse_clr_class_elem(xmlbuf_t* xmlbuf, struct assembly* assembly)
{
xmlstr_t attr_name, attr_value;
BOOL end = FALSE, error;
struct entity* entity;
entity = add_entity(&assembly->entities, ACTIVATION_CONTEXT_SECTION_COM_PROGID_REDIRECTION);
if (!entity) return FALSE;
while (next_xml_attr(xmlbuf, &attr_name, &attr_value, &error, &end))
{
if (xmlstr_cmp(&attr_name, nameW))
{
if (!(entity->u.clrclass.name = xmlstrdupW(&attr_value))) return FALSE;
}
else if (xmlstr_cmp(&attr_name, clsidW))
{
if (!(entity->u.clrclass.clsid = xmlstrdupW(&attr_value))) return FALSE;
}
else
{
WARN("unknown attr %s=%s\n", debugstr_xmlstr(&attr_name), debugstr_xmlstr(&attr_value));
}
}
if (error || end) return end;
return parse_expect_end_elem(xmlbuf, clrClassW, asmv1W);
}
static BOOL parse_clr_surrogate_elem(xmlbuf_t* xmlbuf, struct assembly* assembly)
{
xmlstr_t attr_name, attr_value;
BOOL end = FALSE, error;
struct entity* entity;
entity = add_entity(&assembly->entities, ACTIVATION_CONTEXT_SECTION_CLR_SURROGATES);
if (!entity) return FALSE;
while (next_xml_attr(xmlbuf, &attr_name, &attr_value, &error, &end))
{
if (xmlstr_cmp(&attr_name, nameW))
{
if (!(entity->u.clrsurrogate.name = xmlstrdupW(&attr_value))) return FALSE;
}
else if (xmlstr_cmp(&attr_name, clsidW))
{
if (!(entity->u.clrsurrogate.clsid = xmlstrdupW(&attr_value))) return FALSE;
}
else
{
WARN("unknown attr %s=%s\n", debugstr_xmlstr(&attr_name), debugstr_xmlstr(&attr_value));
}
}
if (error || end) return end;
return parse_expect_end_elem(xmlbuf, clrSurrogateW, asmv1W);
}
static BOOL parse_dependent_assembly_elem(xmlbuf_t* xmlbuf, struct actctx_loader* acl, BOOL optional)
{
struct assembly_identity ai;
xmlstr_t elem;
BOOL end = FALSE, ret = TRUE;
if (!parse_expect_no_attr(xmlbuf, &end) || end) return end;
memset(&ai, 0, sizeof(ai));
ai.optional = optional;
if (!parse_expect_elem(xmlbuf, assemblyIdentityW, asmv1W) ||
!parse_assembly_identity_elem(xmlbuf, acl->actctx, &ai))
return FALSE;
TRACE( "adding name=%s version=%s arch=%s\n",
debugstr_w(ai.name), debugstr_version(&ai.version), debugstr_w(ai.arch) );
/* store the newly found identity for later loading */
if (!add_dependent_assembly_id(acl, &ai)) return FALSE;
while (ret && (ret = next_xml_elem(xmlbuf, &elem)))
{
if (xmlstr_cmp_end(&elem, dependentAssemblyW))
{
ret = parse_end_element(xmlbuf);
break;
}
else if (xmlstr_cmp(&elem, bindingRedirectW))
{
ret = parse_binding_redirect_elem(xmlbuf);
}
else
{
WARN("unknown elem %s\n", debugstr_xmlstr(&elem));
ret = parse_unknown_elem(xmlbuf, &elem);
}
}
return ret;
}
static BOOL parse_dependency_elem(xmlbuf_t* xmlbuf, struct actctx_loader* acl)
{
xmlstr_t attr_name, attr_value, elem;
BOOL end = FALSE, ret = TRUE, error, optional = FALSE;
while (next_xml_attr(xmlbuf, &attr_name, &attr_value, &error, &end))
{
if (xmlstr_cmp(&attr_name, optionalW))
{
optional = xmlstr_cmpi( &attr_value, yesW );
TRACE("optional=%s\n", debugstr_xmlstr(&attr_value));
}
else
{
WARN("unknown attr %s=%s\n", debugstr_xmlstr(&attr_name), debugstr_xmlstr(&attr_value));
}
}
while (ret && (ret = next_xml_elem(xmlbuf, &elem)))
{
if (xmlstr_cmp_end(&elem, dependencyW))
{
ret = parse_end_element(xmlbuf);
break;
}
else if (xmlstr_cmp(&elem, dependentAssemblyW))
{
ret = parse_dependent_assembly_elem(xmlbuf, acl, optional);
}
else
{
WARN("unknown element %s\n", debugstr_xmlstr(&elem));
ret = parse_unknown_elem(xmlbuf, &elem);
}
}
return ret;
}
static BOOL parse_noinherit_elem(xmlbuf_t* xmlbuf)
{
BOOL end = FALSE;
if (!parse_expect_no_attr(xmlbuf, &end)) return FALSE;
return end || parse_expect_end_elem(xmlbuf, noInheritW, asmv1W);
}
static BOOL parse_noinheritable_elem(xmlbuf_t* xmlbuf)
{
BOOL end = FALSE;
if (!parse_expect_no_attr(xmlbuf, &end)) return FALSE;
return end || parse_expect_end_elem(xmlbuf, noInheritableW, asmv1W);
}
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;
struct dll_redirect* dll;
if (!(dll = add_dll_redirect(assembly))) return FALSE;
while (next_xml_attr(xmlbuf, &attr_name, &attr_value, &error, &end))
{
if (xmlstr_cmp(&attr_name, nameW))
{
if (!(dll->name = xmlstrdupW(&attr_value))) return FALSE;
TRACE("name=%s\n", debugstr_xmlstr(&attr_value));
}
else if (xmlstr_cmp(&attr_name, hashW))
{
if (!(dll->hash = xmlstrdupW(&attr_value))) return FALSE;
}
else if (xmlstr_cmp(&attr_name, hashalgW))
{
static const WCHAR sha1W[] = {'S','H','A','1',0};
if (!xmlstr_cmpi(&attr_value, sha1W))
FIXME("hashalg should be SHA1, got %s\n", debugstr_xmlstr(&attr_value));
}
else
{
WARN("unknown attr %s=%s\n", debugstr_xmlstr(&attr_name), debugstr_xmlstr(&attr_value));
}
}
if (error || !dll->name) return FALSE;
acl->actctx->sections |= DLLREDIRECT_SECTION;
if (end) return TRUE;
while (ret && (ret = next_xml_elem(xmlbuf, &elem)))
{
if (xmlstr_cmp_end(&elem, fileW))
{
ret = parse_end_element(xmlbuf);
break;
}
else if (xmlstr_cmp(&elem, comClassW))
{
ret = parse_com_class_elem(xmlbuf, dll);
}
else if (xmlstr_cmp(&elem, comInterfaceProxyStubW))
{
ret = parse_cominterface_proxy_stub_elem(xmlbuf, dll);
}
else if (xml_elem_cmp(&elem, hashW, asmv2W))
{
WARN("asmv2:hash (undocumented) not supported\n");
ret = parse_unknown_elem(xmlbuf, &elem);
}
else if (xmlstr_cmp(&elem, typelibW))
{
ret = parse_typelib_elem(xmlbuf, dll, acl);
}
else if (xmlstr_cmp(&elem, windowClassW))
{
ret = parse_window_class_elem(xmlbuf, dll, acl);
}
else
{
WARN("unknown elem %s\n", debugstr_xmlstr(&elem));
ret = parse_unknown_elem( xmlbuf, &elem );
}
}
return ret;
}
static BOOL parse_assembly_elem(xmlbuf_t* xmlbuf, struct actctx_loader* acl,
struct assembly* assembly,
struct assembly_identity* expected_ai)
{
xmlstr_t attr_name, attr_value, elem;
BOOL end = FALSE, error, version = FALSE, xmlns = FALSE, ret = TRUE;
TRACE("(%p)\n", xmlbuf);
while (next_xml_attr(xmlbuf, &attr_name, &attr_value, &error, &end))
{
if (xmlstr_cmp(&attr_name, manifestVersionW))
{
static const WCHAR v10W[] = {'1','.','0',0};
if (!xmlstr_cmp(&attr_value, v10W))
{
FIXME("wrong version %s\n", debugstr_xmlstr(&attr_value));
return FALSE;
}
version = TRUE;
}
else if (xmlstr_cmp(&attr_name, xmlnsW))
{
if (!xmlstr_cmp(&attr_value, manifestv1W) && !xmlstr_cmp(&attr_value, manifestv3W))
{
FIXME("wrong namespace %s\n", debugstr_xmlstr(&attr_value));
return FALSE;
}
xmlns = TRUE;
}
else
{
WARN("unknown attr %s=%s\n", debugstr_xmlstr(&attr_name), debugstr_xmlstr(&attr_value));
}
}
if (error || end || !xmlns || !version) return FALSE;
if (!next_xml_elem(xmlbuf, &elem)) return FALSE;
if (assembly->type == APPLICATION_MANIFEST && xmlstr_cmp(&elem, noInheritW))
{
if (!parse_noinherit_elem(xmlbuf) || !next_xml_elem(xmlbuf, &elem))
return FALSE;
assembly->no_inherit = TRUE;
}
if (xml_elem_cmp(&elem, noInheritableW, asmv1W))
{
if (!parse_noinheritable_elem(xmlbuf) || !next_xml_elem(xmlbuf, &elem))
return FALSE;
}
else if ((assembly->type == ASSEMBLY_MANIFEST || assembly->type == ASSEMBLY_SHARED_MANIFEST) &&
assembly->no_inherit)
return FALSE;
while (ret)
{
if (xml_elem_cmp_end(&elem, assemblyW, asmv1W))
{
ret = parse_end_element(xmlbuf);
break;
}
else if (xml_elem_cmp(&elem, descriptionW, asmv1W))
{
ret = parse_description_elem(xmlbuf);
}
else if (xml_elem_cmp(&elem, comInterfaceExternalProxyStubW, asmv1W))
{
ret = parse_com_interface_external_proxy_stub_elem(xmlbuf, assembly);
}
else if (xml_elem_cmp(&elem, dependencyW, asmv1W))
{
ret = parse_dependency_elem(xmlbuf, acl);
}
else if (xml_elem_cmp(&elem, fileW, asmv1W))
{
ret = parse_file_elem(xmlbuf, assembly, acl);
}
else if (xml_elem_cmp(&elem, clrClassW, asmv1W))
{
ret = parse_clr_class_elem(xmlbuf, assembly);
}
else if (xml_elem_cmp(&elem, clrSurrogateW, asmv1W))
{
ret = parse_clr_surrogate_elem(xmlbuf, assembly);
}
else if (xml_elem_cmp(&elem, assemblyIdentityW, asmv1W))
{
if (!parse_assembly_identity_elem(xmlbuf, acl->actctx, &assembly->id)) return FALSE;
if (expected_ai)
{
/* FIXME: more tests */
if (assembly->type == ASSEMBLY_MANIFEST &&
memcmp(&assembly->id.version, &expected_ai->version, sizeof(assembly->id.version)))
{
FIXME("wrong version for assembly manifest: %u.%u.%u.%u / %u.%u.%u.%u\n",
expected_ai->version.major, expected_ai->version.minor,
expected_ai->version.build, expected_ai->version.revision,
assembly->id.version.major, assembly->id.version.minor,
assembly->id.version.build, assembly->id.version.revision);
ret = FALSE;
}
else if (assembly->type == ASSEMBLY_SHARED_MANIFEST &&
(assembly->id.version.major != expected_ai->version.major ||
assembly->id.version.minor != expected_ai->version.minor ||
assembly->id.version.build < expected_ai->version.build ||
(assembly->id.version.build == expected_ai->version.build &&
assembly->id.version.revision < expected_ai->version.revision)))
{
FIXME("wrong version for shared assembly manifest\n");
ret = FALSE;
}
}
}
else
{
WARN("unknown element %s\n", debugstr_xmlstr(&elem));
ret = parse_unknown_elem(xmlbuf, &elem);
}
if (ret) ret = next_xml_elem(xmlbuf, &elem);
}
return ret;
}
static NTSTATUS parse_manifest_buffer( struct actctx_loader* acl, struct assembly *assembly,
struct assembly_identity* ai, xmlbuf_t *xmlbuf )
{
xmlstr_t elem;
if (!next_xml_elem(xmlbuf, &elem)) return STATUS_SXS_CANT_GEN_ACTCTX;
if (xmlstr_cmp(&elem, xmlW) &&
(!parse_xml_header(xmlbuf) || !next_xml_elem(xmlbuf, &elem)))
return STATUS_SXS_CANT_GEN_ACTCTX;
if (!xml_elem_cmp(&elem, assemblyW, asmv1W))
{
FIXME("root element is %s, not <assembly>\n", debugstr_xmlstr(&elem));
return STATUS_SXS_CANT_GEN_ACTCTX;
}
if (!parse_assembly_elem(xmlbuf, acl, assembly, ai))
{
FIXME("failed to parse manifest %s\n", debugstr_w(assembly->manifest.info) );
return STATUS_SXS_CANT_GEN_ACTCTX;
}
if (next_xml_elem(xmlbuf, &elem))
{
FIXME("unexpected element %s\n", debugstr_xmlstr(&elem));
return STATUS_SXS_CANT_GEN_ACTCTX;
}
if (xmlbuf->ptr != xmlbuf->end)
{
FIXME("parse error\n");
return STATUS_SXS_CANT_GEN_ACTCTX;
}
return STATUS_SUCCESS;
}
static NTSTATUS parse_manifest( struct actctx_loader* acl, struct assembly_identity* ai,
LPCWSTR filename, LPCWSTR directory, BOOL shared,
const void *buffer, SIZE_T size )
{
xmlbuf_t xmlbuf;
NTSTATUS status;
struct assembly *assembly;
int unicode_tests;
TRACE( "parsing manifest loaded from %s base dir %s\n", debugstr_w(filename), debugstr_w(directory) );
if (!(assembly = add_assembly(acl->actctx, shared ? ASSEMBLY_SHARED_MANIFEST : ASSEMBLY_MANIFEST)))
return STATUS_SXS_CANT_GEN_ACTCTX;
if (directory && !(assembly->directory = strdupW(directory)))
return STATUS_NO_MEMORY;
if (filename) assembly->manifest.info = strdupW( filename + 4 /* skip \??\ prefix */ );
assembly->manifest.type = assembly->manifest.info ? ACTIVATION_CONTEXT_PATH_TYPE_WIN32_FILE
: ACTIVATION_CONTEXT_PATH_TYPE_NONE;
unicode_tests = IS_TEXT_UNICODE_SIGNATURE | IS_TEXT_UNICODE_REVERSE_SIGNATURE;
if (RtlIsTextUnicode( buffer, size, &unicode_tests ))
{
xmlbuf.ptr = buffer;
xmlbuf.end = xmlbuf.ptr + size / sizeof(WCHAR);
status = parse_manifest_buffer( acl, assembly, ai, &xmlbuf );
}
else if (unicode_tests & IS_TEXT_UNICODE_REVERSE_SIGNATURE)
{
const WCHAR *buf = buffer;
WCHAR *new_buff;
unsigned int i;
if (!(new_buff = RtlAllocateHeap( GetProcessHeap(), 0, size )))
return STATUS_NO_MEMORY;
for (i = 0; i < size / sizeof(WCHAR); i++)
new_buff[i] = RtlUshortByteSwap( buf[i] );
xmlbuf.ptr = new_buff;
xmlbuf.end = xmlbuf.ptr + size / sizeof(WCHAR);
status = parse_manifest_buffer( acl, assembly, ai, &xmlbuf );
RtlFreeHeap( GetProcessHeap(), 0, new_buff );
}
else
{
/* let's assume utf-8 for now */
int len = wine_utf8_mbstowcs( 0, buffer, size, NULL, 0 );
WCHAR *new_buff;
if (len == -1)
{
FIXME( "utf-8 conversion failed\n" );
return STATUS_SXS_CANT_GEN_ACTCTX;
}
if (!(new_buff = RtlAllocateHeap( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
return STATUS_NO_MEMORY;
wine_utf8_mbstowcs( 0, buffer, size, new_buff, len );
xmlbuf.ptr = new_buff;
xmlbuf.end = xmlbuf.ptr + len;
status = parse_manifest_buffer( acl, assembly, ai, &xmlbuf );
RtlFreeHeap( GetProcessHeap(), 0, new_buff );
}
return status;
}
static NTSTATUS open_nt_file( HANDLE *handle, UNICODE_STRING *name )
{
OBJECT_ATTRIBUTES attr;
IO_STATUS_BLOCK io;
attr.Length = sizeof(attr);
attr.RootDirectory = 0;
attr.Attributes = OBJ_CASE_INSENSITIVE;
attr.ObjectName = name;
attr.SecurityDescriptor = NULL;
attr.SecurityQualityOfService = NULL;
return NtOpenFile( handle, GENERIC_READ, &attr, &io, FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_ALERT );
}
static NTSTATUS get_module_filename( HMODULE module, UNICODE_STRING *str, unsigned int extra_len )
{
NTSTATUS status;
ULONG magic;
LDR_MODULE *pldr;
LdrLockLoaderLock(0, NULL, &magic);
status = LdrFindEntryForAddress( module, &pldr );
if (status == STATUS_SUCCESS)
{
if ((str->Buffer = RtlAllocateHeap( GetProcessHeap(), 0,
pldr->FullDllName.Length + extra_len + sizeof(WCHAR) )))
{
memcpy( str->Buffer, pldr->FullDllName.Buffer, pldr->FullDllName.Length + sizeof(WCHAR) );
str->Length = pldr->FullDllName.Length;
str->MaximumLength = pldr->FullDllName.Length + extra_len + sizeof(WCHAR);
}
else status = STATUS_NO_MEMORY;
}
LdrUnlockLoaderLock(0, magic);
return status;
}
static NTSTATUS get_manifest_in_module( struct actctx_loader* acl, struct assembly_identity* ai,
LPCWSTR filename, LPCWSTR directory, BOOL shared,
HANDLE hModule, LPCWSTR resname, ULONG lang )
{
NTSTATUS status;
UNICODE_STRING nameW;
LDR_RESOURCE_INFO info;
const IMAGE_RESOURCE_DATA_ENTRY* entry = NULL;
void *ptr;
if (TRACE_ON(actctx))
{
if (!filename && !get_module_filename( hModule, &nameW, 0 ))
{
TRACE( "looking for res %s in module %p %s\n", debugstr_w(resname),
hModule, debugstr_w(nameW.Buffer) );
RtlFreeUnicodeString( &nameW );
}
else TRACE( "looking for res %s in module %p %s\n", debugstr_w(resname),
hModule, debugstr_w(filename) );
}
if (!resname) return STATUS_INVALID_PARAMETER;
info.Type = RT_MANIFEST;
info.Language = lang;
if (!((ULONG_PTR)resname >> 16))
{
info.Name = (ULONG_PTR)resname;
status = LdrFindResource_U(hModule, &info, 3, &entry);
}
else if (resname[0] == '#')
{
ULONG value;
RtlInitUnicodeString(&nameW, resname + 1);
if (RtlUnicodeStringToInteger(&nameW, 10, &value) != STATUS_SUCCESS || HIWORD(value))
return STATUS_INVALID_PARAMETER;
info.Name = value;
status = LdrFindResource_U(hModule, &info, 3, &entry);
}
else
{
RtlCreateUnicodeString(&nameW, resname);
RtlUpcaseUnicodeString(&nameW, &nameW, FALSE);
info.Name = (ULONG_PTR)nameW.Buffer;
status = LdrFindResource_U(hModule, &info, 3, &entry);
RtlFreeUnicodeString(&nameW);
}
if (status == STATUS_SUCCESS) status = LdrAccessResource(hModule, entry, &ptr, NULL);
if (status == STATUS_SUCCESS)
status = parse_manifest(acl, ai, filename, directory, shared, ptr, entry->Size);
return status;
}
static NTSTATUS get_manifest_in_pe_file( struct actctx_loader* acl, struct assembly_identity* ai,
LPCWSTR filename, LPCWSTR directory, BOOL shared,
HANDLE file, LPCWSTR resname, ULONG lang )
{
HANDLE mapping;
OBJECT_ATTRIBUTES attr;
LARGE_INTEGER size;
LARGE_INTEGER offset;
NTSTATUS status;
SIZE_T count;
void *base;
TRACE( "looking for res %s in %s\n", debugstr_w(resname), debugstr_w(filename) );
attr.Length = sizeof(attr);
attr.RootDirectory = 0;
attr.ObjectName = NULL;
attr.Attributes = OBJ_CASE_INSENSITIVE | OBJ_OPENIF;
attr.SecurityDescriptor = NULL;
attr.SecurityQualityOfService = NULL;
size.QuadPart = 0;
status = NtCreateSection( &mapping, STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | SECTION_MAP_READ,
&attr, &size, PAGE_READONLY, SEC_COMMIT, file );
if (status != STATUS_SUCCESS) return status;
offset.QuadPart = 0;
count = 0;
base = NULL;
status = NtMapViewOfSection( mapping, GetCurrentProcess(), &base, 0, 0, &offset,
&count, ViewShare, 0, PAGE_READONLY );
NtClose( mapping );
if (status != STATUS_SUCCESS) return status;
if (RtlImageNtHeader(base)) /* we got a PE file */
{
HANDLE module = (HMODULE)((ULONG_PTR)base | 1); /* make it a LOAD_LIBRARY_AS_DATAFILE handle */
status = get_manifest_in_module( acl, ai, filename, directory, shared, module, resname, lang );
}
else status = STATUS_INVALID_IMAGE_FORMAT;
NtUnmapViewOfSection( GetCurrentProcess(), base );
return status;
}
static NTSTATUS get_manifest_in_manifest_file( struct actctx_loader* acl, struct assembly_identity* ai,
LPCWSTR filename, LPCWSTR directory, BOOL shared, HANDLE file )
{
FILE_END_OF_FILE_INFORMATION info;
IO_STATUS_BLOCK io;
HANDLE mapping;
OBJECT_ATTRIBUTES attr;
LARGE_INTEGER size;
LARGE_INTEGER offset;
NTSTATUS status;
SIZE_T count;
void *base;
TRACE( "loading manifest file %s\n", debugstr_w(filename) );
attr.Length = sizeof(attr);
attr.RootDirectory = 0;
attr.ObjectName = NULL;
attr.Attributes = OBJ_CASE_INSENSITIVE | OBJ_OPENIF;
attr.SecurityDescriptor = NULL;
attr.SecurityQualityOfService = NULL;
size.QuadPart = 0;
status = NtCreateSection( &mapping, STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | SECTION_MAP_READ,
&attr, &size, PAGE_READONLY, SEC_COMMIT, file );
if (status != STATUS_SUCCESS) return status;
offset.QuadPart = 0;
count = 0;
base = NULL;
status = NtMapViewOfSection( mapping, GetCurrentProcess(), &base, 0, 0, &offset,
&count, ViewShare, 0, PAGE_READONLY );
NtClose( mapping );
if (status != STATUS_SUCCESS) return status;
status = NtQueryInformationFile( file, &io, &info, sizeof(info), FileEndOfFileInformation );
if (status == STATUS_SUCCESS)
status = parse_manifest(acl, ai, filename, directory, shared, base, info.EndOfFile.QuadPart);
NtUnmapViewOfSection( GetCurrentProcess(), base );
return status;
}
/* try to load the .manifest file associated to the file */
static NTSTATUS get_manifest_in_associated_manifest( struct actctx_loader* acl, struct assembly_identity* ai,
LPCWSTR filename, LPCWSTR directory, HMODULE module, LPCWSTR resname )
{
static const WCHAR fmtW[] = { '.','%','l','u',0 };
WCHAR *buffer;
NTSTATUS status;
UNICODE_STRING nameW;
HANDLE file;
ULONG_PTR resid = CREATEPROCESS_MANIFEST_RESOURCE_ID;
if (!((ULONG_PTR)resname >> 16)) resid = (ULONG_PTR)resname & 0xffff;
TRACE( "looking for manifest associated with %s id %lu\n", debugstr_w(filename), resid );
if (module) /* use the module filename */
{
UNICODE_STRING name;
if (!(status = get_module_filename( module, &name, sizeof(dotManifestW) + 10*sizeof(WCHAR) )))
{
if (resid != 1) sprintfW( name.Buffer + strlenW(name.Buffer), fmtW, resid );
strcatW( name.Buffer, dotManifestW );
if (!RtlDosPathNameToNtPathName_U( name.Buffer, &nameW, NULL, NULL ))
status = STATUS_RESOURCE_DATA_NOT_FOUND;
RtlFreeUnicodeString( &name );
}
if (status) return status;
}
else
{
if (!(buffer = RtlAllocateHeap( GetProcessHeap(), 0,
(strlenW(filename) + 10) * sizeof(WCHAR) + sizeof(dotManifestW) )))
return STATUS_NO_MEMORY;
strcpyW( buffer, filename );
if (resid != 1) sprintfW( buffer + strlenW(buffer), fmtW, resid );
strcatW( buffer, dotManifestW );
RtlInitUnicodeString( &nameW, buffer );
}
if (!open_nt_file( &file, &nameW ))
{
status = get_manifest_in_manifest_file( acl, ai, nameW.Buffer, directory, FALSE, file );
NtClose( file );
}
else status = STATUS_RESOURCE_DATA_NOT_FOUND;
RtlFreeUnicodeString( &nameW );
return status;
}
static WCHAR *lookup_manifest_file( HANDLE dir, struct assembly_identity *ai )
{
static const WCHAR lookup_fmtW[] =
{'%','s','_','%','s','_','%','s','_','%','u','.','%','u','.','*','.','*','_',
'%','s','_','*','.','m','a','n','i','f','e','s','t',0};
static const WCHAR wine_trailerW[] = {'d','e','a','d','b','e','e','f','.','m','a','n','i','f','e','s','t'};
WCHAR *lookup, *ret = NULL;
UNICODE_STRING lookup_us;
IO_STATUS_BLOCK io;
const WCHAR *lang = ai->language;
unsigned int data_pos = 0, data_len;
char buffer[8192];
if (!(lookup = RtlAllocateHeap( GetProcessHeap(), 0,
(strlenW(ai->arch) + strlenW(ai->name)
+ strlenW(ai->public_key) + 20) * sizeof(WCHAR)
+ sizeof(lookup_fmtW) )))
return NULL;
if (!lang || !strcmpiW( lang, neutralW )) lang = wildcardW;
sprintfW( lookup, lookup_fmtW, ai->arch, ai->name, ai->public_key,
ai->version.major, ai->version.minor, lang );
RtlInitUnicodeString( &lookup_us, lookup );
NtQueryDirectoryFile( dir, 0, NULL, NULL, &io, buffer, sizeof(buffer),
FileBothDirectoryInformation, FALSE, &lookup_us, TRUE );
if (io.u.Status == STATUS_SUCCESS)
{
ULONG min_build = ai->version.build, min_revision = ai->version.revision;
FILE_BOTH_DIR_INFORMATION *dir_info;
WCHAR *tmp;
ULONG build, revision;
data_len = io.Information;
for (;;)
{
if (data_pos >= data_len)
{
NtQueryDirectoryFile( dir, 0, NULL, NULL, &io, buffer, sizeof(buffer),
FileBothDirectoryInformation, FALSE, &lookup_us, FALSE );
if (io.u.Status != STATUS_SUCCESS) break;
data_len = io.Information;
data_pos = 0;
}
dir_info = (FILE_BOTH_DIR_INFORMATION*)(buffer + data_pos);
if (dir_info->NextEntryOffset) data_pos += dir_info->NextEntryOffset;
else data_pos = data_len;
tmp = dir_info->FileName + (strchrW(lookup, '*') - lookup);
build = atoiW(tmp);
if (build < min_build) continue;
tmp = strchrW(tmp, '.') + 1;
revision = atoiW(tmp);
if (build == min_build && revision < min_revision) continue;
tmp = strchrW(tmp, '_') + 1;
tmp = strchrW(tmp, '_') + 1;
if (dir_info->FileNameLength - (tmp - dir_info->FileName) * sizeof(WCHAR) == sizeof(wine_trailerW) &&
!memicmpW( tmp, wine_trailerW, sizeof(wine_trailerW) / sizeof(WCHAR) ))
{
/* prefer a non-Wine manifest if we already have one */
/* we'll still load the builtin dll if specified through DllOverrides */
if (ret) continue;
}
else
{
min_build = build;
min_revision = revision;
}
ai->version.build = build;
ai->version.revision = revision;
RtlFreeHeap( GetProcessHeap(), 0, ret );
if ((ret = RtlAllocateHeap( GetProcessHeap(), 0, dir_info->FileNameLength + sizeof(WCHAR) )))
{
memcpy( ret, dir_info->FileName, dir_info->FileNameLength );
ret[dir_info->FileNameLength/sizeof(WCHAR)] = 0;
}
}
}
else WARN("no matching file for %s\n", debugstr_w(lookup));
RtlFreeHeap( GetProcessHeap(), 0, lookup );
return ret;
}
static NTSTATUS lookup_winsxs(struct actctx_loader* acl, struct assembly_identity* ai)
{
struct assembly_identity sxs_ai;
UNICODE_STRING path_us;
OBJECT_ATTRIBUTES attr;
IO_STATUS_BLOCK io;
WCHAR *path, *file = NULL;
HANDLE handle;
static const WCHAR manifest_dirW[] =
{'\\','w','i','n','s','x','s','\\','m','a','n','i','f','e','s','t','s',0};
if (!ai->arch || !ai->name || !ai->public_key) return STATUS_NO_SUCH_FILE;
if (!(path = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(manifest_dirW) +
strlenW(user_shared_data->NtSystemRoot) * sizeof(WCHAR) )))
return STATUS_NO_MEMORY;
strcpyW( path, user_shared_data->NtSystemRoot );
memcpy( path + strlenW(path), manifest_dirW, sizeof(manifest_dirW) );
if (!RtlDosPathNameToNtPathName_U( path, &path_us, NULL, NULL ))
{
RtlFreeHeap( GetProcessHeap(), 0, path );
return STATUS_NO_SUCH_FILE;
}
RtlFreeHeap( GetProcessHeap(), 0, path );
attr.Length = sizeof(attr);
attr.RootDirectory = 0;
attr.Attributes = OBJ_CASE_INSENSITIVE;
attr.ObjectName = &path_us;
attr.SecurityDescriptor = NULL;
attr.SecurityQualityOfService = NULL;
if (!NtOpenFile( &handle, GENERIC_READ, &attr, &io, FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT ))
{
sxs_ai = *ai;
file = lookup_manifest_file( handle, &sxs_ai );
NtClose( handle );
}
if (!file)
{
RtlFreeUnicodeString( &path_us );
return STATUS_NO_SUCH_FILE;
}
/* append file name to directory path */
if (!(path = RtlReAllocateHeap( GetProcessHeap(), 0, path_us.Buffer,
path_us.Length + (strlenW(file) + 2) * sizeof(WCHAR) )))
{
RtlFreeHeap( GetProcessHeap(), 0, file );
RtlFreeUnicodeString( &path_us );
return STATUS_NO_MEMORY;
}
path[path_us.Length/sizeof(WCHAR)] = '\\';
strcpyW( path + path_us.Length/sizeof(WCHAR) + 1, file );
RtlInitUnicodeString( &path_us, path );
*strrchrW(file, '.') = 0; /* remove .manifest extension */
if (!open_nt_file( &handle, &path_us ))
{
io.u.Status = get_manifest_in_manifest_file(acl, &sxs_ai, path_us.Buffer, file, TRUE, handle);
NtClose( handle );
}
else io.u.Status = STATUS_NO_SUCH_FILE;
RtlFreeHeap( GetProcessHeap(), 0, file );
RtlFreeUnicodeString( &path_us );
return io.u.Status;
}
static NTSTATUS lookup_assembly(struct actctx_loader* acl,
struct assembly_identity* ai)
{
static const WCHAR dotDllW[] = {'.','d','l','l',0};
unsigned int i;
WCHAR *buffer, *p, *directory;
NTSTATUS status;
UNICODE_STRING nameW;
HANDLE file;
TRACE( "looking for name=%s version=%s arch=%s\n",
debugstr_w(ai->name), debugstr_version(&ai->version), debugstr_w(ai->arch) );
if ((status = lookup_winsxs(acl, ai)) != STATUS_NO_SUCH_FILE) return status;
/* FIXME: add support for language specific lookup */
nameW.Buffer = NULL;
if (!(buffer = RtlAllocateHeap( GetProcessHeap(), 0,
(strlenW(acl->actctx->appdir.info) + 2 * strlenW(ai->name) + 2) * sizeof(WCHAR) + sizeof(dotManifestW) )))
return STATUS_NO_MEMORY;
if (!(directory = build_assembly_dir( ai )))
{
RtlFreeHeap( GetProcessHeap(), 0, buffer );
return STATUS_NO_MEMORY;
}
/* lookup in appdir\name.dll
* appdir\name.manifest
* appdir\name\name.dll
* appdir\name\name.manifest
*/
strcpyW( buffer, acl->actctx->appdir.info );
p = buffer + strlenW(buffer);
for (i = 0; i < 2; i++)
{
*p++ = '\\';
strcpyW( p, ai->name );
p += strlenW(p);
strcpyW( p, dotDllW );
if (RtlDosPathNameToNtPathName_U( buffer, &nameW, NULL, NULL ))
{
status = open_nt_file( &file, &nameW );
if (!status)
{
status = get_manifest_in_pe_file( acl, ai, nameW.Buffer, directory, FALSE, file,
(LPCWSTR)CREATEPROCESS_MANIFEST_RESOURCE_ID, 0 );
NtClose( file );
break;
}
RtlFreeUnicodeString( &nameW );
}
strcpyW( p, dotManifestW );
if (RtlDosPathNameToNtPathName_U( buffer, &nameW, NULL, NULL ))
{
status = open_nt_file( &file, &nameW );
if (!status)
{
status = get_manifest_in_manifest_file( acl, ai, nameW.Buffer, directory, FALSE, file );
NtClose( file );
break;
}
RtlFreeUnicodeString( &nameW );
}
status = STATUS_SXS_ASSEMBLY_NOT_FOUND;
}
RtlFreeUnicodeString( &nameW );
RtlFreeHeap( GetProcessHeap(), 0, directory );
RtlFreeHeap( GetProcessHeap(), 0, buffer );
return status;
}
static NTSTATUS parse_depend_manifests(struct actctx_loader* acl)
{
NTSTATUS status = STATUS_SUCCESS;
unsigned int i;
for (i = 0; i < acl->num_dependencies; i++)
{
if (lookup_assembly(acl, &acl->dependencies[i]) != STATUS_SUCCESS)
{
if (!acl->dependencies[i].optional)
{
FIXME( "Could not find dependent assembly %s (%s)\n",
debugstr_w(acl->dependencies[i].name),
debugstr_version(&acl->dependencies[i].version) );
status = STATUS_SXS_CANT_GEN_ACTCTX;
break;
}
}
}
/* FIXME should now iterate through all refs */
return status;
}
/* find the appropriate activation context for RtlQueryInformationActivationContext */
static NTSTATUS find_query_actctx( HANDLE *handle, DWORD flags, ULONG class )
{
NTSTATUS status = STATUS_SUCCESS;
if (flags & QUERY_ACTCTX_FLAG_USE_ACTIVE_ACTCTX)
{
if (*handle) return STATUS_INVALID_PARAMETER;
if (NtCurrentTeb()->ActivationContextStack.ActiveFrame)
*handle = NtCurrentTeb()->ActivationContextStack.ActiveFrame->ActivationContext;
}
else if (flags & (QUERY_ACTCTX_FLAG_ACTCTX_IS_ADDRESS|QUERY_ACTCTX_FLAG_ACTCTX_IS_HMODULE))
{
ULONG magic;
LDR_MODULE *pldr;
if (!*handle) return STATUS_INVALID_PARAMETER;
LdrLockLoaderLock( 0, NULL, &magic );
if (!LdrFindEntryForAddress( *handle, &pldr ))
{
if ((flags & QUERY_ACTCTX_FLAG_ACTCTX_IS_HMODULE) && *handle != pldr->BaseAddress)
status = STATUS_DLL_NOT_FOUND;
else
*handle = pldr->ActivationContext;
}
else status = STATUS_DLL_NOT_FOUND;
LdrUnlockLoaderLock( 0, magic );
}
else if (!*handle && (class != ActivationContextBasicInformation))
*handle = process_actctx;
return status;
}
static NTSTATUS build_dllredirect_section(ACTIVATION_CONTEXT* actctx, struct strsection_header **section)
{
unsigned int i, j, total_len = 0, dll_count = 0;
struct strsection_header *header;
struct dllredirect_data *data;
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];
/* each entry needs index, data and string data */
total_len += sizeof(*index);
total_len += sizeof(*data);
total_len += aligned_string_len((strlenW(dll->name)+1)*sizeof(WCHAR));
}
dll_count += assembly->num_dlls;
}
total_len += sizeof(*header);
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 = dll_count;
header->index_offset = sizeof(*header);
index = (struct string_index*)((BYTE*)header + header->index_offset);
name_offset = header->index_offset + header->count*sizeof(*index);
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];
UNICODE_STRING str;
WCHAR *ptrW;
/* setup new index entry */
str.Buffer = dll->name;
str.Length = strlenW(dll->name)*sizeof(WCHAR);
str.MaximumLength = str.Length + sizeof(WCHAR);
/* hash original 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;
/* setup data */
data = (struct dllredirect_data*)((BYTE*)header + index->data_offset);
data->size = sizeof(*data);
data->unk = 2; /* FIXME: seems to be constant */
memset(data->res, 0, sizeof(data->res));
/* dll name */
ptrW = (WCHAR*)((BYTE*)header + index->name_offset);
memcpy(ptrW, dll->name, index->name_len);
ptrW[index->name_len/sizeof(WCHAR)] = 0;
name_offset += sizeof(*data) + aligned_string_len(str.MaximumLength);
index++;
}
}
*section = header;
return STATUS_SUCCESS;
}
static struct string_index *find_string_index(const struct strsection_header *section, const UNICODE_STRING *name)
{
struct string_index *iter, *index = NULL;
ULONG hash = 0, i;
RtlHashUnicodeString(name, TRUE, HASH_STRING_ALGORITHM_X65599, &hash);
iter = (struct string_index*)((BYTE*)section + section->index_offset);
for (i = 0; i < section->count; i++)
{
if (iter->hash == hash)
{
const WCHAR *nameW = (WCHAR*)((BYTE*)section + iter->name_offset);
if (!strcmpiW(nameW, name->Buffer))
{
index = iter;
break;
}
else
WARN("hash collision 0x%08x, %s, %s\n", hash, debugstr_us(name), debugstr_w(nameW));
}
iter++;
}
return index;
}
static struct guid_index *find_guid_index(const struct guidsection_header *section, const GUID *guid)
{
struct guid_index *iter, *index = NULL;
ULONG i;
iter = (struct guid_index*)((BYTE*)section + section->index_offset);
for (i = 0; i < section->count; i++)
{
if (!memcmp(guid, &iter->guid, sizeof(*guid)))
{
index = iter;
break;
}
iter++;
}
return index;
}
static inline struct dllredirect_data *get_dllredirect_data(ACTIVATION_CONTEXT *ctxt, struct string_index *index)
{
return (struct dllredirect_data*)((BYTE*)ctxt->dllredirect_section + index->data_offset);
}
static NTSTATUS find_dll_redirection(ACTIVATION_CONTEXT* actctx, const UNICODE_STRING *name,
PACTCTX_SECTION_KEYED_DATA data)
{
struct dllredirect_data *dll;
struct string_index *index;
if (!(actctx->sections & DLLREDIRECT_SECTION)) return STATUS_SXS_KEY_NOT_FOUND;
if (!actctx->dllredirect_section)
{
struct strsection_header *section;
NTSTATUS status = build_dllredirect_section(actctx, &section);
if (status) return status;
if (interlocked_cmpxchg_ptr((void**)&actctx->dllredirect_section, section, NULL))
RtlFreeHeap(GetProcessHeap(), 0, section);
}
index = find_string_index(actctx->dllredirect_section, name);
if (!index) return STATUS_SXS_KEY_NOT_FOUND;
dll = get_dllredirect_data(actctx, index);
data->ulDataFormatVersion = 1;
data->lpData = dll;
data->ulLength = dll->size;
data->lpSectionGlobalData = NULL;
data->ulSectionGlobalDataLength = 0;
data->lpSectionBase = actctx->dllredirect_section;
data->ulSectionTotalLength = RtlSizeHeap( GetProcessHeap(), 0, actctx->dllredirect_section );
data->hActCtx = NULL;
if (data->cbSize >= FIELD_OFFSET(ACTCTX_SECTION_KEYED_DATA, ulAssemblyRosterIndex) + sizeof(ULONG))
data->ulAssemblyRosterIndex = index->rosterindex;
return STATUS_SUCCESS;
}
static inline struct string_index *get_wndclass_first_index(ACTIVATION_CONTEXT *actctx)
{
return (struct string_index*)((BYTE*)actctx->wndclass_section + actctx->wndclass_section->index_offset);
}
static inline struct wndclass_redirect_data *get_wndclass_data(ACTIVATION_CONTEXT *ctxt, struct string_index *index)
{
return (struct wndclass_redirect_data*)((BYTE*)ctxt->wndclass_section + index->data_offset);
}
static NTSTATUS build_wndclass_section(ACTIVATION_CONTEXT* actctx, struct strsection_header **section)
{
unsigned int i, j, k, total_len = 0, class_count = 0;
struct wndclass_redirect_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];
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) + 1;
int len;
/* each class entry needs index, data and string data */
total_len += sizeof(*index);
total_len += sizeof(*data);
/* original name is stored separately */
total_len += aligned_string_len(class_len*sizeof(WCHAR));
/* versioned name and module name are stored one after another */
if (entity->u.class.versioned)
len = get_assembly_version(assembly, NULL) + class_len + 1 /* '!' separator */;
else
len = class_len;
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 = STRSECTION_MAGIC;
header->size = sizeof(*header);
header->count = class_count;
header->index_offset = sizeof(*header);
index = (struct string_index*)((BYTE*)header + header->index_offset);
name_offset = header->index_offset + header->count*sizeof(*index);
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)
{
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 */
if (entity->u.class.versioned)
versioned_len = (get_assembly_version(assembly, NULL) + 1)*sizeof(WCHAR) + str.Length;
else
versioned_len = 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 = i + 1;
/* 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);
if (entity->u.class.versioned)
{
get_assembly_version(assembly, ptrW);
strcatW(ptrW, exclW);
strcatW(ptrW, entity->u.class.name);
}
else
{
memcpy(ptrW, entity->u.class.name, index->name_len);
ptrW[index->name_len/sizeof(WCHAR)] = 0;
}
name_offset += sizeof(*data);
name_offset += aligned_string_len(str.MaximumLength) + aligned_string_len(versioned_len + module_len + 2*sizeof(WCHAR));
index++;
}
}
}
}
*section = header;
return STATUS_SUCCESS;
}
static NTSTATUS find_window_class(ACTIVATION_CONTEXT* actctx, const UNICODE_STRING *name,
PACTCTX_SECTION_KEYED_DATA data)
{
struct string_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 strsection_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 build_tlib_section(ACTIVATION_CONTEXT* actctx, struct guidsection_header **section)
{
unsigned int i, j, k, total_len = 0, tlib_count = 0, names_len = 0;
struct guidsection_header *header;
ULONG module_offset, data_offset;
struct tlibredirect_data *data;
struct guid_index *index;
/* 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_COM_TYPE_LIBRARY_REDIRECTION)
{
/* each entry needs index, data and string data for module name and help string */
total_len += sizeof(*index);
total_len += sizeof(*data);
/* help string is stored separately */
if (*entity->u.typelib.helpdir)
total_len += aligned_string_len((strlenW(entity->u.typelib.helpdir)+1)*sizeof(WCHAR));
/* module names are packed one after another */
names_len += (strlenW(dll->name)+1)*sizeof(WCHAR);
tlib_count++;
}
}
}
}
total_len += aligned_string_len(names_len);
total_len += sizeof(*header);
header = RtlAllocateHeap(GetProcessHeap(), 0, total_len);
if (!header) return STATUS_NO_MEMORY;
memset(header, 0, sizeof(*header));
header->magic = GUIDSECTION_MAGIC;
header->size = sizeof(*header);
header->count = tlib_count;
header->index_offset = sizeof(*header) + aligned_string_len(names_len);
index = (struct guid_index*)((BYTE*)header + header->index_offset);
module_offset = sizeof(*header);
data_offset = header->index_offset + tlib_count*sizeof(*index);
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_COM_TYPE_LIBRARY_REDIRECTION)
{
ULONG module_len, help_len;
UNICODE_STRING str;
WCHAR *ptrW;
if (*entity->u.typelib.helpdir)
help_len = strlenW(entity->u.typelib.helpdir)*sizeof(WCHAR);
else
help_len = 0;
module_len = strlenW(dll->name)*sizeof(WCHAR);
/* setup new index entry */
RtlInitUnicodeString(&str, entity->u.typelib.tlbid);
RtlGUIDFromString(&str, &index->guid);
index->data_offset = data_offset;
index->data_len = sizeof(*data) + aligned_string_len(help_len);
index->rosterindex = i + 1;
/* setup data */
data = (struct tlibredirect_data*)((BYTE*)header + index->data_offset);
data->size = sizeof(*data);
data->res = 0;
data->name_len = module_len;
data->name_offset = module_offset;
/* FIXME: resourceid handling is really weird, and it doesn't seem to be useful */
data->langid = 0;
data->flags = entity->u.typelib.flags;
data->help_len = help_len;
data->help_offset = sizeof(*data);
data->major_version = entity->u.typelib.major;
data->minor_version = entity->u.typelib.minor;
/* module name */
ptrW = (WCHAR*)((BYTE*)header + data->name_offset);
memcpy(ptrW, dll->name, data->name_len);
ptrW[data->name_len/sizeof(WCHAR)] = 0;
/* help string */
if (data->help_len)
{
ptrW = (WCHAR*)((BYTE*)data + data->help_offset);
memcpy(ptrW, entity->u.typelib.helpdir, data->help_len);
ptrW[data->help_len/sizeof(WCHAR)] = 0;
}
data_offset += sizeof(*data);
if (help_len)
data_offset += aligned_string_len(help_len + sizeof(WCHAR));
module_offset += module_len + sizeof(WCHAR);
index++;
}
}
}
}
*section = header;
return STATUS_SUCCESS;
}
static inline struct tlibredirect_data *get_tlib_data(ACTIVATION_CONTEXT *actctx, struct guid_index *index)
{
return (struct tlibredirect_data*)((BYTE*)actctx->tlib_section + index->data_offset);
}
static NTSTATUS find_tlib_redirection(ACTIVATION_CONTEXT* actctx, const GUID *guid, ACTCTX_SECTION_KEYED_DATA* data)
{
struct guid_index *index = NULL;
struct tlibredirect_data *tlib;
if (!(actctx->sections & TLIBREDIRECT_SECTION)) return STATUS_SXS_KEY_NOT_FOUND;
if (!actctx->tlib_section)
{
struct guidsection_header *section;
NTSTATUS status = build_tlib_section(actctx, &section);
if (status) return status;
if (interlocked_cmpxchg_ptr((void**)&actctx->tlib_section, section, NULL))
RtlFreeHeap(GetProcessHeap(), 0, section);
}
index = find_guid_index(actctx->tlib_section, guid);
if (!index) return STATUS_SXS_KEY_NOT_FOUND;
tlib = get_tlib_data(actctx, index);
data->ulDataFormatVersion = 1;
data->lpData = tlib;
/* full length includes string length with nulls */
data->ulLength = tlib->size + tlib->help_len + sizeof(WCHAR);
data->lpSectionGlobalData = NULL;
data->ulSectionGlobalDataLength = 0;
data->lpSectionBase = actctx->tlib_section;
data->ulSectionTotalLength = RtlSizeHeap( GetProcessHeap(), 0, actctx->tlib_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,
const UNICODE_STRING *section_name,
DWORD flags, PACTCTX_SECTION_KEYED_DATA data)
{
NTSTATUS status;
switch (section_kind)
{
case ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION:
status = find_dll_redirection(actctx, section_name, data);
break;
case ACTIVATION_CONTEXT_SECTION_WINDOW_CLASS_REDIRECTION:
status = find_window_class(actctx, section_name, data);
break;
case ACTIVATION_CONTEXT_SECTION_COM_PROGID_REDIRECTION:
case ACTIVATION_CONTEXT_SECTION_GLOBAL_OBJECT_RENAME_TABLE:
case ACTIVATION_CONTEXT_SECTION_CLR_SURROGATES:
FIXME("Unsupported yet section_kind %x\n", section_kind);
return STATUS_SXS_SECTION_NOT_FOUND;
default:
WARN("Unknown section_kind %x\n", section_kind);
return STATUS_SXS_SECTION_NOT_FOUND;
}
if (status != STATUS_SUCCESS) return status;
if (flags & FIND_ACTCTX_SECTION_KEY_RETURN_HACTCTX)
{
actctx_addref(actctx);
data->hActCtx = actctx;
}
return STATUS_SUCCESS;
}
static NTSTATUS find_guid(ACTIVATION_CONTEXT* actctx, ULONG section_kind,
const GUID *guid, DWORD flags, PACTCTX_SECTION_KEYED_DATA data)
{
NTSTATUS status;
switch (section_kind)
{
case ACTIVATION_CONTEXT_SECTION_COM_TYPE_LIBRARY_REDIRECTION:
status = find_tlib_redirection(actctx, guid, data);
break;
case ACTIVATION_CONTEXT_SECTION_COM_SERVER_REDIRECTION:
case ACTIVATION_CONTEXT_SECTION_COM_INTERFACE_REDIRECTION:
FIXME("Unsupported yet section_kind %x\n", section_kind);
return STATUS_SXS_SECTION_NOT_FOUND;
default:
WARN("Unknown section_kind %x\n", section_kind);
return STATUS_SXS_SECTION_NOT_FOUND;
}
if (status != STATUS_SUCCESS) return status;
if (flags & FIND_ACTCTX_SECTION_KEY_RETURN_HACTCTX)
{
actctx_addref(actctx);
data->hActCtx = actctx;
}
return STATUS_SUCCESS;
}
/* initialize the activation context for the current process */
void actctx_init(void)
{
ACTCTXW ctx;
HANDLE handle;
ctx.cbSize = sizeof(ctx);
ctx.lpSource = NULL;
ctx.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID | ACTCTX_FLAG_HMODULE_VALID;
ctx.hModule = NtCurrentTeb()->Peb->ImageBaseAddress;
ctx.lpResourceName = (LPCWSTR)CREATEPROCESS_MANIFEST_RESOURCE_ID;
if (!RtlCreateActivationContext( &handle, &ctx )) process_actctx = check_actctx(handle);
}
/***********************************************************************
* RtlCreateActivationContext (NTDLL.@)
*
* Create an activation context.
*
* FIXME: function signature/prototype is wrong
*/
NTSTATUS WINAPI RtlCreateActivationContext( HANDLE *handle, const void *ptr )
{
const ACTCTXW *pActCtx = ptr; /* FIXME: not the right structure */
const WCHAR *directory = NULL;
ACTIVATION_CONTEXT *actctx;
UNICODE_STRING nameW;
ULONG lang = 0;
NTSTATUS status = STATUS_NO_MEMORY;
HANDLE file = 0;
struct actctx_loader acl;
TRACE("%p %08x\n", pActCtx, pActCtx ? pActCtx->dwFlags : 0);
if (!pActCtx || pActCtx->cbSize < sizeof(*pActCtx) ||
(pActCtx->dwFlags & ~ACTCTX_FLAGS_ALL))
return STATUS_INVALID_PARAMETER;
if (!(actctx = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*actctx) )))
return STATUS_NO_MEMORY;
actctx->magic = ACTCTX_MAGIC;
actctx->ref_count = 1;
actctx->config.type = ACTIVATION_CONTEXT_PATH_TYPE_NONE;
actctx->config.info = NULL;
actctx->appdir.type = ACTIVATION_CONTEXT_PATH_TYPE_WIN32_FILE;
if (pActCtx->dwFlags & ACTCTX_FLAG_APPLICATION_NAME_VALID)
{
if (!(actctx->appdir.info = strdupW( pActCtx->lpApplicationName ))) goto error;
}
else
{
UNICODE_STRING dir;
WCHAR *p;
HMODULE module;
if (pActCtx->dwFlags & ACTCTX_FLAG_HMODULE_VALID) module = pActCtx->hModule;
else module = NtCurrentTeb()->Peb->ImageBaseAddress;
if ((status = get_module_filename( module, &dir, 0 ))) goto error;
if ((p = strrchrW( dir.Buffer, '\\' ))) p[1] = 0;
actctx->appdir.info = dir.Buffer;
}
nameW.Buffer = NULL;
if (pActCtx->lpSource)
{
if (!RtlDosPathNameToNtPathName_U(pActCtx->lpSource, &nameW, NULL, NULL))
{
status = STATUS_NO_SUCH_FILE;
goto error;
}
status = open_nt_file( &file, &nameW );
if (status)
{
RtlFreeUnicodeString( &nameW );
goto error;
}
}
acl.actctx = actctx;
acl.dependencies = NULL;
acl.num_dependencies = 0;
acl.allocated_dependencies = 0;
if (pActCtx->dwFlags & ACTCTX_FLAG_LANGID_VALID) lang = pActCtx->wLangId;
if (pActCtx->dwFlags & ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID) directory = pActCtx->lpAssemblyDirectory;
if (pActCtx->dwFlags & ACTCTX_FLAG_RESOURCE_NAME_VALID)
{
/* if we have a resource it's a PE file */
if (pActCtx->dwFlags & ACTCTX_FLAG_HMODULE_VALID)
{
status = get_manifest_in_module( &acl, NULL, NULL, directory, FALSE, pActCtx->hModule,
pActCtx->lpResourceName, lang );
if (status && status != STATUS_SXS_CANT_GEN_ACTCTX)
/* FIXME: what to do if pActCtx->lpSource is set */
status = get_manifest_in_associated_manifest( &acl, NULL, NULL, directory,
pActCtx->hModule, pActCtx->lpResourceName );
}
else if (pActCtx->lpSource)
{
status = get_manifest_in_pe_file( &acl, NULL, nameW.Buffer, directory, FALSE,
file, pActCtx->lpResourceName, lang );
if (status && status != STATUS_SXS_CANT_GEN_ACTCTX)
status = get_manifest_in_associated_manifest( &acl, NULL, nameW.Buffer, directory,
NULL, pActCtx->lpResourceName );
}
else status = STATUS_INVALID_PARAMETER;
}
else
{
status = get_manifest_in_manifest_file( &acl, NULL, nameW.Buffer, directory, FALSE, file );
}
if (file) NtClose( file );
RtlFreeUnicodeString( &nameW );
if (status == STATUS_SUCCESS) status = parse_depend_manifests(&acl);
free_depend_manifests( &acl );
if (status == STATUS_SUCCESS) *handle = actctx;
else actctx_release( actctx );
return status;
error:
if (file) NtClose( file );
actctx_release( actctx );
return status;
}
/***********************************************************************
* RtlAddRefActivationContext (NTDLL.@)
*/
void WINAPI RtlAddRefActivationContext( HANDLE handle )
{
ACTIVATION_CONTEXT *actctx;
if ((actctx = check_actctx( handle ))) actctx_addref( actctx );
}
/******************************************************************
* RtlReleaseActivationContext (NTDLL.@)
*/
void WINAPI RtlReleaseActivationContext( HANDLE handle )
{
ACTIVATION_CONTEXT *actctx;
if ((actctx = check_actctx( handle ))) actctx_release( actctx );
}
/******************************************************************
* RtlActivateActivationContext (NTDLL.@)
*/
NTSTATUS WINAPI RtlActivateActivationContext( ULONG unknown, HANDLE handle, PULONG_PTR cookie )
{
RTL_ACTIVATION_CONTEXT_STACK_FRAME *frame;
if (!(frame = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*frame) )))
return STATUS_NO_MEMORY;
frame->Previous = NtCurrentTeb()->ActivationContextStack.ActiveFrame;
frame->ActivationContext = handle;
frame->Flags = 0;
NtCurrentTeb()->ActivationContextStack.ActiveFrame = frame;
RtlAddRefActivationContext( handle );
*cookie = (ULONG_PTR)frame;
TRACE( "%p cookie=%lx\n", handle, *cookie );
return STATUS_SUCCESS;
}
/***********************************************************************
* RtlDeactivateActivationContext (NTDLL.@)
*/
void WINAPI RtlDeactivateActivationContext( ULONG flags, ULONG_PTR cookie )
{
RTL_ACTIVATION_CONTEXT_STACK_FRAME *frame, *top;
TRACE( "%x cookie=%lx\n", flags, cookie );
/* find the right frame */
top = NtCurrentTeb()->ActivationContextStack.ActiveFrame;
for (frame = top; frame; frame = frame->Previous)
if ((ULONG_PTR)frame == cookie) break;
if (!frame)
RtlRaiseStatus( STATUS_SXS_INVALID_DEACTIVATION );
if (frame != top && !(flags & DEACTIVATE_ACTCTX_FLAG_FORCE_EARLY_DEACTIVATION))
RtlRaiseStatus( STATUS_SXS_EARLY_DEACTIVATION );
/* pop everything up to and including frame */
NtCurrentTeb()->ActivationContextStack.ActiveFrame = frame->Previous;
while (top != NtCurrentTeb()->ActivationContextStack.ActiveFrame)
{
frame = top->Previous;
RtlReleaseActivationContext( top->ActivationContext );
RtlFreeHeap( GetProcessHeap(), 0, top );
top = frame;
}
}
/******************************************************************
* RtlFreeThreadActivationContextStack (NTDLL.@)
*/
void WINAPI RtlFreeThreadActivationContextStack(void)
{
RTL_ACTIVATION_CONTEXT_STACK_FRAME *frame;
frame = NtCurrentTeb()->ActivationContextStack.ActiveFrame;
while (frame)
{
RTL_ACTIVATION_CONTEXT_STACK_FRAME *prev = frame->Previous;
RtlReleaseActivationContext( frame->ActivationContext );
RtlFreeHeap( GetProcessHeap(), 0, frame );
frame = prev;
}
NtCurrentTeb()->ActivationContextStack.ActiveFrame = NULL;
}
/******************************************************************
* RtlGetActiveActivationContext (NTDLL.@)
*/
NTSTATUS WINAPI RtlGetActiveActivationContext( HANDLE *handle )
{
if (NtCurrentTeb()->ActivationContextStack.ActiveFrame)
{
*handle = NtCurrentTeb()->ActivationContextStack.ActiveFrame->ActivationContext;
RtlAddRefActivationContext( *handle );
}
else
*handle = 0;
return STATUS_SUCCESS;
}
/******************************************************************
* RtlIsActivationContextActive (NTDLL.@)
*/
BOOLEAN WINAPI RtlIsActivationContextActive( HANDLE handle )
{
RTL_ACTIVATION_CONTEXT_STACK_FRAME *frame;
for (frame = NtCurrentTeb()->ActivationContextStack.ActiveFrame; frame; frame = frame->Previous)
if (frame->ActivationContext == handle) return TRUE;
return FALSE;
}
/***********************************************************************
* RtlQueryInformationActivationContext (NTDLL.@)
*
* Get information about an activation context.
* FIXME: function signature/prototype may be wrong
*/
NTSTATUS WINAPI RtlQueryInformationActivationContext( ULONG flags, HANDLE handle, PVOID subinst,
ULONG class, PVOID buffer,
SIZE_T bufsize, SIZE_T *retlen )
{
ACTIVATION_CONTEXT *actctx;
NTSTATUS status;
TRACE("%08x %p %p %u %p %ld %p\n", flags, handle,
subinst, class, buffer, bufsize, retlen);
if (retlen) *retlen = 0;
if ((status = find_query_actctx( &handle, flags, class ))) return status;
switch (class)
{
case ActivationContextBasicInformation:
{
ACTIVATION_CONTEXT_BASIC_INFORMATION *info = buffer;
if (retlen) *retlen = sizeof(*info);
if (!info || bufsize < sizeof(*info)) return STATUS_BUFFER_TOO_SMALL;
info->hActCtx = handle;
info->dwFlags = 0; /* FIXME */
if (!(flags & QUERY_ACTCTX_FLAG_NO_ADDREF)) RtlAddRefActivationContext( handle );
}
break;
case ActivationContextDetailedInformation:
{
ACTIVATION_CONTEXT_DETAILED_INFORMATION *acdi = buffer;
struct assembly *assembly = NULL;
SIZE_T len, manifest_len = 0, config_len = 0, appdir_len = 0;
LPWSTR ptr;
if (!(actctx = check_actctx(handle))) return STATUS_INVALID_PARAMETER;
if (actctx->num_assemblies) assembly = actctx->assemblies;
if (assembly && assembly->manifest.info)
manifest_len = strlenW(assembly->manifest.info) + 1;
if (actctx->config.info) config_len = strlenW(actctx->config.info) + 1;
if (actctx->appdir.info) appdir_len = strlenW(actctx->appdir.info) + 1;
len = sizeof(*acdi) + (manifest_len + config_len + appdir_len) * sizeof(WCHAR);
if (retlen) *retlen = len;
if (!buffer || bufsize < len) return STATUS_BUFFER_TOO_SMALL;
acdi->dwFlags = 0;
acdi->ulFormatVersion = assembly ? 1 : 0; /* FIXME */
acdi->ulAssemblyCount = actctx->num_assemblies;
acdi->ulRootManifestPathType = assembly ? assembly->manifest.type : 0 /* FIXME */;
acdi->ulRootManifestPathChars = assembly && assembly->manifest.info ? manifest_len - 1 : 0;
acdi->ulRootConfigurationPathType = actctx->config.type;
acdi->ulRootConfigurationPathChars = actctx->config.info ? config_len - 1 : 0;
acdi->ulAppDirPathType = actctx->appdir.type;
acdi->ulAppDirPathChars = actctx->appdir.info ? appdir_len - 1 : 0;
ptr = (LPWSTR)(acdi + 1);
if (manifest_len)
{
acdi->lpRootManifestPath = ptr;
memcpy(ptr, assembly->manifest.info, manifest_len * sizeof(WCHAR));
ptr += manifest_len;
}
else acdi->lpRootManifestPath = NULL;
if (config_len)
{
acdi->lpRootConfigurationPath = ptr;
memcpy(ptr, actctx->config.info, config_len * sizeof(WCHAR));
ptr += config_len;
}
else acdi->lpRootConfigurationPath = NULL;
if (appdir_len)
{
acdi->lpAppDirPath = ptr;
memcpy(ptr, actctx->appdir.info, appdir_len * sizeof(WCHAR));
}
else acdi->lpAppDirPath = NULL;
}
break;
case AssemblyDetailedInformationInActivationContext:
{
ACTIVATION_CONTEXT_ASSEMBLY_DETAILED_INFORMATION *afdi = buffer;
struct assembly *assembly;
WCHAR *assembly_id;
DWORD index;
SIZE_T len, id_len = 0, ad_len = 0, path_len = 0;
LPWSTR ptr;
if (!(actctx = check_actctx(handle))) return STATUS_INVALID_PARAMETER;
if (!subinst) return STATUS_INVALID_PARAMETER;
index = *(DWORD*)subinst;
if (!index || index > actctx->num_assemblies) return STATUS_INVALID_PARAMETER;
assembly = &actctx->assemblies[index - 1];
if (!(assembly_id = build_assembly_id( &assembly->id ))) return STATUS_NO_MEMORY;
id_len = strlenW(assembly_id) + 1;
if (assembly->directory) ad_len = strlenW(assembly->directory) + 1;
if (assembly->manifest.info &&
(assembly->type == ASSEMBLY_MANIFEST || assembly->type == ASSEMBLY_SHARED_MANIFEST))
path_len = strlenW(assembly->manifest.info) + 1;
len = sizeof(*afdi) + (id_len + ad_len + path_len) * sizeof(WCHAR);
if (retlen) *retlen = len;
if (!buffer || bufsize < len)
{
RtlFreeHeap( GetProcessHeap(), 0, assembly_id );
return STATUS_BUFFER_TOO_SMALL;
}
afdi->ulFlags = 0; /* FIXME */
afdi->ulEncodedAssemblyIdentityLength = (id_len - 1) * sizeof(WCHAR);
afdi->ulManifestPathType = assembly->manifest.type;
afdi->ulManifestPathLength = assembly->manifest.info ? (path_len - 1) * sizeof(WCHAR) : 0;
/* FIXME afdi->liManifestLastWriteTime = 0; */
afdi->ulPolicyPathType = ACTIVATION_CONTEXT_PATH_TYPE_NONE; /* FIXME */
afdi->ulPolicyPathLength = 0;
/* FIXME afdi->liPolicyLastWriteTime = 0; */
afdi->ulMetadataSatelliteRosterIndex = 0; /* FIXME */
afdi->ulManifestVersionMajor = 1;
afdi->ulManifestVersionMinor = 0;
afdi->ulPolicyVersionMajor = 0; /* FIXME */
afdi->ulPolicyVersionMinor = 0; /* FIXME */
afdi->ulAssemblyDirectoryNameLength = ad_len ? (ad_len - 1) * sizeof(WCHAR) : 0;
ptr = (LPWSTR)(afdi + 1);
afdi->lpAssemblyEncodedAssemblyIdentity = ptr;
memcpy( ptr, assembly_id, id_len * sizeof(WCHAR) );
ptr += id_len;
if (path_len)
{
afdi->lpAssemblyManifestPath = ptr;
memcpy(ptr, assembly->manifest.info, path_len * sizeof(WCHAR));
ptr += path_len;
} else afdi->lpAssemblyManifestPath = NULL;
afdi->lpAssemblyPolicyPath = NULL; /* FIXME */
if (ad_len)
{
afdi->lpAssemblyDirectoryName = ptr;
memcpy(ptr, assembly->directory, ad_len * sizeof(WCHAR));
}
else afdi->lpAssemblyDirectoryName = NULL;
RtlFreeHeap( GetProcessHeap(), 0, assembly_id );
}
break;
case FileInformationInAssemblyOfAssemblyInActivationContext:
{
const ACTIVATION_CONTEXT_QUERY_INDEX *acqi = subinst;
ASSEMBLY_FILE_DETAILED_INFORMATION *afdi = buffer;
struct assembly *assembly;
struct dll_redirect *dll;
SIZE_T len, dll_len = 0;
LPWSTR ptr;
if (!(actctx = check_actctx(handle))) return STATUS_INVALID_PARAMETER;
if (!acqi) return STATUS_INVALID_PARAMETER;
if (acqi->ulAssemblyIndex >= actctx->num_assemblies)
return STATUS_INVALID_PARAMETER;
assembly = &actctx->assemblies[acqi->ulAssemblyIndex];
if (acqi->ulFileIndexInAssembly >= assembly->num_dlls)
return STATUS_INVALID_PARAMETER;
dll = &assembly->dlls[acqi->ulFileIndexInAssembly];
if (dll->name) dll_len = strlenW(dll->name) + 1;
len = sizeof(*afdi) + dll_len * sizeof(WCHAR);
if (!buffer || bufsize < len)
{
if (retlen) *retlen = len;
return STATUS_BUFFER_TOO_SMALL;
}
if (retlen) *retlen = 0; /* yes that's what native does !! */
afdi->ulFlags = ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION;
afdi->ulFilenameLength = dll_len ? (dll_len - 1) * sizeof(WCHAR) : 0;
afdi->ulPathLength = 0; /* FIXME */
ptr = (LPWSTR)(afdi + 1);
if (dll_len)
{
afdi->lpFileName = ptr;
memcpy( ptr, dll->name, dll_len * sizeof(WCHAR) );
} else afdi->lpFileName = NULL;
afdi->lpFilePath = NULL; /* FIXME */
}
break;
default:
FIXME( "class %u not implemented\n", class );
return STATUS_NOT_IMPLEMENTED;
}
return STATUS_SUCCESS;
}
/***********************************************************************
* RtlFindActivationContextSectionString (NTDLL.@)
*
* Find information about a string in an activation context.
* FIXME: function signature/prototype may be wrong
*/
NTSTATUS WINAPI RtlFindActivationContextSectionString( ULONG flags, const GUID *guid, ULONG section_kind,
const UNICODE_STRING *section_name, PVOID ptr )
{
PACTCTX_SECTION_KEYED_DATA data = ptr;
NTSTATUS status = STATUS_SXS_KEY_NOT_FOUND;
TRACE("%08x %s %u %s %p\n", flags, debugstr_guid(guid), section_kind,
debugstr_us(section_name), data);
if (guid)
{
FIXME("expected guid == NULL\n");
return STATUS_INVALID_PARAMETER;
}
if (flags & ~FIND_ACTCTX_SECTION_KEY_RETURN_HACTCTX)
{
FIXME("unknown flags %08x\n", flags);
return STATUS_INVALID_PARAMETER;
}
if (!data || data->cbSize < offsetof(ACTCTX_SECTION_KEYED_DATA, ulAssemblyRosterIndex) ||
!section_name || !section_name->Buffer)
{
WARN("invalid parameter\n");
return STATUS_INVALID_PARAMETER;
}
if (NtCurrentTeb()->ActivationContextStack.ActiveFrame)
{
ACTIVATION_CONTEXT *actctx = check_actctx(NtCurrentTeb()->ActivationContextStack.ActiveFrame->ActivationContext);
if (actctx) status = find_string( actctx, section_kind, section_name, flags, data );
}
if (status != STATUS_SUCCESS)
status = find_string( process_actctx, section_kind, section_name, flags, data );
return status;
}
/***********************************************************************
* RtlFindActivationContextSectionGuid (NTDLL.@)
*
* Find information about a GUID in an activation context.
* FIXME: function signature/prototype may be wrong
*/
NTSTATUS WINAPI RtlFindActivationContextSectionGuid( ULONG flags, const GUID *extguid, ULONG section_kind,
const GUID *guid, void *ptr )
{
ACTCTX_SECTION_KEYED_DATA *data = ptr;
NTSTATUS status = STATUS_SXS_KEY_NOT_FOUND;
TRACE("%08x %s %u %s %p\n", flags, debugstr_guid(extguid), section_kind, debugstr_guid(guid), data);
if (extguid)
{
FIXME("expected extguid == NULL\n");
return STATUS_INVALID_PARAMETER;
}
if (flags & ~FIND_ACTCTX_SECTION_KEY_RETURN_HACTCTX)
{
FIXME("unknown flags %08x\n", flags);
return STATUS_INVALID_PARAMETER;
}
if (!data || data->cbSize < FIELD_OFFSET(ACTCTX_SECTION_KEYED_DATA, ulAssemblyRosterIndex) || !guid)
return STATUS_INVALID_PARAMETER;
if (NtCurrentTeb()->ActivationContextStack.ActiveFrame)
{
ACTIVATION_CONTEXT *actctx = check_actctx(NtCurrentTeb()->ActivationContextStack.ActiveFrame->ActivationContext);
if (actctx) status = find_guid( actctx, section_kind, guid, flags, data );
}
if (status != STATUS_SUCCESS)
status = find_guid( process_actctx, section_kind, guid, flags, data );
return status;
}