dmusic: Pass creation parameters to DMUSIC_CreateDirectMusicBufferImpl then allocate...
[wine] / dlls / sxs / cache.c
1 /*
2  * IAssemblyCache implementation
3  *
4  * Copyright 2010 Hans Leidekker for CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <stdarg.h>
22
23 #define COBJMACROS
24 #define INITGUID
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "ole2.h"
29 #include "winsxs.h"
30 #include "msxml2.h"
31
32 #include "wine/debug.h"
33 #include "wine/list.h"
34 #include "wine/unicode.h"
35 #include "sxs_private.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(sxs);
38
39 static const WCHAR win32W[] = {'w','i','n','3','2',0};
40 static const WCHAR win32_policyW[] = {'w','i','n','3','2','-','p','o','l','i','c','y',0};
41
42 struct cache
43 {
44     IAssemblyCache IAssemblyCache_iface;
45     LONG refs;
46 };
47
48 static inline struct cache *impl_from_IAssemblyCache(IAssemblyCache *iface)
49 {
50     return CONTAINING_RECORD(iface, struct cache, IAssemblyCache_iface);
51 }
52
53 static HRESULT WINAPI cache_QueryInterface(
54     IAssemblyCache *iface,
55     REFIID riid,
56     void **obj )
57 {
58     struct cache *cache = impl_from_IAssemblyCache(iface);
59
60     TRACE("%p, %s, %p\n", cache, debugstr_guid(riid), obj);
61
62     *obj = NULL;
63
64     if (IsEqualIID(riid, &IID_IUnknown) ||
65         IsEqualIID(riid, &IID_IAssemblyCache))
66     {
67         IUnknown_AddRef( iface );
68         *obj = cache;
69         return S_OK;
70     }
71
72     return E_NOINTERFACE;
73 }
74
75 static ULONG WINAPI cache_AddRef( IAssemblyCache *iface )
76 {
77     struct cache *cache = impl_from_IAssemblyCache(iface);
78     return InterlockedIncrement( &cache->refs );
79 }
80
81 static ULONG WINAPI cache_Release( IAssemblyCache *iface )
82 {
83     struct cache *cache = impl_from_IAssemblyCache(iface);
84     ULONG refs = InterlockedDecrement( &cache->refs );
85
86     if (!refs)
87     {
88         TRACE("destroying %p\n", cache);
89         HeapFree( GetProcessHeap(), 0, cache );
90     }
91     return refs;
92 }
93
94 static HRESULT WINAPI cache_UninstallAssembly(
95     IAssemblyCache *iface,
96     DWORD flags,
97     LPCWSTR name,
98     LPCFUSION_INSTALL_REFERENCE ref,
99     ULONG *disp )
100 {
101     FIXME("%p, 0x%08x, %s, %p, %p\n", iface, flags, debugstr_w(name), ref, disp);
102     return E_NOTIMPL;
103 }
104
105 static void build_sxs_path( WCHAR *path )
106 {
107     static const WCHAR winsxsW[] = {'\\','w','i','n','s','x','s','\\',0};
108     GetWindowsDirectoryW( path, MAX_PATH );
109     strcatW( path, winsxsW );
110 }
111
112 static WCHAR *build_assembly_name( const WCHAR *arch, const WCHAR *name, const WCHAR *token,
113                                    const WCHAR *version )
114 {
115     static const WCHAR fmtW[] =
116         {'%','s','_','%','s','_','%','s','_','%','s','_','n','o','n','e','_','d','e','a','d','b','e','e','f',0};
117     WCHAR *ret, *p;
118     int len;
119
120     len = strlenW( fmtW );
121     len += strlenW( arch );
122     len += strlenW( name );
123     len += strlenW( token );
124     len += strlenW( version );
125
126     if (!(ret = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return NULL;
127     sprintfW( ret, fmtW, arch, name, token, version );
128     for (p = ret; *p; p++) *p = tolowerW( *p );
129     return ret;
130 }
131
132 static WCHAR *build_policy_name( const WCHAR *arch, const WCHAR *name, const WCHAR *token )
133 {
134     static const WCHAR fmtW[] =
135         {'%','s','_','%','s','_','%','s','_','n','o','n','e','_','d','e','a','d','b','e','e','f',0};
136     WCHAR *ret, *p;
137     int len;
138
139     len = strlenW( fmtW );
140     len += strlenW( arch );
141     len += strlenW( name );
142     len += strlenW( token );
143
144     if (!(ret = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return NULL;
145     sprintfW( ret, fmtW, arch, name, token );
146     for (p = ret; *p; p++) *p = tolowerW( *p );
147     return ret;
148 }
149
150 #define ASSEMBLYINFO_FLAG_INSTALLED 1
151
152 static HRESULT WINAPI cache_QueryAssemblyInfo(
153     IAssemblyCache *iface,
154     DWORD flags,
155     LPCWSTR assembly_name,
156     ASSEMBLY_INFO *info )
157 {
158     static const WCHAR fmt_assemblyW[] =
159         {'%','s','m','a','n','i','f','e','s','t','s','\\','%','s','.','m','a','n','i','f','e','s','t',0};
160     static const WCHAR fmt_policyW[] =
161         {'%','s','p','o','l','i','c','i','e','s','\\','%','s','\\','%','s','.','p','o','l','i','c','y',0};
162     LPASSEMBLYNAME name_obj;
163     unsigned int len;
164     const WCHAR *arch, *name, *token, *type, *version;
165     WCHAR *p, *path = NULL, *full_path = NULL, sxsdir[MAX_PATH];
166     HRESULT hr;
167
168     TRACE("%p, 0x%08x, %s, %p\n", iface, flags, debugstr_w(assembly_name), info);
169
170     if (flags || (info && info->cbAssemblyInfo != sizeof(*info)))
171         return E_INVALIDARG;
172
173     hr = CreateAssemblyNameObject( &name_obj, assembly_name, CANOF_PARSE_DISPLAY_NAME, 0 );
174     if (FAILED( hr ))
175         return hr;
176
177     arch = get_name_attribute( name_obj, NAME_ATTR_ID_ARCH );
178     name = get_name_attribute( name_obj, NAME_ATTR_ID_NAME );
179     token = get_name_attribute( name_obj, NAME_ATTR_ID_TOKEN );
180     type = get_name_attribute( name_obj, NAME_ATTR_ID_TYPE );
181     version = get_name_attribute( name_obj, NAME_ATTR_ID_VERSION );
182     if (!arch || !name || !token || !type || !version)
183     {
184         return HRESULT_FROM_WIN32( ERROR_SXS_MISSING_ASSEMBLY_IDENTITY_ATTRIBUTE );
185     }
186     if (!info)
187     {
188         IAssemblyName_Release( name_obj );
189         return S_OK;
190     }
191     hr = E_OUTOFMEMORY;
192     build_sxs_path( sxsdir );
193     len = strlenW( sxsdir );
194     if (!strcmpW( type, win32W ))
195     {
196         len += strlenW( fmt_assemblyW );
197         if (!(path = build_assembly_name( arch, name, token, version ))) goto done;
198         len += strlenW( path );
199         if (!(full_path = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) goto done;
200         sprintfW( full_path, fmt_assemblyW, sxsdir, path );
201     }
202     else if (!strcmpW( type, win32_policyW ))
203     {
204         len += strlenW( fmt_policyW );
205         if (!(path = build_policy_name( arch, name, token ))) goto done;
206         len += strlenW( path ) + strlenW( version );
207         if (!(full_path = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) goto done;
208         sprintfW( full_path, fmt_policyW, sxsdir, path, version );
209     }
210     else
211     {
212         hr = HRESULT_FROM_WIN32( ERROR_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE );
213         goto done;
214     }
215     hr = S_OK;
216     if (GetFileAttributesW( full_path ) != INVALID_FILE_ATTRIBUTES) /* FIXME: better check */
217     {
218         info->dwAssemblyFlags = ASSEMBLYINFO_FLAG_INSTALLED;
219         TRACE("assembly is installed\n");
220     }
221     if ((p = strrchrW( full_path, '\\' ))) *p = 0;
222     len = strlenW( full_path ) + 1;
223     if (info->pszCurrentAssemblyPathBuf)
224     {
225         if (info->cchBuf < len)
226         {
227             info->cchBuf = len;
228             hr = HRESULT_FROM_WIN32( ERROR_INSUFFICIENT_BUFFER );
229         }
230         else strcpyW( info->pszCurrentAssemblyPathBuf, full_path );
231     }
232
233 done:
234     HeapFree( GetProcessHeap(), 0, full_path );
235     HeapFree( GetProcessHeap(), 0, path );
236     IAssemblyName_Release( name_obj );
237     return hr;
238 }
239
240 static HRESULT WINAPI cache_CreateAssemblyCacheItem(
241     IAssemblyCache *iface,
242     DWORD flags,
243     PVOID reserved,
244     IAssemblyCacheItem **item,
245     LPCWSTR name )
246 {
247     FIXME("%p, 0x%08x, %p, %p, %s\n", iface, flags, reserved, item, debugstr_w(name));
248     return E_NOTIMPL;
249 }
250
251 static HRESULT WINAPI cache_Reserved(
252     IAssemblyCache *iface,
253     IUnknown **reserved)
254 {
255     FIXME("%p\n", reserved);
256     return E_NOTIMPL;
257 }
258
259 static BSTR get_attribute_value( IXMLDOMNamedNodeMap *map, const WCHAR *value_name )
260 {
261     HRESULT hr;
262     IXMLDOMNode *attr;
263     VARIANT var;
264     BSTR str;
265
266     str = SysAllocString( value_name );
267     hr = IXMLDOMNamedNodeMap_getNamedItem( map, str, &attr );
268     SysFreeString( str );
269     if (hr != S_OK) return NULL;
270
271     hr = IXMLDOMNode_get_nodeValue( attr, &var );
272     IXMLDOMNode_Release( attr );
273     if (hr != S_OK) return NULL;
274     if (V_VT(&var) != VT_BSTR)
275     {
276         VariantClear( &var );
277         return NULL;
278     }
279     TRACE("%s=%s\n", debugstr_w(value_name), debugstr_w(V_BSTR( &var )));
280     return V_BSTR( &var );
281 }
282
283 struct file
284 {
285     struct list entry;
286     BSTR name;
287 };
288
289 struct assembly
290 {
291     BSTR type;
292     BSTR name;
293     BSTR version;
294     BSTR arch;
295     BSTR token;
296     struct list files;
297 };
298
299 static void free_assembly( struct assembly *assembly )
300 {
301     struct list *item, *cursor;
302
303     if (!assembly) return;
304     SysFreeString( assembly->type );
305     SysFreeString( assembly->name );
306     SysFreeString( assembly->version );
307     SysFreeString( assembly->arch );
308     SysFreeString( assembly->token );
309     LIST_FOR_EACH_SAFE( item, cursor, &assembly->files )
310     {
311         struct file *file = LIST_ENTRY( item, struct file, entry );
312         list_remove( &file->entry );
313         SysFreeString( file->name );
314         HeapFree( GetProcessHeap(), 0, file );
315     }
316     HeapFree( GetProcessHeap(), 0, assembly );
317 }
318
319 static HRESULT parse_files( IXMLDOMDocument *doc, struct assembly *assembly )
320 {
321     static const WCHAR fileW[] = {'f','i','l','e',0};
322     static const WCHAR nameW[] = {'n','a','m','e',0};
323     IXMLDOMNamedNodeMap *attrs;
324     IXMLDOMNodeList *list;
325     IXMLDOMNode *node;
326     struct file *f;
327     BSTR str;
328     HRESULT hr;
329     LONG len;
330
331     str = SysAllocString( fileW );
332     hr = IXMLDOMDocument_getElementsByTagName( doc, str, &list );
333     SysFreeString( str );
334     if (hr != S_OK) return hr;
335
336     hr = IXMLDOMNodeList_get_length( list, &len );
337     if (hr != S_OK) goto done;
338     TRACE("found %d files\n", len);
339     if (!len)
340     {
341         hr = ERROR_SXS_MANIFEST_FORMAT_ERROR;
342         goto done;
343     }
344
345     for (;;)
346     {
347         hr = IXMLDOMNodeList_nextNode( list, &node );
348         if (hr != S_OK || !node)
349         {
350             hr = S_OK;
351             break;
352         }
353
354         /* FIXME: validate node type */
355
356         hr = IXMLDOMNode_get_attributes( node, &attrs );
357         IXMLDOMNode_Release( node );
358         if (hr != S_OK)
359             goto done;
360
361         if (!(f = HeapAlloc( GetProcessHeap(), 0, sizeof(struct file) )))
362         {
363             IXMLDOMNamedNodeMap_Release( attrs );
364             hr = E_OUTOFMEMORY;
365             goto done;
366         }
367
368         f->name = get_attribute_value( attrs, nameW );
369         IXMLDOMNamedNodeMap_Release( attrs );
370         if (!f->name)
371         {
372             HeapFree( GetProcessHeap(), 0, f );
373             hr = ERROR_SXS_MANIFEST_FORMAT_ERROR;
374             goto done;
375         }
376         list_add_tail( &assembly->files, &f->entry );
377     }
378
379     if (list_empty( &assembly->files ))
380     {
381         WARN("no files found\n");
382         hr = ERROR_SXS_MANIFEST_FORMAT_ERROR;
383     }
384
385 done:
386     IXMLDOMNodeList_Release( list );
387     return hr;
388 }
389
390 static HRESULT parse_assembly( IXMLDOMDocument *doc, struct assembly **assembly )
391 {
392     static const WCHAR identityW[] = {'a','s','s','e','m','b','l','y','I','d','e','n','t','i','t','y',0};
393     static const WCHAR typeW[] = {'t','y','p','e',0};
394     static const WCHAR nameW[] = {'n','a','m','e',0};
395     static const WCHAR versionW[] = {'v','e','r','s','i','o','n',0};
396     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};
397     static const WCHAR tokenW[] = {'p','u','b','l','i','c','K','e','y','T','o','k','e','n',0};
398     IXMLDOMNodeList *list = NULL;
399     IXMLDOMNode *node = NULL;
400     IXMLDOMNamedNodeMap *attrs = NULL;
401     struct assembly *a = NULL;
402     BSTR str;
403     HRESULT hr;
404     LONG len;
405
406     str = SysAllocString( identityW );
407     hr = IXMLDOMDocument_getElementsByTagName( doc, str, &list );
408     SysFreeString( str );
409     if (hr != S_OK) goto done;
410
411     hr = IXMLDOMNodeList_get_length( list, &len );
412     if (hr != S_OK) goto done;
413     if (!len)
414     {
415         hr = ERROR_SXS_MANIFEST_FORMAT_ERROR;
416         goto done;
417     }
418     hr = IXMLDOMNodeList_nextNode( list, &node );
419     if (hr != S_OK) goto done;
420     if (!node)
421     {
422         hr = ERROR_SXS_MANIFEST_FORMAT_ERROR;
423         goto done;
424     }
425     if (!(a = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct assembly) )))
426     {
427         hr = E_OUTOFMEMORY;
428         goto done;
429     }
430     list_init( &a->files );
431
432     hr = IXMLDOMNode_get_attributes( node, &attrs );
433     if (hr != S_OK) goto done;
434
435     a->type    = get_attribute_value( attrs, typeW );
436     a->name    = get_attribute_value( attrs, nameW );
437     a->version = get_attribute_value( attrs, versionW );
438     a->arch    = get_attribute_value( attrs, architectureW );
439     a->token   = get_attribute_value( attrs, tokenW );
440
441     if (!a->type || (strcmpW( a->type, win32W ) && strcmpW( a->type, win32_policyW )) ||
442         !a->name || !a->version || !a->arch || !a->token)
443     {
444         WARN("invalid win32 assembly\n");
445         hr = ERROR_SXS_MANIFEST_FORMAT_ERROR;
446         goto done;
447     }
448     if (!strcmpW( a->type, win32W )) hr = parse_files( doc, a );
449
450 done:
451     if (attrs) IXMLDOMNamedNodeMap_Release( attrs );
452     if (node) IXMLDOMNode_Release( node );
453     if (list) IXMLDOMNodeList_Release( list );
454     if (hr == S_OK) *assembly = a;
455     else free_assembly( a );
456     return hr;
457 }
458
459 static HRESULT install_policy( const WCHAR *manifest, struct assembly *assembly )
460 {
461     static const WCHAR policiesW[] = {'p','o','l','i','c','i','e','s','\\',0};
462     static const WCHAR suffixW[] = {'.','p','o','l','i','c','y',0};
463     static const WCHAR backslashW[] = {'\\',0};
464     WCHAR sxsdir[MAX_PATH], *name, *dst;
465     HRESULT hr = E_OUTOFMEMORY;
466     BOOL ret;
467     int len;
468
469     /* FIXME: handle catalog file */
470
471     build_sxs_path( sxsdir );
472     name = build_policy_name( assembly->arch, assembly->name, assembly->token );
473     if (!name) goto done;
474
475     len = strlenW( sxsdir );
476     len += strlenW( policiesW );
477     len += strlenW( name ) + 1;
478     len += strlenW( assembly->version );
479     len += strlenW( suffixW );
480
481     if (!(dst = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) ))) goto done;
482     strcpyW( dst, sxsdir );
483     strcatW( dst, policiesW );
484     CreateDirectoryW( dst, NULL );
485     strcatW( dst, name );
486     CreateDirectoryW( dst, NULL );
487     strcatW( dst, backslashW );
488     strcatW( dst, assembly->version );
489     strcatW( dst, suffixW );
490
491     ret = CopyFileW( manifest, dst, FALSE );
492     HeapFree( GetProcessHeap(), 0, dst );
493     if (!ret)
494     {
495         hr = HRESULT_FROM_WIN32( GetLastError() );
496         WARN("failed to copy policy manifest file 0x%08x\n", hr);
497     }
498     hr = S_OK;
499
500 done:
501     HeapFree( GetProcessHeap(), 0, name );
502     return hr;
503 }
504
505 static WCHAR *build_source_filename( const WCHAR *manifest, struct file *file )
506 {
507     WCHAR *src;
508     const WCHAR *p;
509     int len;
510
511     p = strrchrW( manifest, '\\' );
512     if (!p) p = strrchrW( manifest, '/' );
513     if (!p) return strdupW( manifest );
514
515     len = p - manifest + 1;
516     if (!(src = HeapAlloc( GetProcessHeap(), 0, (len + strlenW( file->name ) + 1) * sizeof(WCHAR) )))
517         return NULL;
518
519     memcpy( src, manifest, len * sizeof(WCHAR) );
520     strcpyW( src + len, file->name );
521     return src;
522 }
523
524 static HRESULT install_assembly( const WCHAR *manifest, struct assembly *assembly )
525 {
526     static const WCHAR manifestsW[] = {'m','a','n','i','f','e','s','t','s','\\',0};
527     static const WCHAR suffixW[] = {'.','m','a','n','i','f','e','s','t',0};
528     static const WCHAR backslashW[] = {'\\',0};
529     WCHAR sxsdir[MAX_PATH], *p, *name, *dst, *src;
530     struct file *file;
531     HRESULT hr = E_OUTOFMEMORY;
532     BOOL ret;
533     int len;
534
535     build_sxs_path( sxsdir );
536     name = build_assembly_name( assembly->arch, assembly->name, assembly->token, assembly->version );
537     if (!name) goto done;
538
539     len = strlenW( sxsdir );
540     len += strlenW( manifestsW );
541     len += strlenW( name );
542     len += strlenW( suffixW );
543     if (!(dst = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) ))) goto done;
544     strcpyW( dst, sxsdir );
545     strcatW( dst, manifestsW );
546     strcatW( dst, name );
547     strcatW( dst, suffixW );
548
549     ret = CopyFileW( manifest, dst, FALSE );
550     HeapFree( GetProcessHeap(), 0, dst );
551     if (!ret)
552     {
553         hr = HRESULT_FROM_WIN32( GetLastError() );
554         WARN("failed to copy manifest file 0x%08x\n", hr);
555         goto done;
556     }
557
558     /* FIXME: this should be a transaction */
559     LIST_FOR_EACH_ENTRY( file, &assembly->files, struct file, entry )
560     {
561         if (!(src = build_source_filename( manifest, file )))
562         {
563             hr = E_OUTOFMEMORY;
564             goto done;
565         }
566         len = strlenW( sxsdir ) + strlenW( name ) + strlenW( file->name );
567         if (!(dst = HeapAlloc( GetProcessHeap(), 0, (len + 2) * sizeof(WCHAR) )))
568         {
569             HeapFree( GetProcessHeap(), 0, src );
570             hr = E_OUTOFMEMORY;
571             goto done;
572         }
573         strcpyW( dst, sxsdir );
574         strcatW( dst, name );
575         CreateDirectoryW( dst, NULL );
576
577         strcatW( dst, backslashW );
578         strcatW( dst, file->name );
579         for (p = dst; *p; p++) *p = tolowerW( *p );
580
581         ret = CopyFileW( src, dst, FALSE );
582         HeapFree( GetProcessHeap(), 0, src );
583         HeapFree( GetProcessHeap(), 0, dst );
584         if (!ret)
585         {
586             hr = HRESULT_FROM_WIN32( GetLastError() );
587             WARN("failed to copy file 0x%08x\n", hr);
588             goto done;
589         }
590     }
591     hr = S_OK;
592
593 done:
594     HeapFree( GetProcessHeap(), 0, name );
595     return hr;
596 }
597
598 static HRESULT WINAPI cache_InstallAssembly(
599     IAssemblyCache *iface,
600     DWORD flags,
601     LPCWSTR path,
602     LPCFUSION_INSTALL_REFERENCE ref )
603 {
604     HRESULT hr, init;
605     IXMLDOMDocument *doc = NULL;
606     struct assembly *assembly = NULL;
607     BSTR str;
608     VARIANT var;
609     VARIANT_BOOL b;
610
611     TRACE("%p, 0x%08x, %s, %p\n", iface, flags, debugstr_w(path), ref);
612
613     init = CoInitialize( NULL );
614
615     hr = CoCreateInstance( &CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, &IID_IXMLDOMDocument, (void **)&doc );
616     if (hr != S_OK)
617         goto done;
618
619     str = SysAllocString( path );
620     VariantInit( &var );
621     V_VT( &var ) = VT_BSTR;
622     V_BSTR( &var ) = str;
623     hr = IXMLDOMDocument_load( doc, var, &b );
624     SysFreeString( str );
625     if (hr != S_OK) goto done;
626     if (!b)
627     {
628         WARN("failed to load manifest\n");
629         hr = S_FALSE;
630         goto done;
631     }
632
633     hr = parse_assembly( doc, &assembly );
634     if (hr != S_OK)
635         goto done;
636
637     /* FIXME: verify name attributes */
638
639     if (!strcmpW( assembly->type, win32_policyW ))
640         hr = install_policy( path, assembly );
641     else
642         hr = install_assembly( path, assembly );
643
644 done:
645     free_assembly( assembly );
646     if (doc) IXMLDOMDocument_Release( doc );
647
648     if (SUCCEEDED(init))
649         CoUninitialize();
650
651     return hr;
652 }
653
654 static const IAssemblyCacheVtbl cache_vtbl =
655 {
656     cache_QueryInterface,
657     cache_AddRef,
658     cache_Release,
659     cache_UninstallAssembly,
660     cache_QueryAssemblyInfo,
661     cache_CreateAssemblyCacheItem,
662     cache_Reserved,
663     cache_InstallAssembly
664 };
665
666 /******************************************************************
667  *  CreateAssemblyCache   (SXS.@)
668  */
669 HRESULT WINAPI CreateAssemblyCache( IAssemblyCache **obj, DWORD reserved )
670 {
671     struct cache *cache;
672
673     TRACE("%p, %u\n", obj, reserved);
674
675     if (!obj)
676         return E_INVALIDARG;
677
678     *obj = NULL;
679
680     cache = HeapAlloc( GetProcessHeap(), 0, sizeof(struct cache) );
681     if (!cache)
682         return E_OUTOFMEMORY;
683
684     cache->IAssemblyCache_iface.lpVtbl = &cache_vtbl;
685     cache->refs = 1;
686
687     *obj = &cache->IAssemblyCache_iface;
688     return S_OK;
689 }