[SXS] Sync with Wine Staging 4.18. CORE-16441
[reactos.git] / dll / win32 / sxs / cache.c
index af19e60..361c057 100644 (file)
@@ -19,6 +19,9 @@
  */
 
 #include <stdarg.h>
+#ifdef __REACTOS__
+#include <wchar.h>
+#endif
 
 #define COBJMACROS
 #define INITGUID
 
 #include "wine/debug.h"
 #include "wine/list.h"
-#include "wine/unicode.h"
+#include "sxs_private.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(sxs);
 
-static inline WCHAR *strdupW( const WCHAR *s )
-{
-    WCHAR *t;
-    if (!s) return NULL;
-    if ((t = HeapAlloc( GetProcessHeap(), 0, (strlenW( s ) + 1) * sizeof(WCHAR) ))) strcpyW( t, s );
-    return t;
-}
+static const WCHAR cache_mutex_nameW[] =
+    {'_','_','W','I','N','E','_','S','X','S','_','C','A','C','H','E','_','M','U','T','E','X','_','_',0};
+
+static const WCHAR win32W[] = {'w','i','n','3','2',0};
+static const WCHAR win32_policyW[] = {'w','i','n','3','2','-','p','o','l','i','c','y',0};
+static const WCHAR backslashW[] = {'\\',0};
 
 struct cache
 {
     IAssemblyCache IAssemblyCache_iface;
     LONG refs;
+    HANDLE lock;
 };
 
 static inline struct cache *impl_from_IAssemblyCache(IAssemblyCache *iface)
@@ -68,7 +71,7 @@ static HRESULT WINAPI cache_QueryInterface(
     if (IsEqualIID(riid, &IID_IUnknown) ||
         IsEqualIID(riid, &IID_IAssemblyCache))
     {
-        IUnknown_AddRef( iface );
+        IAssemblyCache_AddRef( iface );
         *obj = cache;
         return S_OK;
     }
@@ -90,30 +93,183 @@ static ULONG WINAPI cache_Release( IAssemblyCache *iface )
     if (!refs)
     {
         TRACE("destroying %p\n", cache);
+        CloseHandle( cache->lock );
         HeapFree( GetProcessHeap(), 0, cache );
     }
     return refs;
 }
 
-static HRESULT WINAPI cache_UninstallAssembly(
-    IAssemblyCache *iface,
-    DWORD flags,
-    LPCWSTR name,
-    LPCFUSION_INSTALL_REFERENCE ref,
-    ULONG *disp )
+static unsigned int build_sxs_path( WCHAR *path )
 {
-    FIXME("%p, 0x%08x, %s, %p, %p\n", iface, flags, debugstr_w(name), ref, disp);
-    return E_NOTIMPL;
+    static const WCHAR winsxsW[] = {'\\','w','i','n','s','x','s','\\',0};
+    unsigned int len = GetWindowsDirectoryW( path, MAX_PATH );
+
+    memcpy( path + len, winsxsW, sizeof(winsxsW) );
+    return len + ARRAY_SIZE(winsxsW) - 1;
+}
+
+static WCHAR *build_assembly_name( const WCHAR *arch, const WCHAR *name, const WCHAR *token,
+                                   const WCHAR *version, unsigned int *len )
+{
+    static const WCHAR fmtW[] =
+        {'%','s','_','%','s','_','%','s','_','%','s','_','n','o','n','e','_','d','e','a','d','b','e','e','f',0};
+    unsigned int buflen = ARRAY_SIZE(fmtW);
+    WCHAR *ret;
+
+    buflen += lstrlenW( arch );
+    buflen += lstrlenW( name );
+    buflen += lstrlenW( token );
+    buflen += lstrlenW( version );
+    if (!(ret = HeapAlloc( GetProcessHeap(), 0, buflen * sizeof(WCHAR) ))) return NULL;
+    *len = swprintf( ret, fmtW, arch, name, token, version );
+    return _wcslwr( ret );
+}
+
+static WCHAR *build_manifest_path( const WCHAR *arch, const WCHAR *name, const WCHAR *token,
+                                   const WCHAR *version )
+{
+    static const WCHAR fmtW[] =
+        {'%','s','m','a','n','i','f','e','s','t','s','\\','%','s','.','m','a','n','i','f','e','s','t',0};
+    WCHAR *path = NULL, *ret, sxsdir[MAX_PATH];
+    unsigned int len;
+
+    if (!(path = build_assembly_name( arch, name, token, version, &len ))) return NULL;
+    len += ARRAY_SIZE(fmtW);
+    len += build_sxs_path( sxsdir );
+    if (!(ret = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
+    {
+        HeapFree( GetProcessHeap(), 0, path );
+        return NULL;
+    }
+    swprintf( ret, fmtW, sxsdir, path );
+    HeapFree( GetProcessHeap(), 0, path );
+    return ret;
+}
+
+static WCHAR *build_policy_name( const WCHAR *arch, const WCHAR *name, const WCHAR *token,
+                                 unsigned int *len )
+{
+    static const WCHAR fmtW[] =
+        {'%','s','_','%','s','_','%','s','_','n','o','n','e','_','d','e','a','d','b','e','e','f',0};
+    unsigned int buflen = ARRAY_SIZE(fmtW);
+    WCHAR *ret;
+
+    buflen += lstrlenW( arch );
+    buflen += lstrlenW( name );
+    buflen += lstrlenW( token );
+    if (!(ret = HeapAlloc( GetProcessHeap(), 0, buflen * sizeof(WCHAR) ))) return NULL;
+    *len = swprintf( ret, fmtW, arch, name, token );
+    return _wcslwr( ret );
+}
+
+static WCHAR *build_policy_path( const WCHAR *arch, const WCHAR *name, const WCHAR *token,
+                                 const WCHAR *version )
+{
+    static const WCHAR fmtW[] =
+        {'%','s','p','o','l','i','c','i','e','s','\\','%','s','\\','%','s','.','p','o','l','i','c','y',0};
+    WCHAR *path = NULL, *ret, sxsdir[MAX_PATH];
+    unsigned int len;
+
+    if (!(path = build_policy_name( arch, name, token, &len ))) return NULL;
+    len += ARRAY_SIZE(fmtW);
+    len += build_sxs_path( sxsdir );
+    len += lstrlenW( version );
+    if (!(ret = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
+    {
+        HeapFree( GetProcessHeap(), 0, path );
+        return NULL;
+    }
+    swprintf( ret, fmtW, sxsdir, path, version );
+    HeapFree( GetProcessHeap(), 0, path );
+    return ret;
+}
+
+static void cache_lock( struct cache *cache )
+{
+    WaitForSingleObject( cache->lock, INFINITE );
+}
+
+static void cache_unlock( struct cache *cache )
+{
+    ReleaseMutex( cache->lock );
 }
 
+#define ASSEMBLYINFO_FLAG_INSTALLED 1
+
 static HRESULT WINAPI cache_QueryAssemblyInfo(
     IAssemblyCache *iface,
     DWORD flags,
-    LPCWSTR name,
+    LPCWSTR assembly_name,
     ASSEMBLY_INFO *info )
 {
-    FIXME("%p, 0x%08x, %s, %p\n", iface, flags, debugstr_w(name), info);
-    return E_NOTIMPL;
+    struct cache *cache = impl_from_IAssemblyCache( iface );
+    IAssemblyName *name_obj;
+    const WCHAR *arch, *name, *token, *type, *version;
+    WCHAR *p, *path = NULL;
+    unsigned int len;
+    HRESULT hr;
+
+    TRACE("%p, 0x%08x, %s, %p\n", iface, flags, debugstr_w(assembly_name), info);
+
+    if (flags || (info && info->cbAssemblyInfo != sizeof(*info)))
+        return E_INVALIDARG;
+
+    hr = CreateAssemblyNameObject( &name_obj, assembly_name, CANOF_PARSE_DISPLAY_NAME, 0 );
+    if (FAILED( hr ))
+        return hr;
+
+    arch = get_name_attribute( name_obj, NAME_ATTR_ID_ARCH );
+    name = get_name_attribute( name_obj, NAME_ATTR_ID_NAME );
+    token = get_name_attribute( name_obj, NAME_ATTR_ID_TOKEN );
+    type = get_name_attribute( name_obj, NAME_ATTR_ID_TYPE );
+    version = get_name_attribute( name_obj, NAME_ATTR_ID_VERSION );
+    if (!arch || !name || !token || !type || !version)
+    {
+        IAssemblyName_Release( name_obj );
+        return HRESULT_FROM_WIN32( ERROR_SXS_MISSING_ASSEMBLY_IDENTITY_ATTRIBUTE );
+    }
+    if (!info)
+    {
+        IAssemblyName_Release( name_obj );
+        return S_OK;
+    }
+    cache_lock( cache );
+
+    if (!wcscmp( type, win32W )) path = build_manifest_path( arch, name, token, version );
+    else if (!wcscmp( type, win32_policyW )) path = build_policy_path( arch, name, token, version );
+    else
+    {
+        hr = HRESULT_FROM_WIN32( ERROR_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE );
+        goto done;
+    }
+    if (!path)
+    {
+        hr = E_OUTOFMEMORY;
+        goto done;
+    }
+    hr = S_OK;
+    if (GetFileAttributesW( path ) != INVALID_FILE_ATTRIBUTES) /* FIXME: better check */
+    {
+        info->dwAssemblyFlags = ASSEMBLYINFO_FLAG_INSTALLED;
+        TRACE("assembly is installed\n");
+    }
+    if ((p = wcsrchr( path, '\\' ))) *p = 0;
+    len = lstrlenW( path ) + 1;
+    if (info->pszCurrentAssemblyPathBuf)
+    {
+        if (info->cchBuf < len)
+        {
+            info->cchBuf = len;
+            hr = HRESULT_FROM_WIN32( ERROR_INSUFFICIENT_BUFFER );
+        }
+        else lstrcpyW( info->pszCurrentAssemblyPathBuf, path );
+    }
+
+done:
+    HeapFree( GetProcessHeap(), 0, path );
+    IAssemblyName_Release( name_obj );
+    cache_unlock( cache );
+    return hr;
 }
 
 static HRESULT WINAPI cache_CreateAssemblyCacheItem(
@@ -274,8 +430,6 @@ static HRESULT parse_assembly( IXMLDOMDocument *doc, struct assembly **assembly
     static const WCHAR versionW[] = {'v','e','r','s','i','o','n',0};
     static const WCHAR architectureW[] = {'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 tokenW[] = {'p','u','b','l','i','c','K','e','y','T','o','k','e','n',0};
-    static const WCHAR win32W[] = {'w','i','n','3','2',0};
-    static const WCHAR policyW[] = {'w','i','n','3','2','-','p','o','l','i','c','y',0};
     IXMLDOMNodeList *list = NULL;
     IXMLDOMNode *node = NULL;
     IXMLDOMNamedNodeMap *attrs = NULL;
@@ -319,14 +473,14 @@ static HRESULT parse_assembly( IXMLDOMDocument *doc, struct assembly **assembly
     a->arch    = get_attribute_value( attrs, architectureW );
     a->token   = get_attribute_value( attrs, tokenW );
 
-    if (!a->type || (strcmpW( a->type, win32W ) && strcmpW( a->type, policyW )) ||
+    if (!a->type || (wcscmp( a->type, win32W ) && wcscmp( a->type, win32_policyW )) ||
         !a->name || !a->version || !a->arch || !a->token)
     {
         WARN("invalid win32 assembly\n");
         hr = ERROR_SXS_MANIFEST_FORMAT_ERROR;
         goto done;
     }
-    if (!strcmpW( a->type, win32W )) hr = parse_files( doc, a );
+    if (!wcscmp( a->type, win32W )) hr = parse_files( doc, a );
 
 done:
     if (attrs) IXMLDOMNamedNodeMap_Release( attrs );
@@ -337,97 +491,56 @@ done:
     return hr;
 }
 
-static WCHAR *build_sxs_path( void )
-{
-    static const WCHAR winsxsW[] = {'\\','w','i','n','s','x','s','\\',0};
-    WCHAR sxsdir[MAX_PATH];
-
-    GetWindowsDirectoryW( sxsdir, MAX_PATH );
-    strcatW( sxsdir, winsxsW );
-    return strdupW( sxsdir );
-}
-
-static WCHAR *build_assembly_name( struct assembly *assembly )
-{
-    static const WCHAR fmtW[] =
-        {'%','s','_','%','s','_','%','s','_','%','s','_','n','o','n','e','_','d','e','a','d','b','e','e','f',0};
-    WCHAR *ret, *p;
-    int len;
-
-    len = strlenW( fmtW );
-    len += strlenW( assembly->arch );
-    len += strlenW( assembly->name );
-    len += strlenW( assembly->token );
-    len += strlenW( assembly->version );
-
-    if (!(ret = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return NULL;
-    sprintfW( ret, fmtW, assembly->arch, assembly->name, assembly->token, assembly->version );
-    for (p = ret; *p; p++) *p = tolowerW( *p );
-    return ret;
-}
-
-static WCHAR *build_policy_name( struct assembly *assembly )
+static WCHAR *build_policy_filename( const WCHAR *arch, const WCHAR *name, const WCHAR *token,
+                                     const WCHAR *version )
 {
-    static const WCHAR fmtW[] =
-        {'%','s','_','%','s','_','%','s','_','n','o','n','e','_','d','e','a','d','b','e','e','f',0};
-    WCHAR *ret, *p;
-    int len;
-
-    len = strlenW( fmtW );
-    len += strlenW( assembly->arch );
-    len += strlenW( assembly->name );
-    len += strlenW( assembly->token );
-
-    if (!(ret = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return NULL;
-    sprintfW( ret, fmtW, assembly->arch, assembly->name, assembly->token );
-    for (p = ret; *p; p++) *p = tolowerW( *p );
+    static const WCHAR policiesW[] = {'p','o','l','i','c','i','e','s','\\',0};
+    static const WCHAR suffixW[] = {'.','p','o','l','i','c','y',0};
+    WCHAR sxsdir[MAX_PATH], *ret, *fullname;
+    unsigned int len;
+
+    if (!(fullname = build_policy_name( arch, name, token, &len ))) return NULL;
+    len += build_sxs_path( sxsdir );
+    len += ARRAY_SIZE(policiesW) - 1;
+    len += lstrlenW( version );
+    len += ARRAY_SIZE(suffixW) - 1;
+    if (!(ret = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) )))
+    {
+        HeapFree( GetProcessHeap(), 0, fullname );
+        return NULL;
+    }
+    lstrcpyW( ret, sxsdir );
+    lstrcatW( ret, policiesW );
+    CreateDirectoryW( ret, NULL );
+    lstrcatW( ret, name );
+    CreateDirectoryW( ret, NULL );
+    lstrcatW( ret, backslashW );
+    lstrcatW( ret, version );
+    lstrcatW( ret, suffixW );
+
+    HeapFree( GetProcessHeap(), 0, fullname );
     return ret;
 }
 
 static HRESULT install_policy( const WCHAR *manifest, struct assembly *assembly )
 {
-    static const WCHAR policiesW[] = {'p','o','l','i','c','i','e','s','\\',0};
-    static const WCHAR suffixW[] = {'.','p','o','l','i','c','y',0};
-    static const WCHAR backslashW[] = {'\\',0};
-    WCHAR *sxsdir, *name, *dst;
-    HRESULT hr = E_OUTOFMEMORY;
+    WCHAR *dst;
     BOOL ret;
-    int len;
 
     /* FIXME: handle catalog file */
 
-    if (!(sxsdir = build_sxs_path())) return E_OUTOFMEMORY;
-    if (!(name = build_policy_name( assembly ))) goto done;
-
-    len = strlenW( sxsdir );
-    len += strlenW( policiesW );
-    len += strlenW( name ) + 1;
-    len += strlenW( assembly->version );
-    len += strlenW( suffixW );
-
-    if (!(dst = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) ))) goto done;
-    strcpyW( dst, sxsdir );
-    strcatW( dst, policiesW );
-    CreateDirectoryW( dst, NULL );
-    strcatW( dst, name );
-    CreateDirectoryW( dst, NULL );
-    strcatW( dst, backslashW );
-    strcatW( dst, assembly->version );
-    strcatW( dst, suffixW );
+    dst = build_policy_filename( assembly->arch, assembly->name, assembly->token, assembly->version );
+    if (!dst) return E_OUTOFMEMORY;
 
     ret = CopyFileW( manifest, dst, FALSE );
     HeapFree( GetProcessHeap(), 0, dst );
     if (!ret)
     {
-        hr = HRESULT_FROM_WIN32( GetLastError() );
+        HRESULT hr = HRESULT_FROM_WIN32( GetLastError() );
         WARN("failed to copy policy manifest file 0x%08x\n", hr);
+        return hr;
     }
-    hr = S_OK;
-
-done:
-    HeapFree( GetProcessHeap(), 0, sxsdir );
-    HeapFree( GetProcessHeap(), 0, name );
-    return hr;
+    return S_OK;
 }
 
 static WCHAR *build_source_filename( const WCHAR *manifest, struct file *file )
@@ -436,42 +549,77 @@ static WCHAR *build_source_filename( const WCHAR *manifest, struct file *file )
     const WCHAR *p;
     int len;
 
-    p = strrchrW( manifest, '\\' );
-    if (!p) p = strrchrW( manifest, '/' );
+    p = wcsrchr( manifest, '\\' );
+    if (!p) p = wcsrchr( manifest, '/' );
     if (!p) return strdupW( manifest );
 
     len = p - manifest + 1;
-    if (!(src = HeapAlloc( GetProcessHeap(), 0, (len + strlenW( file->name ) + 1) * sizeof(WCHAR) )))
+    if (!(src = HeapAlloc( GetProcessHeap(), 0, (len + lstrlenW( file->name ) + 1) * sizeof(WCHAR) )))
         return NULL;
 
     memcpy( src, manifest, len * sizeof(WCHAR) );
-    strcpyW( src + len, file->name );
+    lstrcpyW( src + len, file->name );
     return src;
 }
 
-static HRESULT install_assembly( const WCHAR *manifest, struct assembly *assembly )
+static WCHAR *build_manifest_filename( const WCHAR *arch, const WCHAR *name, const WCHAR *token,
+                                       const WCHAR *version )
 {
     static const WCHAR manifestsW[] = {'m','a','n','i','f','e','s','t','s','\\',0};
     static const WCHAR suffixW[] = {'.','m','a','n','i','f','e','s','t',0};
-    static const WCHAR backslashW[] = {'\\',0};
-    WCHAR *sxsdir, *p, *name, *dst, *src;
+    WCHAR sxsdir[MAX_PATH], *ret, *fullname;
+    unsigned int len;
+
+    if (!(fullname = build_assembly_name( arch, name, token, version, &len ))) return NULL;
+    len += build_sxs_path( sxsdir );
+    len += ARRAY_SIZE(manifestsW) - 1;
+    len += ARRAY_SIZE(suffixW) - 1;
+    if (!(ret = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) )))
+    {
+        HeapFree( GetProcessHeap(), 0, fullname );
+        return NULL;
+    }
+    lstrcpyW( ret, sxsdir );
+    lstrcatW( ret, manifestsW );
+    lstrcatW( ret, fullname );
+    lstrcatW( ret, suffixW );
+
+    HeapFree( GetProcessHeap(), 0, fullname );
+    return ret;
+}
+
+static HRESULT load_manifest( IXMLDOMDocument *doc, const WCHAR *filename )
+{
+    HRESULT hr;
+    VARIANT var;
+    VARIANT_BOOL b;
+    BSTR str;
+
+    str = SysAllocString( filename );
+    VariantInit( &var );
+    V_VT( &var ) = VT_BSTR;
+    V_BSTR( &var ) = str;
+    hr = IXMLDOMDocument_load( doc, var, &b );
+    SysFreeString( str );
+    if (hr != S_OK) return hr;
+    if (!b)
+    {
+        WARN("failed to load manifest\n");
+        return S_FALSE;
+    }
+    return S_OK;
+}
+
+static HRESULT install_assembly( const WCHAR *manifest, struct assembly *assembly )
+{
+    WCHAR sxsdir[MAX_PATH], *p, *name, *dst, *src;
+    unsigned int len, len_name, len_sxsdir = build_sxs_path( sxsdir );
     struct file *file;
     HRESULT hr = E_OUTOFMEMORY;
     BOOL ret;
-    int len;
 
-    if (!(sxsdir = build_sxs_path())) return E_OUTOFMEMORY;
-    if (!(name = build_assembly_name( assembly ))) goto done;
-
-    len = strlenW( sxsdir );
-    len += strlenW( manifestsW );
-    len += strlenW( name );
-    len += strlenW( suffixW );
-    if (!(dst = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) ))) goto done;
-    strcpyW( dst, sxsdir );
-    strcatW( dst, manifestsW );
-    strcatW( dst, name );
-    strcatW( dst, suffixW );
+    dst = build_manifest_filename( assembly->arch, assembly->name, assembly->token, assembly->version );
+    if (!dst) return E_OUTOFMEMORY;
 
     ret = CopyFileW( manifest, dst, FALSE );
     HeapFree( GetProcessHeap(), 0, dst );
@@ -479,31 +627,31 @@ static HRESULT install_assembly( const WCHAR *manifest, struct assembly *assembl
     {
         hr = HRESULT_FROM_WIN32( GetLastError() );
         WARN("failed to copy manifest file 0x%08x\n", hr);
-        goto done;
+        return hr;
     }
 
+    name = build_assembly_name( assembly->arch, assembly->name, assembly->token, assembly->version,
+                                &len_name );
+    if (!name) return E_OUTOFMEMORY;
+
     /* FIXME: this should be a transaction */
     LIST_FOR_EACH_ENTRY( file, &assembly->files, struct file, entry )
     {
-        if (!(src = build_source_filename( manifest, file )))
-        {
-            hr = E_OUTOFMEMORY;
-            goto done;
-        }
-        len = strlenW( sxsdir ) + strlenW( name ) + strlenW( file->name );
+        if (!(src = build_source_filename( manifest, file ))) goto done;
+
+        len = len_sxsdir + len_name + lstrlenW( file->name );
         if (!(dst = HeapAlloc( GetProcessHeap(), 0, (len + 2) * sizeof(WCHAR) )))
         {
             HeapFree( GetProcessHeap(), 0, src );
-            hr = E_OUTOFMEMORY;
             goto done;
         }
-        strcpyW( dst, sxsdir );
-        strcatW( dst, name );
+        lstrcpyW( dst, sxsdir );
+        lstrcatW( dst, name );
         CreateDirectoryW( dst, NULL );
 
-        strcatW( dst, backslashW );
-        strcatW( dst, file->name );
-        for (p = dst; *p; p++) *p = tolowerW( *p );
+        lstrcatW( dst, backslashW );
+        lstrcatW( dst, file->name );
+        for (p = dst; *p; p++) *p = towlower( *p );
 
         ret = CopyFileW( src, dst, FALSE );
         HeapFree( GetProcessHeap(), 0, src );
@@ -518,7 +666,6 @@ static HRESULT install_assembly( const WCHAR *manifest, struct assembly *assembl
     hr = S_OK;
 
 done:
-    HeapFree( GetProcessHeap(), 0, sxsdir );
     HeapFree( GetProcessHeap(), 0, name );
     return hr;
 }
@@ -529,43 +676,26 @@ static HRESULT WINAPI cache_InstallAssembly(
     LPCWSTR path,
     LPCFUSION_INSTALL_REFERENCE ref )
 {
-    static const WCHAR policyW[] = {'w','i','n','3','2','-','p','o','l','i','c','y',0};
+    struct cache *cache = impl_from_IAssemblyCache( iface );
     HRESULT hr, init;
     IXMLDOMDocument *doc = NULL;
     struct assembly *assembly = NULL;
-    BSTR str;
-    VARIANT var;
-    VARIANT_BOOL b;
 
     TRACE("%p, 0x%08x, %s, %p\n", iface, flags, debugstr_w(path), ref);
 
+    cache_lock( cache );
     init = CoInitialize( NULL );
 
     hr = CoCreateInstance( &CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, &IID_IXMLDOMDocument, (void **)&doc );
     if (hr != S_OK)
         goto done;
 
-    str = SysAllocString( path );
-    VariantInit( &var );
-    V_VT( &var ) = VT_BSTR;
-    V_BSTR( &var ) = str;
-    hr = IXMLDOMDocument_load( doc, var, &b );
-    SysFreeString( str );
-    if (hr != S_OK) goto done;
-    if (!b)
-    {
-        WARN("failed to load manifest\n");
-        hr = S_FALSE;
-        goto done;
-    }
-
-    hr = parse_assembly( doc, &assembly );
-    if (hr != S_OK)
-        goto done;
+    if ((hr = load_manifest( doc, path )) != S_OK) goto done;
+    if ((hr = parse_assembly( doc, &assembly )) != S_OK) goto done;
 
     /* FIXME: verify name attributes */
 
-    if (!strcmpW( assembly->type, policyW ))
+    if (!wcscmp( assembly->type, win32_policyW ))
         hr = install_policy( path, assembly );
     else
         hr = install_assembly( path, assembly );
@@ -573,10 +703,115 @@ static HRESULT WINAPI cache_InstallAssembly(
 done:
     free_assembly( assembly );
     if (doc) IXMLDOMDocument_Release( doc );
+    if (SUCCEEDED(init)) CoUninitialize();
+    cache_unlock( cache );
+    return hr;
+}
 
-    if (SUCCEEDED(init))
-        CoUninitialize();
+static HRESULT uninstall_assembly( struct assembly *assembly )
+{
+    WCHAR sxsdir[MAX_PATH], *name, *dirname, *filename;
+    unsigned int len, len_name, len_sxsdir = build_sxs_path( sxsdir );
+    HRESULT hr = E_OUTOFMEMORY;
+    struct file *file;
+
+    name = build_assembly_name( assembly->arch, assembly->name, assembly->token, assembly->version,
+                                &len_name );
+    if (!name) return E_OUTOFMEMORY;
+    if (!(dirname = HeapAlloc( GetProcessHeap(), 0, (len_sxsdir + len_name + 1) * sizeof(WCHAR) )))
+        goto done;
+    lstrcpyW( dirname, sxsdir );
+    lstrcpyW( dirname + len_sxsdir, name );
+
+    LIST_FOR_EACH_ENTRY( file, &assembly->files, struct file, entry )
+    {
+        len = len_sxsdir + len_name + 1 + lstrlenW( file->name );
+        if (!(filename = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) ))) goto done;
+        lstrcpyW( filename, dirname );
+        lstrcatW( filename, backslashW );
+        lstrcatW( filename, file->name );
+
+        if (!DeleteFileW( filename )) WARN( "failed to delete file %u\n", GetLastError() );
+        HeapFree( GetProcessHeap(), 0, filename );
+    }
+    RemoveDirectoryW( dirname );
+    hr = S_OK;
 
+done:
+    HeapFree( GetProcessHeap(), 0, dirname );
+    HeapFree( GetProcessHeap(), 0, name );
+    return hr;
+}
+
+static HRESULT WINAPI cache_UninstallAssembly(
+    IAssemblyCache *iface,
+    DWORD flags,
+    LPCWSTR assembly_name,
+    LPCFUSION_INSTALL_REFERENCE ref,
+    ULONG *disp )
+{
+    struct cache *cache = impl_from_IAssemblyCache( iface );
+    HRESULT hr, init;
+    IXMLDOMDocument *doc = NULL;
+    struct assembly *assembly = NULL;
+    IAssemblyName *name_obj = NULL;
+    const WCHAR *arch, *name, *token, *type, *version;
+    WCHAR *p, *path = NULL;
+
+    TRACE("%p, 0x%08x, %s, %p, %p\n", iface, flags, debugstr_w(assembly_name), ref, disp);
+
+    if (ref)
+    {
+        FIXME("application reference not supported\n");
+        return E_NOTIMPL;
+    }
+    cache_lock( cache );
+    init = CoInitialize( NULL );
+
+    hr = CreateAssemblyNameObject( &name_obj, assembly_name, CANOF_PARSE_DISPLAY_NAME, NULL );
+    if (FAILED( hr ))
+        goto done;
+
+    arch = get_name_attribute( name_obj, NAME_ATTR_ID_ARCH );
+    name = get_name_attribute( name_obj, NAME_ATTR_ID_NAME );
+    token = get_name_attribute( name_obj, NAME_ATTR_ID_TOKEN );
+    type = get_name_attribute( name_obj, NAME_ATTR_ID_TYPE );
+    version = get_name_attribute( name_obj, NAME_ATTR_ID_VERSION );
+    if (!arch || !name || !token || !type || !version)
+    {
+        hr = E_INVALIDARG;
+        goto done;
+    }
+    if (!wcscmp( type, win32W )) path = build_manifest_filename( arch, name, token, version );
+    else if (!wcscmp( type, win32_policyW )) path = build_policy_filename( arch, name, token, version );
+    else
+    {
+        hr = E_INVALIDARG;
+        goto done;
+    }
+
+    hr = CoCreateInstance( &CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, &IID_IXMLDOMDocument, (void **)&doc );
+    if (hr != S_OK)
+        goto done;
+
+    if ((hr = load_manifest( doc, path )) != S_OK) goto done;
+    if ((hr = parse_assembly( doc, &assembly )) != S_OK) goto done;
+
+    if (!DeleteFileW( path )) WARN( "unable to remove manifest file %u\n", GetLastError() );
+    else if ((p = wcsrchr( path, '\\' )))
+    {
+        *p = 0;
+        RemoveDirectoryW( path );
+    }
+    if (!wcscmp( assembly->type, win32W )) hr = uninstall_assembly( assembly );
+
+done:
+    if (name_obj) IAssemblyName_Release( name_obj );
+    HeapFree( GetProcessHeap(), 0, path );
+    free_assembly( assembly );
+    if (doc) IXMLDOMDocument_Release( doc );
+    if (SUCCEEDED(init)) CoUninitialize();
+    cache_unlock( cache );
     return hr;
 }
 
@@ -612,7 +847,12 @@ HRESULT WINAPI CreateAssemblyCache( IAssemblyCache **obj, DWORD reserved )
 
     cache->IAssemblyCache_iface.lpVtbl = &cache_vtbl;
     cache->refs = 1;
-
+    cache->lock = CreateMutexW( NULL, FALSE, cache_mutex_nameW );
+    if (!cache->lock)
+    {
+        HeapFree( GetProcessHeap(), 0, cache );
+        return HRESULT_FROM_WIN32( GetLastError() );
+    }
     *obj = &cache->IAssemblyCache_iface;
     return S_OK;
 }