|
|
#include <fusenetincludes.h>
#include <assemblycache.h>
#include <sxsapi.h>
#include "fusion.h"
#include "macros.h"
//BUGBUG - this is not localizeable ? could cause avalon a problem
// use shell apis instead.
#define WZ_CACHE_LOCALROOTDIR L"Local Settings\\My Programs\\"
#define WZ_TEMP_DIR L"__temp__\\"
#define WZ_MANIFEST_STAGING_DIR L"__temp__\\__manifests__\\"
#define WZ_SHARED_DIR L"__shared__\\"
#define WZ_WILDCARDSTRING L"*"
typedef HRESULT(*PFNGETCORSYSTEMDIRECTORY)(LPWSTR, DWORD, LPDWORD); typedef HRESULT (__stdcall *PFNCREATEASSEMBLYCACHE) (IAssemblyCache **ppAsmCache, DWORD dwReserved);
#define WZ_MSCOREE_DLL_NAME L"mscoree.dll"
#define GETCORSYSTEMDIRECTORY_FN_NAME "GetCORSystemDirectory"
#define CREATEASSEMBLYCACHE_FN_NAME "CreateAssemblyCache"
#define WZ_FUSION_DLL_NAME L"Fusion.dll"
IAssemblyCache* CAssemblyCache::g_pFusionAssemblyCache = NULL;
// ---------------------------------------------------------------------------
// CreateAssemblyCacheImport
// ---------------------------------------------------------------------------
HRESULT CreateAssemblyCacheImport( LPASSEMBLY_CACHE_IMPORT *ppAssemblyCacheImport, LPASSEMBLY_IDENTITY pAssemblyIdentity, DWORD dwFlags) { return CAssemblyCache::Retrieve(ppAssemblyCacheImport, pAssemblyIdentity, dwFlags); }
// ---------------------------------------------------------------------------
// CreateAssemblyCacheEmit
// ---------------------------------------------------------------------------
HRESULT CreateAssemblyCacheEmit( LPASSEMBLY_CACHE_EMIT *ppAssemblyCacheEmit, LPASSEMBLY_CACHE_EMIT pAssemblyCacheEmit, DWORD dwFlags) { return CAssemblyCache::Create(ppAssemblyCacheEmit, pAssemblyCacheEmit, dwFlags); }
// ---------------------------------------------------------------------------
// Retrieve
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::Retrieve( LPASSEMBLY_CACHE_IMPORT *ppAssemblyCacheImport, LPASSEMBLY_IDENTITY pAssemblyIdentity, DWORD dwFlags) { HRESULT hr = S_OK; MAKE_ERROR_MACROS_STATIC(hr); LPWSTR pwzSearchDisplayName = NULL; BOOL bNewAsmId = FALSE; LPWSTR pwzBuf = NULL; DWORD dwCC = 0; CAssemblyCache *pAssemblyCache = NULL;
CString sManifestFilename; CString sDisplayName;
IF_FALSE_EXIT(dwFlags == CACHEIMP_CREATE_RETRIEVE_MAX || dwFlags == CACHEIMP_CREATE_RETRIEVE || dwFlags == CACHEIMP_CREATE_RESOLVE_REF || dwFlags == CACHEIMP_CREATE_RESOLVE_REF_EX, E_INVALIDARG);
IF_NULL_EXIT(pAssemblyIdentity, E_INVALIDARG);
IF_ALLOC_FAILED_EXIT(pAssemblyCache = new(CAssemblyCache));
IF_FAILED_EXIT(pAssemblyCache->Init(NULL, ASSEMBLY_CACHE_TYPE_APP | ASSEMBLY_CACHE_TYPE_IMPORT));
// get the identity name
IF_FALSE_EXIT(pAssemblyIdentity->GetAttribute(SXS_ASSEMBLY_IDENTITY_STD_ATTRIBUTE_NAME_NAME, &pwzBuf, &dwCC) == S_OK, E_INVALIDARG);
// filename of the manifest must be the same as the assembly name
// BUGBUG??: this implies manifest filename (and asm name) be remained unchange because
// the assembly name from the new AsmId is used for looking up in the older cached version...
IF_FAILED_EXIT(sManifestFilename.TakeOwnership(pwzBuf, dwCC)); IF_FAILED_EXIT(sManifestFilename.Append(L".manifest"));
if (dwFlags == CACHEIMP_CREATE_RETRIEVE_MAX) { LPASSEMBLY_IDENTITY pNewAsmId = NULL; IF_FAILED_EXIT(CloneAssemblyIdentity(pAssemblyIdentity, &pNewAsmId));
pAssemblyIdentity = pNewAsmId; bNewAsmId = TRUE; // force Version to be a wildcard
IF_FAILED_EXIT(pAssemblyIdentity->SetAttribute(SXS_ASSEMBLY_IDENTITY_STD_ATTRIBUTE_NAME_VERSION, WZ_WILDCARDSTRING, lstrlen(WZ_WILDCARDSTRING)+1)); }
if (dwFlags == CACHEIMP_CREATE_RETRIEVE_MAX || dwFlags == CACHEIMP_CREATE_RESOLVE_REF || dwFlags == CACHEIMP_CREATE_RESOLVE_REF_EX) { // issues: what if other then Version is already wildcarded? does version comparison make sense here?
IF_FAILED_EXIT(pAssemblyIdentity->GetDisplayName(ASMID_DISPLAYNAME_WILDCARDED, &pwzSearchDisplayName, &dwCC));
if ( (hr = SearchForHighestVersionInCache(&pwzBuf, pwzSearchDisplayName, CAssemblyCache::VISIBLE, pAssemblyCache) == S_OK)) { IF_FAILED_EXIT(sDisplayName.TakeOwnership(pwzBuf)); // BUGBUG - make GetDisplayName call getassemblyid/getdisplayname instead
IF_FAILED_EXIT((pAssemblyCache->_sDisplayName).Assign(sDisplayName)); } else { IF_FAILED_EXIT(hr);
// can't resolve
hr = S_FALSE;
if (dwFlags != CACHEIMP_CREATE_RESOLVE_REF_EX) goto exit; } }
if (dwFlags == CACHEIMP_CREATE_RETRIEVE || (hr == S_FALSE && dwFlags == CACHEIMP_CREATE_RESOLVE_REF_EX)) { // make the name anyway if resolving a ref that does not have any completed cache counterpart
// BUGBUG: this may no longer be necessary if shortcut code/UI changes - it's expecting a path
// plus this is inefficient as it searchs the disk at above, even if ref is fully qualified
IF_FAILED_EXIT(pAssemblyIdentity->GetDisplayName(ASMID_DISPLAYNAME_NOMANGLING, &pwzBuf, &dwCC)); IF_FAILED_EXIT(sDisplayName.TakeOwnership(pwzBuf, dwCC));
// BUGBUG - make GetDisplayName call getassemblyid/getdisplayname instead
IF_FAILED_EXIT((pAssemblyCache->_sDisplayName).Assign(sDisplayName)); } // Note: this will prepare for delay initializing _pManifestImport
IF_FAILED_EXIT((pAssemblyCache->_sManifestFileDir).Assign(pAssemblyCache->_sRootDir));
// build paths
IF_FAILED_EXIT((pAssemblyCache->_sManifestFileDir).Append(sDisplayName));
if (dwFlags == CACHEIMP_CREATE_RETRIEVE) { BOOL bExists = FALSE;
// simple check if dir is in cache or not
IF_FAILED_EXIT(CheckFileExistence((pAssemblyCache->_sManifestFileDir)._pwz, &bExists));
if (!bExists) { // cache dir not exists
hr = S_FALSE; goto exit; } }
IF_FAILED_EXIT((pAssemblyCache->_sManifestFileDir).Append(L"\\"));
IF_FAILED_EXIT((pAssemblyCache->_sManifestFilePath).Assign(pAssemblyCache->_sManifestFileDir));
IF_FAILED_EXIT((pAssemblyCache->_sManifestFilePath).Append(sManifestFilename)); *ppAssemblyCacheImport = static_cast<IAssemblyCacheImport*> (pAssemblyCache);
(*ppAssemblyCacheImport)->AddRef();
exit:
SAFEDELETEARRAY(pwzSearchDisplayName);
if (bNewAsmId) SAFERELEASE(pAssemblyIdentity);
SAFERELEASE(pAssemblyCache); return hr; }
// ---------------------------------------------------------------------------
// Create
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::Create( LPASSEMBLY_CACHE_EMIT *ppAssemblyCacheEmit, LPASSEMBLY_CACHE_EMIT pAssemblyCacheEmit, DWORD dwFlags) { HRESULT hr = S_OK; MAKE_ERROR_MACROS_STATIC(hr); CAssemblyCache *pAssemblyCache = NULL;
IF_ALLOC_FAILED_EXIT(pAssemblyCache = new(CAssemblyCache) );
IF_FAILED_EXIT(hr = pAssemblyCache->Init(static_cast<CAssemblyCache*> (pAssemblyCacheEmit), ASSEMBLY_CACHE_TYPE_APP | ASSEMBLY_CACHE_TYPE_EMIT));
*ppAssemblyCacheEmit = static_cast<IAssemblyCacheEmit*> (pAssemblyCache); (*ppAssemblyCacheEmit)->AddRef(); exit:
SAFERELEASE(pAssemblyCache);
return hr; }
// ---------------------------------------------------------------------------
// FindVersionInDisplayName
// ---------------------------------------------------------------------------
LPCWSTR CAssemblyCache::FindVersionInDisplayName(LPCWSTR pwzDisplayName) { int cNumUnderscoreFromEndToVersionString = 2; int count = 0; int ccLen = lstrlen(pwzDisplayName); LPWSTR pwz = (LPWSTR) (pwzDisplayName+ccLen-1); LPWSTR pwzRetVal = NULL;
// return a pointer to the start of Version string inside a displayName
while (*pwz != NULL && pwz > pwzDisplayName) { if (*pwz == L'_') count++;
if (count == cNumUnderscoreFromEndToVersionString) break;
pwz--; }
if (count == cNumUnderscoreFromEndToVersionString) pwzRetVal = ++pwz;
return pwzRetVal; }
// ---------------------------------------------------------------------------
// CompareVersion
// ---------------------------------------------------------------------------
int CAssemblyCache::CompareVersion(LPCWSTR pwzVersion1, LPCWSTR pwzVersion2) { // BUGBUG: this should compare version by its major minor build revision!
// possible break if V1=10.0.0.0 and V2=2.0.0.0?
// plus pwzVersion1 is something like "1.0.0.0_en"
return wcscmp(pwzVersion1, pwzVersion2); // This is not used....
}
// ---------------------------------------------------------------------------
// SearchForHighestVersionInCache
// Look for a copy in cache that has the highest version and the specified status
// pwzSearchDisplayName should really be created from a partial ref
//
// return: S_OK - found a version from the ref
// S_FALSE - not found any version from the ref, or
// ref not partial and that version is not there/not in that status
// E_*
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::SearchForHighestVersionInCache(LPWSTR *ppwzResultDisplayName, LPWSTR pwzSearchDisplayName, CAssemblyCache::CacheStatus eCacheStatus, CAssemblyCache* pCache) { HRESULT hr = S_OK; MAKE_ERROR_MACROS_STATIC(hr); HANDLE hFind = INVALID_HANDLE_VALUE; WIN32_FIND_DATA fdAppDir; DWORD dwLastError = 0; BOOL fFound = FALSE;
CString sDisplayName; CString sSearchPath;
*ppwzResultDisplayName = NULL;
sDisplayName.Assign(pwzSearchDisplayName); IF_FAILED_EXIT(sSearchPath.Assign(pCache->_sRootDir));
IF_FAILED_EXIT(sSearchPath.Append(sDisplayName));
hFind = FindFirstFileEx(sSearchPath._pwz, FindExInfoStandard, &fdAppDir, FindExSearchLimitToDirectories, NULL, 0); if (hFind == INVALID_HANDLE_VALUE) { hr = S_FALSE; goto exit; }
do { // ???? check file attribute to see if it's a directory? needed only if the file system does not support the filter...
// ???? check version string format?
if (CAssemblyCache::IsStatus(fdAppDir.cFileName, eCacheStatus)) { ULONGLONG ullMax; ULONGLONG ullCur;
LPCWSTR pwzVerStr = FindVersionInDisplayName(sDisplayName._pwz);
IF_FAILED_EXIT(ConvertVersionStrToULL(pwzVerStr, &ullMax));
pwzVerStr = FindVersionInDisplayName(fdAppDir.cFileName);
if(!pwzVerStr || FAILED(hr = ConvertVersionStrToULL(pwzVerStr, &ullCur)) ) { // ignore badly formed dirs; maybe we should delete them
continue; }
if (ullCur > ullMax) { IF_FAILED_EXIT(sDisplayName.Assign(fdAppDir.cFileName)); fFound = TRUE; } else if (ullCur == ullMax) fFound = TRUE; // else keep the newest
}
} while(FindNextFile(hFind, &fdAppDir));
if( (dwLastError = GetLastError()) != ERROR_NO_MORE_FILES) { IF_WIN32_FAILED_EXIT(dwLastError); }
if (fFound) { sDisplayName.ReleaseOwnership(ppwzResultDisplayName); hr = S_OK; } else hr = S_FALSE;
exit: if (hFind != INVALID_HANDLE_VALUE) { if (!FindClose(hFind) && SUCCEEDED(hr)) // don't overwrite if we already have useful hr.
{ ASSERT(0); hr = FusionpHresultFromLastError(); } }
return hr; }
// ---------------------------------------------------------------------------
// CreateFusionAssemblyCacheEx
// ---------------------------------------------------------------------------
HRESULT CreateFusionAssemblyCacheEx (IAssemblyCache **ppFusionAsmCache) { HRESULT hr = S_OK; hr = CAssemblyCache::CreateFusionAssemblyCache(ppFusionAsmCache); return hr; }
// ---------------------------------------------------------------------------
// ctor
// ---------------------------------------------------------------------------
CAssemblyCache::CAssemblyCache() : _dwSig('hcac'), _cRef(1), _hr(S_OK), _dwFlags(0), _pManifestImport(NULL), _pAssemblyId(NULL) {}
// ---------------------------------------------------------------------------
// dtor
// ---------------------------------------------------------------------------
CAssemblyCache::~CAssemblyCache() { SAFERELEASE(_pManifestImport); SAFERELEASE(_pAssemblyId); /*
if( _hr != S_OK) RemoveDirectoryAndChildren(_sManifestFileDir._pwz); */ }
// ---------------------------------------------------------------------------
// Init
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::Init(CAssemblyCache *pAssemblyCache, DWORD dwFlags) { _dwFlags = dwFlags;
if (!pAssemblyCache) { if (_dwFlags & ASSEMBLY_CACHE_TYPE_APP) { if (_dwFlags & ASSEMBLY_CACHE_TYPE_IMPORT) IF_FAILED_EXIT( GetCacheRootDir(_sRootDir, Base)); else if (_dwFlags & ASSEMBLY_CACHE_TYPE_EMIT) IF_FAILED_EXIT( GetCacheRootDir(_sRootDir, Temp)); } else if (_dwFlags & ASSEMBLY_CACHE_TYPE_SHARED) { IF_FAILED_EXIT( GetCacheRootDir(_sRootDir, Shared)); } } else IF_FAILED_EXIT( _sRootDir.Assign(pAssemblyCache->_sManifestFileDir));
exit :
return _hr; }
// ---------------------------------------------------------------------------
// GetManifestFilePath
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::GetManifestFilePath(LPOLESTR *ppwzManifestFilePath, LPDWORD pccManifestFilePath) { CString sPathOut;
IF_FAILED_EXIT(sPathOut.Assign(_sManifestFilePath)); *pccManifestFilePath = sPathOut.CharCount(); IF_FAILED_EXIT(sPathOut.ReleaseOwnership(ppwzManifestFilePath));
exit:
if(FAILED(_hr)) { *ppwzManifestFilePath = NULL; *pccManifestFilePath = 0; }
return _hr; }
// ---------------------------------------------------------------------------
// GetManifestFileDir
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::GetManifestFileDir(LPOLESTR *ppwzManifestFileDir, LPDWORD pccManifestFileDir) { CString sDirOut;
IF_FAILED_EXIT(sDirOut.Assign(_sManifestFileDir)); *pccManifestFileDir = sDirOut.CharCount(); IF_FAILED_EXIT(sDirOut.ReleaseOwnership(ppwzManifestFileDir));
exit: if(FAILED(_hr)) { *ppwzManifestFileDir = NULL; *pccManifestFileDir = 0; }
return _hr; }
// ---------------------------------------------------------------------------
// GetManifestImport
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::GetManifestImport(LPASSEMBLY_MANIFEST_IMPORT *ppManifestImport) { IF_NULL_EXIT(_pManifestImport, E_INVALIDARG);
*ppManifestImport = _pManifestImport; (*ppManifestImport)->AddRef();
_hr = S_OK; exit:
return _hr; }
// ---------------------------------------------------------------------------
// GetAssemblyIdentity
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::GetAssemblyIdentity(LPASSEMBLY_IDENTITY *ppAssemblyId) { if (_pAssemblyId) { *ppAssemblyId = _pAssemblyId; (*ppAssemblyId)->AddRef(); _hr = S_OK; } else { IF_NULL_EXIT(_pManifestImport, E_INVALIDARG);
IF_FAILED_EXIT(_pManifestImport->GetAssemblyIdentity(&_pAssemblyId)); *ppAssemblyId = _pAssemblyId; (*ppAssemblyId)->AddRef(); _hr = S_OK; }
exit: return _hr; }
// ---------------------------------------------------------------------------
// GetDisplayName
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::GetDisplayName(LPOLESTR *ppwzDisplayName, LPDWORD pccDiaplyName) { CString sDisplayNameOut;
IF_FAILED_EXIT(sDisplayNameOut.Assign(_sDisplayName)); *pccDiaplyName= sDisplayNameOut.CharCount(); IF_FAILED_EXIT(sDisplayNameOut.ReleaseOwnership(ppwzDisplayName));
exit:
if(FAILED(_hr)) { *pccDiaplyName= 0; *ppwzDisplayName = NULL; }
return _hr; }
// ---------------------------------------------------------------------------
// FindExistMatching
// return:
// S_OK
// S_FALSE -not exist or not match
// E_*
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::FindExistMatching(IManifestInfo *pAssemblyFileInfo, LPOLESTR *ppwzPath) { LPWSTR pwzBuf = NULL; DWORD cbBuf = 0, dwFlag; CString sFileName; CString sTargetPath; IManifestInfo *pFoundFileInfo = NULL; BOOL bExists=FALSE;
IF_NULL_EXIT(pAssemblyFileInfo, E_INVALIDARG); IF_NULL_EXIT(ppwzPath, E_INVALIDARG); *ppwzPath = NULL;
if (_pManifestImport == NULL) { if (_sManifestFilePath._cc == 0) { // no manifest path
_hr = CO_E_NOTINITIALIZED; goto exit; }
// lazy init
IF_FAILED_EXIT(CreateAssemblyManifestImport(&_pManifestImport, _sManifestFilePath._pwz, NULL, 0)); }
// file name parsed from manifest.
IF_FAILED_EXIT(pAssemblyFileInfo->Get(MAN_INFO_ASM_FILE_NAME, (LPVOID *)&pwzBuf, &cbBuf, &dwFlag));
IF_FAILED_EXIT(sFileName.TakeOwnership(pwzBuf));
IF_FAILED_EXIT(sTargetPath.Assign(_sManifestFileDir));
IF_FAILED_EXIT(sTargetPath.Append(sFileName._pwz));
// optimization: check if the target exists
IF_FAILED_EXIT(CheckFileExistence(sTargetPath._pwz, &bExists));
if (!bExists) { // file doesn't exist - no point looking into the manifest file
_hr = S_FALSE; goto exit; }
// find the specified file entry in the manifest
// BUGBUG: check for missing attribute case
if (FAILED(_hr = _pManifestImport->QueryFile(sFileName._pwz, &pFoundFileInfo)) || _hr == S_FALSE) goto exit;
// check if the entries match
if (pAssemblyFileInfo->IsEqual(pFoundFileInfo) == S_OK) { // BUGBUG:? should now check if the actual file has the matching hash etc.
*ppwzPath = sTargetPath._pwz; IF_FAILED_EXIT(sTargetPath.ReleaseOwnership(ppwzPath)); } else _hr = S_FALSE;
exit: SAFERELEASE(pFoundFileInfo); return _hr; }
// ---------------------------------------------------------------------------
// CopyFile
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::CopyFile(LPOLESTR pwzSourcePath, LPOLESTR pwzRelativeFileName, DWORD dwFlags) { LPWSTR pwzBuf = NULL; DWORD ccBuf = 0, cbBuf =0, dwFlag = 0, n = 0; WCHAR wzRandom[8+1] = {0}; CString sDisplayName;
LPASSEMBLY_MANIFEST_IMPORT pManifestImport = NULL; LPASSEMBLY_IDENTITY pIdentity = NULL; IManifestInfo *pAssemblyFile= NULL; if (dwFlags & MANIFEST) { // Get display name.
IF_FAILED_EXIT(CreateAssemblyManifestImport(&pManifestImport, pwzSourcePath, NULL, 0)); IF_FAILED_EXIT(pManifestImport->GetAssemblyIdentity(&pIdentity)); IF_FAILED_EXIT(pIdentity->GetDisplayName(ASMID_DISPLAYNAME_NOMANGLING, &pwzBuf, &ccBuf)); IF_FAILED_EXIT(sDisplayName.TakeOwnership(pwzBuf, ccBuf)); IF_FAILED_EXIT(_sDisplayName.Assign(sDisplayName)); SAFERELEASE(pManifestImport);
// Create manifest file path.
IF_FAILED_EXIT(_sManifestFilePath.Assign(_sRootDir));
// Component manifests cached
// relative to application dir.
if (!(dwFlags & COMPONENT)) { IF_FAILED_EXIT(CreateRandomDir(_sManifestFilePath._pwz, wzRandom, 8)); IF_FAILED_EXIT(_sManifestFilePath.Append(wzRandom)); IF_FAILED_EXIT(_sManifestFilePath.Append(L"\\")); } IF_FAILED_EXIT(_sManifestFilePath.Append(pwzRelativeFileName)); _sManifestFilePath.PathNormalize();
// Manifest file dir.
IF_FAILED_EXIT(_sManifestFileDir.Assign(_sManifestFilePath)); IF_FAILED_EXIT(_sManifestFileDir.RemoveLastElement()); IF_FAILED_EXIT(_sManifestFileDir.Append(L"\\"));
// Construct target paths
IF_FAILED_EXIT(CreateDirectoryHierarchy(NULL, _sManifestFilePath._pwz));
// Copy the manifest from staging area into cache.
IF_WIN32_FALSE_EXIT(::CopyFile(pwzSourcePath, _sManifestFilePath._pwz, FALSE));
// Create the manifest import interface on cached manifest.
IF_FAILED_EXIT(CreateAssemblyManifestImport(&_pManifestImport, _sManifestFilePath._pwz, NULL, 0));
// Enumerate files from manifest and pre-generate nested
// directories required for background file copy.
while (_pManifestImport->GetNextFile(n++, &pAssemblyFile) == S_OK) { CString sPath; IF_FAILED_EXIT(pAssemblyFile->Get(MAN_INFO_ASM_FILE_NAME, (LPVOID *)&pwzBuf, &cbBuf, &dwFlag)); IF_FAILED_EXIT(sPath.TakeOwnership(pwzBuf)); sPath.PathNormalize(); IF_FAILED_EXIT(CreateDirectoryHierarchy(_sManifestFileDir._pwz, sPath._pwz));
// RELEASE pAssebmlyFile everytime through the while loop
SAFERELEASE(pAssemblyFile); } } else { CString sTargetPath;
// Construct target path
IF_FAILED_EXIT(sTargetPath.Assign(_sManifestFileDir)); IF_FAILED_EXIT(sTargetPath.Append(pwzRelativeFileName));
IF_FAILED_EXIT(CreateDirectoryHierarchy(NULL, sTargetPath._pwz));
// Copy non-manifest files into cache. Presumably from previous cached location to the new
IF_WIN32_FALSE_EXIT(::CopyFile(pwzSourcePath, sTargetPath._pwz, FALSE));
}
exit:
SAFERELEASE(pIdentity); SAFERELEASE(pAssemblyFile); SAFERELEASE(pManifestImport);
return _hr;
}
// ---------------------------------------------------------------------------
// Commit
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::Commit(DWORD dwFlags) { CString sTargetDir;
IF_NULL_EXIT( _sDisplayName._pwz, E_INVALIDARG); // No-op for shared assemblies; no directory move.
if (_dwFlags & ASSEMBLY_CACHE_TYPE_SHARED) { _hr = S_OK; goto exit; }
// Need to rename directory
IF_FAILED_EXIT(GetCacheRootDir(sTargetDir, Base)); IF_FAILED_EXIT(sTargetDir.Append(_sDisplayName));
// Move the file from staging dir. The application is now complete.
if(!MoveFileEx(_sManifestFileDir._pwz, sTargetDir._pwz, MOVEFILE_COPY_ALLOWED)) { _hr = FusionpHresultFromLastError();
// BUGBUG : move this to destructor.
RemoveDirectoryAndChildren(_sManifestFileDir._pwz);
if(_hr == HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS)) { _hr = S_FALSE; goto exit; }
IF_FAILED_EXIT(_hr); }
//BUGBUG - if any files are held open, eg due to leaked/unreleased interfaces
// then movefile will fail. Solution is to ensure that IAssemblyManifestImport does
// not hold file, and to attempt copy if failure occurs. In case a collision occurs,
// delete redundant app copy in staging dir.
exit:
return _hr; }
#define APP_STATUS_KEY TEXT("1.0.0.0\\Cache\\")
#define WZ_STATUS_CONFIRMED L"Confirmed"
#define WZ_STATUS_VISIBLE L"Visible"
#define WZ_STATUS_CRITICAL L"Critical"
HRESULT CAssemblyCache::GetStatusStrings( CacheStatus eStatus, LPWSTR *ppValueString, LPCWSTR pwzDisplayName, CString& sRelStatusKey) { HRESULT hr = S_OK; MAKE_ERROR_MACROS_STATIC(hr);
switch(eStatus) { case VISIBLE: *ppValueString = WZ_STATUS_VISIBLE; break; case CONFIRMED: *ppValueString = WZ_STATUS_CONFIRMED; break; case CRITICAL: *ppValueString = WZ_STATUS_CRITICAL; break; default: hr = E_INVALIDARG; goto exit; }
IF_FAILED_EXIT(sRelStatusKey.Assign(APP_STATUS_KEY));
IF_FAILED_EXIT(sRelStatusKey.Append(pwzDisplayName));
exit: return hr; }
// ---------------------------------------------------------------------------
// IsStatus
// return FALSE if value FALSE or absent, TRUE if value TRUE
// ---------------------------------------------------------------------------
BOOL CAssemblyCache::IsStatus(LPWSTR pwzDisplayName, CacheStatus eStatus) { HRESULT hr = S_OK; MAKE_ERROR_MACROS_STATIC(hr); CString sStatus; DWORD dwValue = -1; LPWSTR pwzQueryString = NULL; // Default values spelled out.
BOOL bStatus = FALSE; CRegImport *pRegImport = NULL;
if((eStatus == VISIBLE) || (eStatus == CONFIRMED) ) { bStatus = TRUE; }
IF_FAILED_EXIT(hr = GetStatusStrings( eStatus, &pwzQueryString, pwzDisplayName, sStatus)); IF_FAILED_EXIT(hr = CRegImport::Create(&pRegImport, sStatus._pwz));
if(hr == S_FALSE) goto exit;
IF_FAILED_EXIT(pRegImport->ReadDword(pwzQueryString, &dwValue));
// Found a value in registry. Return value.
bStatus = (BOOL) dwValue;
hr = S_OK;
exit:
SAFEDELETE(pRegImport);
return bStatus; }
// ---------------------------------------------------------------------------
// SetStatus
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::SetStatus(LPWSTR pwzDisplayName, CacheStatus eStatus, BOOL fStatus) { HRESULT hr = S_OK; MAKE_ERROR_MACROS_STATIC(hr);
CString sStatus; DWORD dwValue = (DWORD) (fStatus); LPWSTR pwzValueNameString = NULL; CRegEmit *pRegEmit = NULL; // BUGBUG: should this be in-sync with what server does to register update?
IF_FAILED_EXIT(GetStatusStrings( eStatus, &pwzValueNameString, pwzDisplayName, sStatus));
IF_FAILED_EXIT(CRegEmit::Create(&pRegEmit, sStatus._pwz));
// Write
IF_FAILED_EXIT(pRegEmit->WriteDword(pwzValueNameString, dwValue));
hr = S_OK; exit:
SAFEDELETE(pRegEmit);
return hr;
}
// ---------------------------------------------------------------------------
// GetCacheRootDir
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::GetCacheRootDir(CString &sCacheDir, CacheFlags eFlags) { HRESULT hr = S_OK; MAKE_ERROR_MACROS_STATIC(hr); CString sPath; LPWSTR pwzPath = NULL; DWORD ccSize=0;
IF_FALSE_EXIT((ccSize = GetEnvironmentVariable(L"UserProfile", NULL, 0)) != 0, E_FAIL);
IF_ALLOC_FAILED_EXIT(pwzPath = new WCHAR[ccSize+1]);
IF_FALSE_EXIT(GetEnvironmentVariable(L"UserProfile", pwzPath, ccSize) != 0, E_FAIL);
IF_FAILED_EXIT(sCacheDir.Assign(pwzPath)); // BUGBUG: don't use PathCombine
IF_FAILED_EXIT((DoPathCombine(sCacheDir, WZ_CACHE_LOCALROOTDIR)));
switch(eFlags) { case Base: break; case Manifests: // BUGBUG: don't use PathCombine
IF_FAILED_EXIT(DoPathCombine(sCacheDir, WZ_MANIFEST_STAGING_DIR)); break; case Temp: // BUGBUG: don't use PathCombine
IF_FAILED_EXIT(DoPathCombine(sCacheDir, WZ_TEMP_DIR)); break; case Shared: // BUGBUG: don't use PathCombine
IF_FAILED_EXIT(DoPathCombine(sCacheDir, WZ_SHARED_DIR)); break; default: break; }
exit:
SAFEDELETEARRAY(pwzPath); return hr; }
// ---------------------------------------------------------------------------
// IsCached
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::IsCached(IAssemblyIdentity *pAppId) { HRESULT hr = S_FALSE; MAKE_ERROR_MACROS_STATIC(hr);
LPWSTR pwz = NULL; DWORD cc = 0, dwAttrib = 0; CString sDisplayName; CString sCacheDir; BOOL bExists=FALSE;
// Get the assembly display name.
IF_FAILED_EXIT(pAppId->GetDisplayName(0, &pwz, &cc)); IF_FAILED_EXIT(sDisplayName.TakeOwnership(pwz));
// Check if top-level dir is present.
IF_FAILED_EXIT(GetCacheRootDir(sCacheDir, Base)); IF_FAILED_EXIT(sCacheDir.Append(sDisplayName));
IF_FAILED_EXIT(CheckFileExistence(sCacheDir._pwz, &bExists));
(bExists) ? (hr = S_OK) : (hr = S_FALSE);
exit : return hr; }
// ---------------------------------------------------------------------------
// IsKnownAssembly
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::IsKnownAssembly(IAssemblyIdentity *pId, DWORD dwFlags) { return ::IsKnownAssembly(pId, dwFlags); }
// ---------------------------------------------------------------------------
// IsaMissingSystemAssembly
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::IsaMissingSystemAssembly(IAssemblyIdentity *pId, DWORD dwFlags) { HRESULT hr = S_FALSE; CString sCurrentAssemblyPath;
// check if this is a system assembly.
if ((hr = CAssemblyCache::IsKnownAssembly(pId, KNOWN_SYSTEM_ASSEMBLY)) != S_OK) goto exit; // see if it exists in GAC
if ((hr = CAssemblyCache::GlobalCacheLookup(pId, sCurrentAssemblyPath)) == S_OK) goto exit;
if(hr == S_FALSE) hr = S_OK; // this is a system assembly which has not yet been installed to GAC.
exit:
return hr; }
// ---------------------------------------------------------------------------
// CreateFusionAssemblyCache
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::CreateFusionAssemblyCache(IAssemblyCache **ppFusionAsmCache) { HRESULT hr = S_OK; MAKE_ERROR_MACROS_STATIC(hr); HMODULE hEEShim = NULL; HMODULE hFusion = NULL; DWORD ccPath = MAX_PATH; CString sFusionPath; LPWSTR pwzPath=NULL;
if (g_pFusionAssemblyCache) { *ppFusionAsmCache = g_pFusionAssemblyCache; (*ppFusionAsmCache)->AddRef(); goto exit; }
PFNGETCORSYSTEMDIRECTORY pfnGetCorSystemDirectory = NULL; PFNCREATEASSEMBLYCACHE pfnCreateAssemblyCache = NULL;
// Find out where the current version of URT is installed
hEEShim = LoadLibrary(WZ_MSCOREE_DLL_NAME); if(!hEEShim) { hr = FusionpHresultFromLastError(); goto exit; }
pfnGetCorSystemDirectory = (PFNGETCORSYSTEMDIRECTORY) GetProcAddress(hEEShim, GETCORSYSTEMDIRECTORY_FN_NAME);
if((!pfnGetCorSystemDirectory)) { hr = FusionpHresultFromLastError(); goto exit; }
// Get cor path.
hr = pfnGetCorSystemDirectory(NULL, 0, &ccPath);
IF_FALSE_EXIT(hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER), FAILED(hr) ? hr : E_FAIL);
IF_ALLOC_FAILED_EXIT(pwzPath = new WCHAR[ccPath+1]);
IF_FAILED_EXIT(pfnGetCorSystemDirectory(pwzPath, ccPath, &ccPath));
IF_FAILED_EXIT(sFusionPath.Assign(pwzPath));
// Form path to fusion
IF_FAILED_EXIT(sFusionPath.Append(WZ_FUSION_DLL_NAME));
// Fusion.dll has a static dependency on msvcr70.dll.
// If msvcr70.dll is not in the path (a rare case), a simple LoadLibrary() fails (ERROR_MOD_NOT_FOUND).
// LoadLibraryEx() with LOAD_WITH_ALTERED_SEARCH_PATH fixes this.
hFusion = LoadLibraryEx(sFusionPath._pwz, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); if(!hFusion) { hr = FusionpHresultFromLastError(); goto exit; }
// Get method ptr.
pfnCreateAssemblyCache = (PFNCREATEASSEMBLYCACHE) GetProcAddress(hFusion, CREATEASSEMBLYCACHE_FN_NAME);
if((!pfnCreateAssemblyCache)) { hr = FusionpHresultFromLastError(); goto exit; }
// Create the fusion cache interface.
IF_FAILED_EXIT(pfnCreateAssemblyCache(ppFusionAsmCache, 0));
//BUGBUG - we never unload fusion, which is ok for now
// but should when switchover to cache api objects.
g_pFusionAssemblyCache = *ppFusionAsmCache; g_pFusionAssemblyCache->AddRef(); hr = S_OK; exit:
SAFEDELETEARRAY(pwzPath); return hr; }
// ---------------------------------------------------------------------------
// GlobalCacheLookup
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::GlobalCacheLookup(IAssemblyIdentity *pId, CString& sCurrentAssemblyPath) { HRESULT hr = S_OK; MAKE_ERROR_MACROS_STATIC(hr); LPWSTR pwz = NULL; DWORD cc = 0;
CString sCLRDisplayName;
IAssemblyCache *pFusionCache = NULL; ASSEMBLY_INFO asminfo = {0}; WCHAR pwzPath[MAX_PATH]; CString sPath;
IF_FAILED_EXIT(sPath.ResizeBuffer(MAX_PATH+1));
// Get the URT display name for lookup.
IF_FAILED_EXIT(pId->GetCLRDisplayName(0, &pwz, &cc)); IF_FAILED_EXIT(sCLRDisplayName.TakeOwnership(pwz));
// Set size on asminfo struct.
asminfo.cbAssemblyInfo = sizeof(ASSEMBLY_INFO);
asminfo.pszCurrentAssemblyPathBuf = sPath._pwz; asminfo.cchBuf = MAX_PATH;
// Create the fusion cache object for lookup.
IF_FAILED_EXIT(CreateFusionAssemblyCache(&pFusionCache));
// Get cache info for assembly. Needs to free [out] pathbuf
if(FAILED(hr = pFusionCache->QueryAssemblyInfo(0, sCLRDisplayName._pwz, &asminfo))) { hr = S_FALSE; // all failures are being interpreted as ERROR_FILE_NOT_FOUND
goto exit; }
// Return ok if install flag present.
if (asminfo.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED) { IF_FAILED_EXIT(sCurrentAssemblyPath.Assign(asminfo.pszCurrentAssemblyPathBuf)); hr = S_OK; } else hr = S_FALSE; exit:
SAFERELEASE(pFusionCache);
return hr; }
// ---------------------------------------------------------------------------
// GlobalCacheInstall
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::GlobalCacheInstall(IAssemblyCacheImport *pCacheImport, CString& sCurrentAssemblyPath, CString& sInstallRefString) { HRESULT hr = S_OK; MAKE_ERROR_MACROS_STATIC(hr); LPWSTR pwz = NULL; DWORD cc = 0; // Fusion.dll's assembly cache, not to be confusing.
IAssemblyCache *pFusionCache = NULL;
// note: InstallAssembly takes in LPCFUSION_INSTALL_REFERENCE
// so fix this to have one fiRef instead (of one per loop)
// - make static also ? - adriaanc
FUSION_INSTALL_REFERENCE fiRef = {0};
// Create Fusion cache object for install.
IF_FAILED_EXIT(CreateFusionAssemblyCache(&pFusionCache));
// Setup the necessary reference struct.
fiRef.cbSize = sizeof(FUSION_INSTALL_REFERENCE); fiRef.dwFlags = 0; fiRef.guidScheme = FUSION_REFCOUNT_OPAQUE_STRING_GUID; fiRef.szIdentifier = sInstallRefString._pwz; fiRef.szNonCannonicalData = NULL;
if (pCacheImport != NULL) { CString sManifestFilePath;
// 1. Install the downloaded assembly
// Get the source manifest path.
IF_FAILED_EXIT(pCacheImport->GetManifestFilePath(&pwz, &cc)); IF_FAILED_EXIT(sManifestFilePath.TakeOwnership(pwz));
// Do the install.
// ISSUE - always refresh - check fusion doc on refresh
IF_FAILED_EXIT(pFusionCache->InstallAssembly(IASSEMBLYCACHE_INSTALL_FLAG_REFRESH, sManifestFilePath._pwz, &fiRef));
} else if ((sCurrentAssemblyPath)._cc != 0) { // bugbug - as the list is set up during pre-download, the assemblies to be add-ref-ed
// could have been removed by this time. Need to recover from this.
// ignore error from Fusion and continue for now
// 2. Up the ref count of the existing assembly by doing install.
IF_FAILED_EXIT(pFusionCache->InstallAssembly(0, sCurrentAssemblyPath._pwz, &fiRef)); }
exit :
SAFERELEASE(pFusionCache);
return hr; }
// ---------------------------------------------------------------------------
// DeleteAssemblyAndModules
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::DeleteAssemblyAndModules(LPWSTR pszManifestFilePath) { HRESULT hr = S_OK; MAKE_ERROR_MACROS_STATIC(hr); IAssemblyManifestImport *pManImport=NULL; DWORD nIndex=0; DWORD dwFlag; DWORD cbBuf; LPWSTR pwzBuf=NULL; IManifestInfo *pFileInfo = NULL; CString sAssemblyPath;
IF_FAILED_EXIT(CreateAssemblyManifestImport(&pManImport, pszManifestFilePath, NULL, 0));
IF_FAILED_EXIT(sAssemblyPath.Assign(pszManifestFilePath));
while ((hr = pManImport->GetNextFile(nIndex++, &pFileInfo)) == S_OK) { IF_FAILED_EXIT(pFileInfo->Get(MAN_INFO_ASM_FILE_NAME, (LPVOID *)&pwzBuf, &cbBuf, &dwFlag));
IF_FAILED_EXIT(sAssemblyPath.RemoveLastElement()); IF_FAILED_EXIT(sAssemblyPath.Append(L"\\")); IF_FAILED_EXIT(sAssemblyPath.Append(pwzBuf));
IF_WIN32_FALSE_EXIT(::DeleteFile(sAssemblyPath._pwz));
SAFEDELETEARRAY(pwzBuf); SAFERELEASE(pFileInfo); }
if(hr == HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS)) hr = S_OK;
IF_FAILED_EXIT(hr);
SAFERELEASE(pManImport); // release manImport before deleting manifestFile
IF_WIN32_FALSE_EXIT(::DeleteFile(pszManifestFilePath));
IF_FAILED_EXIT(sAssemblyPath.RemoveLastElement());
if(!::RemoveDirectory(sAssemblyPath._pwz)) { hr = FusionpHresultFromLastError(); if(hr == HRESULT_FROM_WIN32(ERROR_DIR_NOT_EMPTY)) hr = S_OK; // looks like there are more files in this dir.
goto exit; }
hr = S_OK;
exit:
SAFERELEASE(pManImport); SAFEDELETEARRAY(pwzBuf); SAFERELEASE(pFileInfo);
return hr; }
// IUnknown methods
// ---------------------------------------------------------------------------
// CAssemblyCache::QI
// ---------------------------------------------------------------------------
STDMETHODIMP CAssemblyCache::QueryInterface(REFIID riid, void** ppvObj) { if ( IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IAssemblyCacheImport) ) { *ppvObj = static_cast<IAssemblyCacheImport*> (this); AddRef(); return S_OK; } else if (IsEqualIID(riid, IID_IAssemblyCacheEmit)) { *ppvObj = static_cast<IAssemblyCacheEmit*> (this); AddRef(); return S_OK; } else { *ppvObj = NULL; return E_NOINTERFACE; } }
// ---------------------------------------------------------------------------
// CAssemblyCache::AddRef
// ---------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CAssemblyCache::AddRef() { return InterlockedIncrement ((LONG*) &_cRef); }
// ---------------------------------------------------------------------------
// CAssemblyCache::Release
// ---------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CAssemblyCache::Release() { ULONG lRet = InterlockedDecrement ((LONG*) &_cRef); if (!lRet) delete this; return lRet; }
|