2 * IAssemblyCache implementation
4 * Copyright 2008 James Hawkins
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.
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.
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
38 #include "fusionpriv.h"
39 #include "wine/debug.h"
40 #include "wine/unicode.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(fusion
);
45 IAssemblyCache IAssemblyCache_iface
;
52 IAssemblyCacheItem IAssemblyCacheItem_iface
;
55 } IAssemblyCacheItemImpl
;
57 static const WCHAR cache_mutex_nameW
[] =
58 {'_','_','W','I','N','E','_','F','U','S','I','O','N','_','C','A','C','H','E','_','M','U','T','E','X','_','_',0};
60 static BOOL
create_full_path(LPCWSTR path
)
66 new_path
= HeapAlloc(GetProcessHeap(), 0, (strlenW(path
) + 1) * sizeof(WCHAR
));
70 strcpyW(new_path
, path
);
72 while ((len
= strlenW(new_path
)) && new_path
[len
- 1] == '\\')
73 new_path
[len
- 1] = 0;
75 while (!CreateDirectoryW(new_path
, NULL
))
78 DWORD last_error
= GetLastError();
80 if(last_error
== ERROR_ALREADY_EXISTS
)
83 if(last_error
!= ERROR_PATH_NOT_FOUND
)
89 if(!(slash
= strrchrW(new_path
, '\\')))
95 len
= slash
- new_path
;
97 if(!create_full_path(new_path
))
103 new_path
[len
] = '\\';
106 HeapFree(GetProcessHeap(), 0, new_path
);
110 static BOOL
get_assembly_directory(LPWSTR dir
, DWORD size
, const char *version
, PEKIND architecture
)
112 static const WCHAR dotnet
[] = {'\\','M','i','c','r','o','s','o','f','t','.','N','E','T','\\',0};
113 static const WCHAR gac
[] = {'\\','a','s','s','e','m','b','l','y','\\','G','A','C',0};
114 static const WCHAR msil
[] = {'_','M','S','I','L',0};
115 static const WCHAR x86
[] = {'_','3','2',0};
116 static const WCHAR amd64
[] = {'_','6','4',0};
117 DWORD len
= GetWindowsDirectoryW(dir
, size
);
119 if (!strcmp(version
, "v4.0.30319"))
121 strcpyW(dir
+ len
, dotnet
);
122 len
+= sizeof(dotnet
)/sizeof(WCHAR
) -1;
123 strcpyW(dir
+ len
, gac
+ 1);
124 len
+= sizeof(gac
)/sizeof(WCHAR
) - 2;
128 strcpyW(dir
+ len
, gac
);
129 len
+= sizeof(gac
)/sizeof(WCHAR
) - 1;
131 switch (architecture
)
137 strcpyW(dir
+ len
, msil
);
141 strcpyW(dir
+ len
, x86
);
145 strcpyW(dir
+ len
, amd64
);
149 WARN("unhandled architecture %u\n", architecture
);
157 static inline IAssemblyCacheImpl
*impl_from_IAssemblyCache(IAssemblyCache
*iface
)
159 return CONTAINING_RECORD(iface
, IAssemblyCacheImpl
, IAssemblyCache_iface
);
162 static HRESULT WINAPI
IAssemblyCacheImpl_QueryInterface(IAssemblyCache
*iface
,
163 REFIID riid
, LPVOID
*ppobj
)
165 IAssemblyCacheImpl
*This
= impl_from_IAssemblyCache(iface
);
167 TRACE("(%p, %s, %p)\n", This
, debugstr_guid(riid
), ppobj
);
171 if (IsEqualIID(riid
, &IID_IUnknown
) ||
172 IsEqualIID(riid
, &IID_IAssemblyCache
))
174 IAssemblyCache_AddRef(iface
);
175 *ppobj
= &This
->IAssemblyCache_iface
;
179 WARN("(%p, %s, %p): not found\n", This
, debugstr_guid(riid
), ppobj
);
180 return E_NOINTERFACE
;
183 static ULONG WINAPI
IAssemblyCacheImpl_AddRef(IAssemblyCache
*iface
)
185 IAssemblyCacheImpl
*This
= impl_from_IAssemblyCache(iface
);
186 ULONG refCount
= InterlockedIncrement(&This
->ref
);
188 TRACE("(%p)->(ref before = %u)\n", This
, refCount
- 1);
193 static ULONG WINAPI
IAssemblyCacheImpl_Release(IAssemblyCache
*iface
)
195 IAssemblyCacheImpl
*cache
= impl_from_IAssemblyCache(iface
);
196 ULONG refCount
= InterlockedDecrement( &cache
->ref
);
198 TRACE("(%p)->(ref before = %u)\n", cache
, refCount
+ 1);
202 CloseHandle( cache
->lock
);
203 HeapFree( GetProcessHeap(), 0, cache
);
208 static void cache_lock( IAssemblyCacheImpl
*cache
)
210 WaitForSingleObject( cache
->lock
, INFINITE
);
213 static void cache_unlock( IAssemblyCacheImpl
*cache
)
215 ReleaseMutex( cache
->lock
);
218 static HRESULT WINAPI
IAssemblyCacheImpl_UninstallAssembly(IAssemblyCache
*iface
,
220 LPCWSTR pszAssemblyName
,
221 LPCFUSION_INSTALL_REFERENCE pRefData
,
222 ULONG
*pulDisposition
)
225 IAssemblyCacheImpl
*cache
= impl_from_IAssemblyCache(iface
);
226 IAssemblyName
*asmname
, *next
= NULL
;
227 IAssemblyEnum
*asmenum
= NULL
;
228 WCHAR
*p
, *path
= NULL
;
232 TRACE("(%p, 0%08x, %s, %p, %p)\n", iface
, dwFlags
,
233 debugstr_w(pszAssemblyName
), pRefData
, pulDisposition
);
237 FIXME("application reference not supported\n");
240 hr
= CreateAssemblyNameObject( &asmname
, pszAssemblyName
, CANOF_PARSE_DISPLAY_NAME
, NULL
);
246 hr
= CreateAssemblyEnum( &asmenum
, NULL
, asmname
, ASM_CACHE_GAC
, NULL
);
250 hr
= IAssemblyEnum_GetNextAssembly( asmenum
, NULL
, &next
, 0 );
254 *pulDisposition
= IASSEMBLYCACHE_UNINSTALL_DISPOSITION_ALREADY_UNINSTALLED
;
257 hr
= IAssemblyName_GetPath( next
, NULL
, &len
);
258 if (hr
!= HRESULT_FROM_WIN32( ERROR_INSUFFICIENT_BUFFER
))
261 if (!(path
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) )))
266 hr
= IAssemblyName_GetPath( next
, path
, &len
);
270 if (DeleteFileW( path
))
272 if ((p
= strrchrW( path
, '\\' )))
275 RemoveDirectoryW( path
);
276 if ((p
= strrchrW( path
, '\\' )))
279 RemoveDirectoryW( path
);
282 disp
= IASSEMBLYCACHE_UNINSTALL_DISPOSITION_UNINSTALLED
;
287 disp
= IASSEMBLYCACHE_UNINSTALL_DISPOSITION_ALREADY_UNINSTALLED
;
290 if (pulDisposition
) *pulDisposition
= disp
;
293 IAssemblyName_Release( asmname
);
294 if (next
) IAssemblyName_Release( next
);
295 if (asmenum
) IAssemblyEnum_Release( asmenum
);
296 HeapFree( GetProcessHeap(), 0, path
);
297 cache_unlock( cache
);
301 static HRESULT WINAPI
IAssemblyCacheImpl_QueryAssemblyInfo(IAssemblyCache
*iface
,
303 LPCWSTR pszAssemblyName
,
304 ASSEMBLY_INFO
*pAsmInfo
)
306 IAssemblyCacheImpl
*cache
= impl_from_IAssemblyCache(iface
);
307 IAssemblyName
*asmname
, *next
= NULL
;
308 IAssemblyEnum
*asmenum
= NULL
;
311 TRACE("(%p, %d, %s, %p)\n", iface
, dwFlags
,
312 debugstr_w(pszAssemblyName
), pAsmInfo
);
316 if (pAsmInfo
->cbAssemblyInfo
== 0)
317 pAsmInfo
->cbAssemblyInfo
= sizeof(ASSEMBLY_INFO
);
318 else if (pAsmInfo
->cbAssemblyInfo
!= sizeof(ASSEMBLY_INFO
))
322 hr
= CreateAssemblyNameObject(&asmname
, pszAssemblyName
,
323 CANOF_PARSE_DISPLAY_NAME
, NULL
);
329 hr
= CreateAssemblyEnum(&asmenum
, NULL
, asmname
, ASM_CACHE_GAC
, NULL
);
335 hr
= IAssemblyEnum_GetNextAssembly(asmenum
, NULL
, &next
, 0);
338 hr
= HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND
);
341 hr
= IAssemblyName_IsEqual(asmname
, next
, ASM_CMPF_IL_ALL
);
342 if (hr
== S_OK
) break;
348 hr
= IAssemblyName_GetPath(next
, pAsmInfo
->pszCurrentAssemblyPathBuf
, &pAsmInfo
->cchBuf
);
350 pAsmInfo
->dwAssemblyFlags
= ASSEMBLYINFO_FLAG_INSTALLED
;
353 IAssemblyName_Release(asmname
);
354 if (next
) IAssemblyName_Release(next
);
355 if (asmenum
) IAssemblyEnum_Release(asmenum
);
356 cache_unlock( cache
);
360 static const IAssemblyCacheItemVtbl AssemblyCacheItemVtbl
;
362 static HRESULT WINAPI
IAssemblyCacheImpl_CreateAssemblyCacheItem(IAssemblyCache
*iface
,
365 IAssemblyCacheItem
**ppAsmItem
,
366 LPCWSTR pszAssemblyName
)
368 IAssemblyCacheItemImpl
*item
;
370 FIXME("(%p, %d, %p, %p, %s) semi-stub!\n", iface
, dwFlags
, pvReserved
,
371 ppAsmItem
, debugstr_w(pszAssemblyName
));
378 item
= HeapAlloc(GetProcessHeap(), 0, sizeof(IAssemblyCacheItemImpl
));
380 return E_OUTOFMEMORY
;
382 item
->IAssemblyCacheItem_iface
.lpVtbl
= &AssemblyCacheItemVtbl
;
385 *ppAsmItem
= &item
->IAssemblyCacheItem_iface
;
389 static HRESULT WINAPI
IAssemblyCacheImpl_CreateAssemblyScavenger(IAssemblyCache
*iface
,
390 IUnknown
**ppUnkReserved
)
392 FIXME("(%p, %p) stub!\n", iface
, ppUnkReserved
);
396 static HRESULT
copy_file( const WCHAR
*src_dir
, DWORD src_len
, const WCHAR
*dst_dir
, DWORD dst_len
,
397 const WCHAR
*filename
)
399 WCHAR
*src_file
, *dst_file
;
400 DWORD len
= strlenW( filename
);
403 if (!(src_file
= HeapAlloc( GetProcessHeap(), 0, (src_len
+ len
+ 1) * sizeof(WCHAR
) )))
404 return E_OUTOFMEMORY
;
405 memcpy( src_file
, src_dir
, src_len
* sizeof(WCHAR
) );
406 strcpyW( src_file
+ src_len
, filename
);
408 if (!(dst_file
= HeapAlloc( GetProcessHeap(), 0, (dst_len
+ len
+ 1) * sizeof(WCHAR
) )))
410 HeapFree( GetProcessHeap(), 0, src_file
);
411 return E_OUTOFMEMORY
;
413 memcpy( dst_file
, dst_dir
, dst_len
* sizeof(WCHAR
) );
414 strcpyW( dst_file
+ dst_len
, filename
);
416 if (!CopyFileW( src_file
, dst_file
, FALSE
)) hr
= HRESULT_FROM_WIN32( GetLastError() );
417 HeapFree( GetProcessHeap(), 0, src_file
);
418 HeapFree( GetProcessHeap(), 0, dst_file
);
422 static HRESULT WINAPI
IAssemblyCacheImpl_InstallAssembly(IAssemblyCache
*iface
,
424 LPCWSTR pszManifestFilePath
,
425 LPCFUSION_INSTALL_REFERENCE pRefData
)
427 static const WCHAR format
[] =
428 {'%','s','\\','%','s','\\','%','s','_','_','%','s','\\',0};
429 static const WCHAR format_v40
[] =
430 {'%','s','\\','%','s','\\','v','4','.','0','_','%','s','_','_','%','s','\\',0};
431 static const WCHAR ext_exe
[] = {'.','e','x','e',0};
432 static const WCHAR ext_dll
[] = {'.','d','l','l',0};
433 IAssemblyCacheImpl
*cache
= impl_from_IAssemblyCache(iface
);
435 const WCHAR
*extension
, *filename
, *src_dir
;
436 WCHAR
*name
= NULL
, *token
= NULL
, *version
= NULL
, *asmpath
= NULL
;
437 WCHAR asmdir
[MAX_PATH
], *p
, **external_files
= NULL
, *dst_dir
= NULL
;
440 DWORD i
, count
= 0, src_len
, dst_len
= sizeof(format_v40
)/sizeof(format_v40
[0]);
443 TRACE("(%p, %d, %s, %p)\n", iface
, dwFlags
,
444 debugstr_w(pszManifestFilePath
), pRefData
);
446 if (!pszManifestFilePath
|| !*pszManifestFilePath
)
449 if (!(extension
= strrchrW(pszManifestFilePath
, '.')))
450 return HRESULT_FROM_WIN32(ERROR_INVALID_NAME
);
452 if (lstrcmpiW(extension
, ext_exe
) && lstrcmpiW(extension
, ext_dll
))
453 return HRESULT_FROM_WIN32(ERROR_INVALID_NAME
);
455 if (GetFileAttributesW(pszManifestFilePath
) == INVALID_FILE_ATTRIBUTES
)
456 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND
);
458 hr
= assembly_create(&assembly
, pszManifestFilePath
);
461 hr
= COR_E_ASSEMBLYEXPECTED
;
465 hr
= assembly_get_name(assembly
, &name
);
469 hr
= assembly_get_pubkey_token(assembly
, &token
);
473 hr
= assembly_get_version(assembly
, &version
);
477 hr
= assembly_get_runtime_version(assembly
, &clr_version
);
481 hr
= assembly_get_external_files(assembly
, &external_files
, &count
);
487 architecture
= assembly_get_architecture(assembly
);
488 get_assembly_directory(asmdir
, MAX_PATH
, clr_version
, architecture
);
490 dst_len
+= strlenW(asmdir
) + strlenW(name
) + strlenW(version
) + strlenW(token
);
491 if (!(dst_dir
= HeapAlloc(GetProcessHeap(), 0, dst_len
* sizeof(WCHAR
))))
496 if (!strcmp(clr_version
, "v4.0.30319"))
497 dst_len
= sprintfW(dst_dir
, format_v40
, asmdir
, name
, version
, token
);
499 dst_len
= sprintfW(dst_dir
, format
, asmdir
, name
, version
, token
);
501 create_full_path(dst_dir
);
503 hr
= assembly_get_path(assembly
, &asmpath
);
507 if ((p
= strrchrW(asmpath
, '\\')))
511 src_len
= filename
- asmpath
;
519 hr
= copy_file(src_dir
, src_len
, dst_dir
, dst_len
, filename
);
523 for (i
= 0; i
< count
; i
++)
525 hr
= copy_file(src_dir
, src_len
, dst_dir
, dst_len
, external_files
[i
]);
531 HeapFree(GetProcessHeap(), 0, name
);
532 HeapFree(GetProcessHeap(), 0, token
);
533 HeapFree(GetProcessHeap(), 0, version
);
534 HeapFree(GetProcessHeap(), 0, asmpath
);
535 HeapFree(GetProcessHeap(), 0, dst_dir
);
536 for (i
= 0; i
< count
; i
++) HeapFree(GetProcessHeap(), 0, external_files
[i
]);
537 HeapFree(GetProcessHeap(), 0, external_files
);
538 assembly_release(assembly
);
539 cache_unlock( cache
);
543 static const IAssemblyCacheVtbl AssemblyCacheVtbl
= {
544 IAssemblyCacheImpl_QueryInterface
,
545 IAssemblyCacheImpl_AddRef
,
546 IAssemblyCacheImpl_Release
,
547 IAssemblyCacheImpl_UninstallAssembly
,
548 IAssemblyCacheImpl_QueryAssemblyInfo
,
549 IAssemblyCacheImpl_CreateAssemblyCacheItem
,
550 IAssemblyCacheImpl_CreateAssemblyScavenger
,
551 IAssemblyCacheImpl_InstallAssembly
554 /******************************************************************
555 * CreateAssemblyCache (FUSION.@)
557 HRESULT WINAPI
CreateAssemblyCache(IAssemblyCache
**ppAsmCache
, DWORD dwReserved
)
559 IAssemblyCacheImpl
*cache
;
561 TRACE("(%p, %d)\n", ppAsmCache
, dwReserved
);
568 cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(IAssemblyCacheImpl
));
570 return E_OUTOFMEMORY
;
572 cache
->IAssemblyCache_iface
.lpVtbl
= &AssemblyCacheVtbl
;
574 cache
->lock
= CreateMutexW( NULL
, FALSE
, cache_mutex_nameW
);
577 HeapFree( GetProcessHeap(), 0, cache
);
578 return HRESULT_FROM_WIN32( GetLastError() );
580 *ppAsmCache
= &cache
->IAssemblyCache_iface
;
584 /* IAssemblyCacheItem */
586 static inline IAssemblyCacheItemImpl
*impl_from_IAssemblyCacheItem(IAssemblyCacheItem
*iface
)
588 return CONTAINING_RECORD(iface
, IAssemblyCacheItemImpl
, IAssemblyCacheItem_iface
);
591 static HRESULT WINAPI
IAssemblyCacheItemImpl_QueryInterface(IAssemblyCacheItem
*iface
,
592 REFIID riid
, LPVOID
*ppobj
)
594 IAssemblyCacheItemImpl
*This
= impl_from_IAssemblyCacheItem(iface
);
596 TRACE("(%p, %s, %p)\n", This
, debugstr_guid(riid
), ppobj
);
600 if (IsEqualIID(riid
, &IID_IUnknown
) ||
601 IsEqualIID(riid
, &IID_IAssemblyCacheItem
))
603 IAssemblyCacheItem_AddRef(iface
);
604 *ppobj
= &This
->IAssemblyCacheItem_iface
;
608 WARN("(%p, %s, %p): not found\n", This
, debugstr_guid(riid
), ppobj
);
609 return E_NOINTERFACE
;
612 static ULONG WINAPI
IAssemblyCacheItemImpl_AddRef(IAssemblyCacheItem
*iface
)
614 IAssemblyCacheItemImpl
*This
= impl_from_IAssemblyCacheItem(iface
);
615 ULONG refCount
= InterlockedIncrement(&This
->ref
);
617 TRACE("(%p)->(ref before = %u)\n", This
, refCount
- 1);
622 static ULONG WINAPI
IAssemblyCacheItemImpl_Release(IAssemblyCacheItem
*iface
)
624 IAssemblyCacheItemImpl
*This
= impl_from_IAssemblyCacheItem(iface
);
625 ULONG refCount
= InterlockedDecrement(&This
->ref
);
627 TRACE("(%p)->(ref before = %u)\n", This
, refCount
+ 1);
630 HeapFree(GetProcessHeap(), 0, This
);
635 static HRESULT WINAPI
IAssemblyCacheItemImpl_CreateStream(IAssemblyCacheItem
*iface
,
637 LPCWSTR pszStreamName
,
641 ULARGE_INTEGER
*puliMaxSize
)
643 FIXME("(%p, %d, %s, %d, %d, %p, %p) stub!\n", iface
, dwFlags
,
644 debugstr_w(pszStreamName
), dwFormat
, dwFormatFlags
, ppIStream
, puliMaxSize
);
649 static HRESULT WINAPI
IAssemblyCacheItemImpl_Commit(IAssemblyCacheItem
*iface
,
651 ULONG
*pulDisposition
)
653 FIXME("(%p, %d, %p) stub!\n", iface
, dwFlags
, pulDisposition
);
657 static HRESULT WINAPI
IAssemblyCacheItemImpl_AbortItem(IAssemblyCacheItem
*iface
)
659 FIXME("(%p) stub!\n", iface
);
663 static const IAssemblyCacheItemVtbl AssemblyCacheItemVtbl
= {
664 IAssemblyCacheItemImpl_QueryInterface
,
665 IAssemblyCacheItemImpl_AddRef
,
666 IAssemblyCacheItemImpl_Release
,
667 IAssemblyCacheItemImpl_CreateStream
,
668 IAssemblyCacheItemImpl_Commit
,
669 IAssemblyCacheItemImpl_AbortItem