xref: /reactos/dll/win32/fusion/asmcache.c (revision b819608e)
1 /*
2  * IAssemblyCache implementation
3  *
4  * Copyright 2008 James Hawkins
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 "fusionpriv.h"
22 
23 static const WCHAR cache_mutex_nameW[] =
24     {'_','_','W','I','N','E','_','F','U','S','I','O','N','_','C','A','C','H','E','_','M','U','T','E','X','_','_',0};
25 
26 static BOOL create_full_path(LPCWSTR path)
27 {
28     LPWSTR new_path;
29     BOOL ret = TRUE;
30     int len;
31 
32     new_path = HeapAlloc(GetProcessHeap(), 0, (strlenW(path) + 1) * sizeof(WCHAR));
33     if (!new_path)
34         return FALSE;
35 
36     strcpyW(new_path, path);
37 
38     while ((len = strlenW(new_path)) && new_path[len - 1] == '\\')
39         new_path[len - 1] = 0;
40 
41     while (!CreateDirectoryW(new_path, NULL))
42     {
43         LPWSTR slash;
44         DWORD last_error = GetLastError();
45 
46         if(last_error == ERROR_ALREADY_EXISTS)
47             break;
48 
49         if(last_error != ERROR_PATH_NOT_FOUND)
50         {
51             ret = FALSE;
52             break;
53         }
54 
55         if(!(slash = strrchrW(new_path, '\\')))
56         {
57             ret = FALSE;
58             break;
59         }
60 
61         len = slash - new_path;
62         new_path[len] = 0;
63         if(!create_full_path(new_path))
64         {
65             ret = FALSE;
66             break;
67         }
68 
69         new_path[len] = '\\';
70     }
71 
72     HeapFree(GetProcessHeap(), 0, new_path);
73     return ret;
74 }
75 
76 static BOOL get_assembly_directory(LPWSTR dir, DWORD size, const char *version, PEKIND architecture)
77 {
78     static const WCHAR dotnet[] = {'\\','M','i','c','r','o','s','o','f','t','.','N','E','T','\\',0};
79     static const WCHAR gac[] = {'\\','a','s','s','e','m','b','l','y','\\','G','A','C',0};
80     static const WCHAR msil[] = {'_','M','S','I','L',0};
81     static const WCHAR x86[] = {'_','3','2',0};
82     static const WCHAR amd64[] = {'_','6','4',0};
83     DWORD len = GetWindowsDirectoryW(dir, size);
84 
85     if (!strcmp(version, "v4.0.30319"))
86     {
87         strcpyW(dir + len, dotnet);
88         len += sizeof(dotnet)/sizeof(WCHAR) -1;
89         strcpyW(dir + len, gac + 1);
90         len += sizeof(gac)/sizeof(WCHAR) - 2;
91     }
92     else
93     {
94         strcpyW(dir + len, gac);
95         len += sizeof(gac)/sizeof(WCHAR) - 1;
96     }
97     switch (architecture)
98     {
99         case peNone:
100             break;
101 
102         case peMSIL:
103             strcpyW(dir + len, msil);
104             break;
105 
106         case peI386:
107             strcpyW(dir + len, x86);
108             break;
109 
110         case peAMD64:
111             strcpyW(dir + len, amd64);
112             break;
113 
114         default:
115             WARN("unhandled architecture %u\n", architecture);
116             return FALSE;
117     }
118     return TRUE;
119 }
120 
121 /* IAssemblyCache */
122 
123 typedef struct {
124     IAssemblyCache IAssemblyCache_iface;
125 
126     LONG ref;
127     HANDLE lock;
128 } IAssemblyCacheImpl;
129 
130 static inline IAssemblyCacheImpl *impl_from_IAssemblyCache(IAssemblyCache *iface)
131 {
132     return CONTAINING_RECORD(iface, IAssemblyCacheImpl, IAssemblyCache_iface);
133 }
134 
135 static HRESULT WINAPI IAssemblyCacheImpl_QueryInterface(IAssemblyCache *iface,
136                                                         REFIID riid, LPVOID *ppobj)
137 {
138     IAssemblyCacheImpl *This = impl_from_IAssemblyCache(iface);
139 
140     TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppobj);
141 
142     *ppobj = NULL;
143 
144     if (IsEqualIID(riid, &IID_IUnknown) ||
145         IsEqualIID(riid, &IID_IAssemblyCache))
146     {
147         IAssemblyCache_AddRef(iface);
148         *ppobj = This;
149         return S_OK;
150     }
151 
152     WARN("(%p, %s, %p): not found\n", This, debugstr_guid(riid), ppobj);
153     return E_NOINTERFACE;
154 }
155 
156 static ULONG WINAPI IAssemblyCacheImpl_AddRef(IAssemblyCache *iface)
157 {
158     IAssemblyCacheImpl *This = impl_from_IAssemblyCache(iface);
159     ULONG refCount = InterlockedIncrement(&This->ref);
160 
161     TRACE("(%p)->(ref before = %u)\n", This, refCount - 1);
162 
163     return refCount;
164 }
165 
166 static ULONG WINAPI IAssemblyCacheImpl_Release(IAssemblyCache *iface)
167 {
168     IAssemblyCacheImpl *cache = impl_from_IAssemblyCache(iface);
169     ULONG refCount = InterlockedDecrement( &cache->ref );
170 
171     TRACE("(%p)->(ref before = %u)\n", cache, refCount + 1);
172 
173     if (!refCount)
174     {
175         CloseHandle( cache->lock );
176         HeapFree( GetProcessHeap(), 0, cache );
177     }
178     return refCount;
179 }
180 
181 static void cache_lock( IAssemblyCacheImpl *cache )
182 {
183     WaitForSingleObject( cache->lock, INFINITE );
184 }
185 
186 static void cache_unlock( IAssemblyCacheImpl *cache )
187 {
188     ReleaseMutex( cache->lock );
189 }
190 
191 static HRESULT WINAPI IAssemblyCacheImpl_UninstallAssembly(IAssemblyCache *iface,
192                                                            DWORD dwFlags,
193                                                            LPCWSTR pszAssemblyName,
194                                                            LPCFUSION_INSTALL_REFERENCE pRefData,
195                                                            ULONG *pulDisposition)
196 {
197     HRESULT hr;
198     IAssemblyCacheImpl *cache = impl_from_IAssemblyCache(iface);
199     IAssemblyName *asmname, *next = NULL;
200     IAssemblyEnum *asmenum = NULL;
201     WCHAR *p, *path = NULL;
202     ULONG disp;
203     DWORD len;
204 
205     TRACE("(%p, 0%08x, %s, %p, %p)\n", iface, dwFlags,
206           debugstr_w(pszAssemblyName), pRefData, pulDisposition);
207 
208     if (pRefData)
209     {
210         FIXME("application reference not supported\n");
211         return E_NOTIMPL;
212     }
213     hr = CreateAssemblyNameObject( &asmname, pszAssemblyName, CANOF_PARSE_DISPLAY_NAME, NULL );
214     if (FAILED( hr ))
215         return hr;
216 
217     cache_lock( cache );
218 
219     hr = CreateAssemblyEnum( &asmenum, NULL, asmname, ASM_CACHE_GAC, NULL );
220     if (FAILED( hr ))
221         goto done;
222 
223     hr = IAssemblyEnum_GetNextAssembly( asmenum, NULL, &next, 0 );
224     if (hr == S_FALSE)
225     {
226         if (pulDisposition)
227             *pulDisposition = IASSEMBLYCACHE_UNINSTALL_DISPOSITION_ALREADY_UNINSTALLED;
228         goto done;
229     }
230     hr = IAssemblyName_GetPath( next, NULL, &len );
231     if (hr != HRESULT_FROM_WIN32( ERROR_INSUFFICIENT_BUFFER ))
232         goto done;
233 
234     if (!(path = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
235     {
236         hr = E_OUTOFMEMORY;
237         goto done;
238     }
239     hr = IAssemblyName_GetPath( next, path, &len );
240     if (FAILED( hr ))
241         goto done;
242 
243     if (DeleteFileW( path ))
244     {
245         if ((p = strrchrW( path, '\\' )))
246         {
247             *p = 0;
248             RemoveDirectoryW( path );
249             if ((p = strrchrW( path, '\\' )))
250             {
251                 *p = 0;
252                 RemoveDirectoryW( path );
253             }
254         }
255         disp = IASSEMBLYCACHE_UNINSTALL_DISPOSITION_UNINSTALLED;
256         hr = S_OK;
257     }
258     else
259     {
260         disp = IASSEMBLYCACHE_UNINSTALL_DISPOSITION_ALREADY_UNINSTALLED;
261         hr = S_FALSE;
262     }
263     if (pulDisposition) *pulDisposition = disp;
264 
265 done:
266     IAssemblyName_Release( asmname );
267     if (next) IAssemblyName_Release( next );
268     if (asmenum) IAssemblyEnum_Release( asmenum );
269     HeapFree( GetProcessHeap(), 0, path );
270     cache_unlock( cache );
271     return hr;
272 }
273 
274 static HRESULT WINAPI IAssemblyCacheImpl_QueryAssemblyInfo(IAssemblyCache *iface,
275                                                            DWORD dwFlags,
276                                                            LPCWSTR pszAssemblyName,
277                                                            ASSEMBLY_INFO *pAsmInfo)
278 {
279     IAssemblyCacheImpl *cache = impl_from_IAssemblyCache(iface);
280     IAssemblyName *asmname, *next = NULL;
281     IAssemblyEnum *asmenum = NULL;
282     HRESULT hr;
283 
284     TRACE("(%p, %d, %s, %p)\n", iface, dwFlags,
285           debugstr_w(pszAssemblyName), pAsmInfo);
286 
287     if (pAsmInfo)
288     {
289         if (pAsmInfo->cbAssemblyInfo == 0)
290             pAsmInfo->cbAssemblyInfo = sizeof(ASSEMBLY_INFO);
291         else if (pAsmInfo->cbAssemblyInfo != sizeof(ASSEMBLY_INFO))
292             return E_INVALIDARG;
293     }
294 
295     hr = CreateAssemblyNameObject(&asmname, pszAssemblyName,
296                                   CANOF_PARSE_DISPLAY_NAME, NULL);
297     if (FAILED(hr))
298         return hr;
299 
300     cache_lock( cache );
301 
302     hr = CreateAssemblyEnum(&asmenum, NULL, asmname, ASM_CACHE_GAC, NULL);
303     if (FAILED(hr))
304         goto done;
305 
306     for (;;)
307     {
308         hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0);
309         if (hr != S_OK)
310         {
311             hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
312             goto done;
313         }
314         hr = IAssemblyName_IsEqual(asmname, next, ASM_CMPF_IL_ALL);
315         if (hr == S_OK) break;
316     }
317 
318     if (!pAsmInfo)
319         goto done;
320 
321     hr = IAssemblyName_GetPath(next, pAsmInfo->pszCurrentAssemblyPathBuf, &pAsmInfo->cchBuf);
322 
323     pAsmInfo->dwAssemblyFlags = ASSEMBLYINFO_FLAG_INSTALLED;
324 
325 done:
326     IAssemblyName_Release(asmname);
327     if (next) IAssemblyName_Release(next);
328     if (asmenum) IAssemblyEnum_Release(asmenum);
329     cache_unlock( cache );
330     return hr;
331 }
332 
333 static HRESULT WINAPI IAssemblyCacheImpl_CreateAssemblyCacheItem(IAssemblyCache *iface,
334                                                                  DWORD dwFlags,
335                                                                  PVOID pvReserved,
336                                                                  IAssemblyCacheItem **ppAsmItem,
337                                                                  LPCWSTR pszAssemblyName)
338 {
339     FIXME("(%p, %d, %p, %p, %s) stub!\n", iface, dwFlags, pvReserved,
340           ppAsmItem, debugstr_w(pszAssemblyName));
341 
342     return E_NOTIMPL;
343 }
344 
345 static HRESULT WINAPI IAssemblyCacheImpl_CreateAssemblyScavenger(IAssemblyCache *iface,
346                                                                  IUnknown **ppUnkReserved)
347 {
348     FIXME("(%p, %p) stub!\n", iface, ppUnkReserved);
349     return E_NOTIMPL;
350 }
351 
352 static HRESULT copy_file( const WCHAR *src_dir, DWORD src_len, const WCHAR *dst_dir, DWORD dst_len,
353                           const WCHAR *filename )
354 {
355     WCHAR *src_file, *dst_file;
356     DWORD len = strlenW( filename );
357     HRESULT hr = S_OK;
358 
359     if (!(src_file = HeapAlloc( GetProcessHeap(), 0, (src_len + len + 1) * sizeof(WCHAR) )))
360         return E_OUTOFMEMORY;
361     memcpy( src_file, src_dir, src_len * sizeof(WCHAR) );
362     strcpyW( src_file + src_len, filename );
363 
364     if (!(dst_file = HeapAlloc( GetProcessHeap(), 0, (dst_len + len + 1) * sizeof(WCHAR) )))
365     {
366         HeapFree( GetProcessHeap(), 0, src_file );
367         return E_OUTOFMEMORY;
368     }
369     memcpy( dst_file, dst_dir, dst_len * sizeof(WCHAR) );
370     strcpyW( dst_file + dst_len, filename );
371 
372     if (!CopyFileW( src_file, dst_file, FALSE )) hr = HRESULT_FROM_WIN32( GetLastError() );
373     HeapFree( GetProcessHeap(), 0, src_file );
374     HeapFree( GetProcessHeap(), 0, dst_file );
375     return hr;
376 }
377 
378 static HRESULT WINAPI IAssemblyCacheImpl_InstallAssembly(IAssemblyCache *iface,
379                                                          DWORD dwFlags,
380                                                          LPCWSTR pszManifestFilePath,
381                                                          LPCFUSION_INSTALL_REFERENCE pRefData)
382 {
383     static const WCHAR format[] =
384         {'%','s','\\','%','s','\\','%','s','_','_','%','s','\\',0};
385     static const WCHAR format_v40[] =
386         {'%','s','\\','%','s','\\','v','4','.','0','_','%','s','_','_','%','s','\\',0};
387     static const WCHAR ext_exe[] = {'.','e','x','e',0};
388     static const WCHAR ext_dll[] = {'.','d','l','l',0};
389     IAssemblyCacheImpl *cache = impl_from_IAssemblyCache(iface);
390     ASSEMBLY *assembly;
391     const WCHAR *extension, *filename, *src_dir;
392     WCHAR *name = NULL, *token = NULL, *version = NULL, *asmpath = NULL;
393     WCHAR asmdir[MAX_PATH], *p, **external_files = NULL, *dst_dir = NULL;
394     PEKIND architecture;
395     char *clr_version;
396     DWORD i, count = 0, src_len, dst_len = sizeof(format_v40)/sizeof(format_v40[0]);
397     HRESULT hr;
398 
399     TRACE("(%p, %d, %s, %p)\n", iface, dwFlags,
400           debugstr_w(pszManifestFilePath), pRefData);
401 
402     if (!pszManifestFilePath || !*pszManifestFilePath)
403         return E_INVALIDARG;
404 
405     if (!(extension = strrchrW(pszManifestFilePath, '.')))
406         return HRESULT_FROM_WIN32(ERROR_INVALID_NAME);
407 
408     if (lstrcmpiW(extension, ext_exe) && lstrcmpiW(extension, ext_dll))
409         return HRESULT_FROM_WIN32(ERROR_INVALID_NAME);
410 
411     if (GetFileAttributesW(pszManifestFilePath) == INVALID_FILE_ATTRIBUTES)
412         return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
413 
414     hr = assembly_create(&assembly, pszManifestFilePath);
415     if (FAILED(hr))
416     {
417         hr = COR_E_ASSEMBLYEXPECTED;
418         goto done;
419     }
420 
421     hr = assembly_get_name(assembly, &name);
422     if (FAILED(hr))
423         goto done;
424 
425     hr = assembly_get_pubkey_token(assembly, &token);
426     if (FAILED(hr))
427         goto done;
428 
429     hr = assembly_get_version(assembly, &version);
430     if (FAILED(hr))
431         goto done;
432 
433     hr = assembly_get_runtime_version(assembly, &clr_version);
434     if (FAILED(hr))
435         goto done;
436 
437     hr = assembly_get_external_files(assembly, &external_files, &count);
438     if (FAILED(hr))
439         goto done;
440 
441     cache_lock( cache );
442 
443     architecture = assembly_get_architecture(assembly);
444     get_assembly_directory(asmdir, MAX_PATH, clr_version, architecture);
445 
446     dst_len += strlenW(asmdir) + strlenW(name) + strlenW(version) + strlenW(token);
447     if (!(dst_dir = HeapAlloc(GetProcessHeap(), 0, dst_len * sizeof(WCHAR))))
448     {
449         hr = E_OUTOFMEMORY;
450         goto done;
451     }
452     if (!strcmp(clr_version, "v4.0.30319"))
453         dst_len = sprintfW(dst_dir, format_v40, asmdir, name, version, token);
454     else
455         dst_len = sprintfW(dst_dir, format, asmdir, name, version, token);
456 
457     create_full_path(dst_dir);
458 
459     hr = assembly_get_path(assembly, &asmpath);
460     if (FAILED(hr))
461         goto done;
462 
463     if ((p = strrchrW(asmpath, '\\')))
464     {
465         filename = p + 1;
466         src_dir  = asmpath;
467         src_len  = filename - asmpath;
468     }
469     else
470     {
471         filename = asmpath;
472         src_dir  = NULL;
473         src_len  = 0;
474     }
475     hr = copy_file(src_dir, src_len, dst_dir, dst_len, filename);
476     if (FAILED(hr))
477         goto done;
478 
479     for (i = 0; i < count; i++)
480     {
481         hr = copy_file(src_dir, src_len, dst_dir, dst_len, external_files[i]);
482         if (FAILED(hr))
483             break;
484     }
485 
486 done:
487     HeapFree(GetProcessHeap(), 0, name);
488     HeapFree(GetProcessHeap(), 0, token);
489     HeapFree(GetProcessHeap(), 0, version);
490     HeapFree(GetProcessHeap(), 0, asmpath);
491     HeapFree(GetProcessHeap(), 0, dst_dir);
492     for (i = 0; i < count; i++) HeapFree(GetProcessHeap(), 0, external_files[i]);
493     HeapFree(GetProcessHeap(), 0, external_files);
494     assembly_release(assembly);
495     cache_unlock( cache );
496     return hr;
497 }
498 
499 static const IAssemblyCacheVtbl AssemblyCacheVtbl = {
500     IAssemblyCacheImpl_QueryInterface,
501     IAssemblyCacheImpl_AddRef,
502     IAssemblyCacheImpl_Release,
503     IAssemblyCacheImpl_UninstallAssembly,
504     IAssemblyCacheImpl_QueryAssemblyInfo,
505     IAssemblyCacheImpl_CreateAssemblyCacheItem,
506     IAssemblyCacheImpl_CreateAssemblyScavenger,
507     IAssemblyCacheImpl_InstallAssembly
508 };
509 
510 /******************************************************************
511  *  CreateAssemblyCache   (FUSION.@)
512  */
513 HRESULT WINAPI CreateAssemblyCache(IAssemblyCache **ppAsmCache, DWORD dwReserved)
514 {
515     IAssemblyCacheImpl *cache;
516 
517     TRACE("(%p, %d)\n", ppAsmCache, dwReserved);
518 
519     if (!ppAsmCache)
520         return E_INVALIDARG;
521 
522     *ppAsmCache = NULL;
523 
524     cache = HeapAlloc(GetProcessHeap(), 0, sizeof(IAssemblyCacheImpl));
525     if (!cache)
526         return E_OUTOFMEMORY;
527 
528     cache->IAssemblyCache_iface.lpVtbl = &AssemblyCacheVtbl;
529     cache->ref = 1;
530     cache->lock = CreateMutexW( NULL, FALSE, cache_mutex_nameW );
531     if (!cache->lock)
532     {
533         HeapFree( GetProcessHeap(), 0, cache );
534         return HRESULT_FROM_WIN32( GetLastError() );
535     }
536     *ppAsmCache = &cache->IAssemblyCache_iface;
537     return S_OK;
538 }
539 
540 /* IAssemblyCacheItem */
541 
542 typedef struct {
543     IAssemblyCacheItem IAssemblyCacheItem_iface;
544 
545     LONG ref;
546 } IAssemblyCacheItemImpl;
547 
548 static inline IAssemblyCacheItemImpl *impl_from_IAssemblyCacheItem(IAssemblyCacheItem *iface)
549 {
550     return CONTAINING_RECORD(iface, IAssemblyCacheItemImpl, IAssemblyCacheItem_iface);
551 }
552 
553 static HRESULT WINAPI IAssemblyCacheItemImpl_QueryInterface(IAssemblyCacheItem *iface,
554                                                             REFIID riid, LPVOID *ppobj)
555 {
556     IAssemblyCacheItemImpl *This = impl_from_IAssemblyCacheItem(iface);
557 
558     TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppobj);
559 
560     *ppobj = NULL;
561 
562     if (IsEqualIID(riid, &IID_IUnknown) ||
563         IsEqualIID(riid, &IID_IAssemblyCacheItem))
564     {
565         IAssemblyCacheItem_AddRef(iface);
566         *ppobj = This;
567         return S_OK;
568     }
569 
570     WARN("(%p, %s, %p): not found\n", This, debugstr_guid(riid), ppobj);
571     return E_NOINTERFACE;
572 }
573 
574 static ULONG WINAPI IAssemblyCacheItemImpl_AddRef(IAssemblyCacheItem *iface)
575 {
576     IAssemblyCacheItemImpl *This = impl_from_IAssemblyCacheItem(iface);
577     ULONG refCount = InterlockedIncrement(&This->ref);
578 
579     TRACE("(%p)->(ref before = %u)\n", This, refCount - 1);
580 
581     return refCount;
582 }
583 
584 static ULONG WINAPI IAssemblyCacheItemImpl_Release(IAssemblyCacheItem *iface)
585 {
586     IAssemblyCacheItemImpl *This = impl_from_IAssemblyCacheItem(iface);
587     ULONG refCount = InterlockedDecrement(&This->ref);
588 
589     TRACE("(%p)->(ref before = %u)\n", This, refCount + 1);
590 
591     if (!refCount)
592         HeapFree(GetProcessHeap(), 0, This);
593 
594     return refCount;
595 }
596 
597 static HRESULT WINAPI IAssemblyCacheItemImpl_CreateStream(IAssemblyCacheItem *iface,
598                                                         DWORD dwFlags,
599                                                         LPCWSTR pszStreamName,
600                                                         DWORD dwFormat,
601                                                         DWORD dwFormatFlags,
602                                                         IStream **ppIStream,
603                                                         ULARGE_INTEGER *puliMaxSize)
604 {
605     FIXME("(%p, %d, %s, %d, %d, %p, %p) stub!\n", iface, dwFlags,
606           debugstr_w(pszStreamName), dwFormat, dwFormatFlags, ppIStream, puliMaxSize);
607 
608     return E_NOTIMPL;
609 }
610 
611 static HRESULT WINAPI IAssemblyCacheItemImpl_Commit(IAssemblyCacheItem *iface,
612                                                   DWORD dwFlags,
613                                                   ULONG *pulDisposition)
614 {
615     FIXME("(%p, %d, %p) stub!\n", iface, dwFlags, pulDisposition);
616     return E_NOTIMPL;
617 }
618 
619 static HRESULT WINAPI IAssemblyCacheItemImpl_AbortItem(IAssemblyCacheItem *iface)
620 {
621     FIXME("(%p) stub!\n", iface);
622     return E_NOTIMPL;
623 }
624 
625 static const IAssemblyCacheItemVtbl AssemblyCacheItemVtbl = {
626     IAssemblyCacheItemImpl_QueryInterface,
627     IAssemblyCacheItemImpl_AddRef,
628     IAssemblyCacheItemImpl_Release,
629     IAssemblyCacheItemImpl_CreateStream,
630     IAssemblyCacheItemImpl_Commit,
631     IAssemblyCacheItemImpl_AbortItem
632 };
633