|
|
#include <fusenetincludes.h>
#include <bits.h>
#include <assemblycache.h>
#include "dialog.h"
#include <assemblydownload.h>
#include <msxml2.h>
#include <manifestimport.h>
#include <patchingutil.h>
#include <sxsapi.h>
#include ".\patchapi.h"
// Update services
#include "server.h"
#include "fusion.h"
#include <shellapi.h>
#include "regdb.h"
#include "macros.h"
IBackgroundCopyManager* g_pBITSManager = NULL;
// ---------------------------------------------------------------------------
// CreateAssemblyDownload
// ---------------------------------------------------------------------------
STDAPI CreateAssemblyDownload(IAssemblyDownload** ppDownload, CDebugLog *pDbgLog, DWORD dwFlags) { HRESULT hr = S_OK; MAKE_ERROR_MACROS_STATIC(hr);
CAssemblyDownload *pDownload = NULL; IF_ALLOC_FAILED_EXIT( pDownload = new(CAssemblyDownload )); IF_FAILED_EXIT(pDownload->_hr);
IF_FAILED_EXIT(pDownload->Init((CDebugLog *) pDbgLog)); *ppDownload = (IAssemblyDownload*) pDownload; pDownload = NULL;
#ifdef DEVMODE
if (dwFlags == DOWNLOAD_DEVMODE) ((CAssemblyDownload *)*ppDownload)->_bIsDevMode = TRUE; #endif
exit:
SAFERELEASE(pDownload); return hr; }
HRESULT CAssemblyDownload::Init( CDebugLog * pDbgLog) { _pDbgLog = pDbgLog;
if(pDbgLog) { pDbgLog->AddRef(); } else { _bLocalLog = TRUE; IF_FAILED_EXIT(CreateLogObject(&_pDbgLog, NULL)); }
exit : return _hr; }
// ---------------------------------------------------------------------------
// ctor
// ---------------------------------------------------------------------------
CAssemblyDownload::CAssemblyDownload() : _dwSig('DLND'), _cRef(1), _hr(S_OK), _hrError(S_OK), _pRootEmit(NULL), _pBindSink(NULL), _pJob(NULL), _pDlg(NULL), _pPatchingInfo(NULL), _bAbort(FALSE), #ifdef DEVMODE
_bIsDevMode(FALSE), #endif
_bAbortFromBindSink(FALSE), _bErrorHandled(FALSE), _pDbgLog(NULL), _bLocalLog(FALSE) { __try { InitializeCriticalSection(&_cs); } __except (GetExceptionCode() == STATUS_NO_MEMORY ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { _hr = E_OUTOFMEMORY; }
return; }
// ---------------------------------------------------------------------------
// dtor
// ---------------------------------------------------------------------------
CAssemblyDownload::~CAssemblyDownload() { if(_pDbgLog && _bLocalLog) { DUMPDEBUGLOG(_pDbgLog, -1, _hr); }
SAFERELEASE(_pPatchingInfo); SAFERELEASE(_pRootEmit); SAFEDELETE(_pDlg); SAFERELEASE(_pJob); SAFERELEASE(_pDbgLog); DeleteCriticalSection(&_cs);
return; }
// IAssemblyDownload methods
// ---------------------------------------------------------------------------
// DownloadManifestAndDependencies
// ---------------------------------------------------------------------------
HRESULT CAssemblyDownload::DownloadManifestAndDependencies( LPWSTR pwzManifestUrl, IAssemblyBindSink *pBindSink, DWORD dwFlags) { LPWSTR pwz = NULL; CString sRemoteUrl; CString sLocalName;
IBackgroundCopyJob *pJob = NULL; IF_FAILED_EXIT(_pDbgLog->SetDownloadType(dwFlags)); // Create temporary manifest path from url.
IF_FAILED_EXIT(sRemoteUrl.Assign(pwzManifestUrl)); IF_FAILED_EXIT(MakeTempManifestLocation(sRemoteUrl, sLocalName));
// Init dialog object with job
if (dwFlags & DOWNLOAD_FLAGS_PROGRESS_UI) IF_FAILED_EXIT(CreateDialogObject(&_pDlg));
// set named event if specified.
if (dwFlags & DOWNLOAD_FLAGS_NOTIFY_BINDSINK) _pBindSink = pBindSink;
// Create new job. Display name is url.
IF_FAILED_EXIT(CreateNewBITSJob(&pJob, sRemoteUrl));
// add this job to reg.
IF_FAILED_EXIT(AddJobToRegistry(sRemoteUrl._pwz, sLocalName._pwz, pJob, 0)); // Add single app or subscription manifest to job.
IF_FAILED_EXIT(pJob->AddFile(sRemoteUrl._pwz, sLocalName._pwz));
// Submit the job.
IF_FAILED_EXIT(pJob->Resume());
// Release the job; BITS keeps own refcount.
SAFERELEASE(pJob);
// Pump messages if progress ui specified.
if (dwFlags & DOWNLOAD_FLAGS_PROGRESS_UI) { MSG msg; BOOL bRet; DWORD dwError; while((bRet = GetMessage( &msg, _pDlg->_hwndDlg, 0, 0 ))) { DWORD dwLow = LOWORD(msg.message); if (dwLow == WM_CANCEL_DOWNLOAD) { // Signal abort; hide progress UI.
CancelDownload(); } else if (dwLow == WM_FINISH_DOWNLOAD) { // Terminates progress UI.
FinishDownload(); break; }
if (!IsDialogMessage(_pDlg->_hwndDlg, &msg)) { TranslateMessage( &msg ); DispatchMessage( &msg ); } }
}
exit:
// If aborted by bindsink, return S_OK. // We should return hrError instead of _hr.
return ((_hr == E_ABORT) && _bAbortFromBindSink) ? S_OK : ( FAILED(_hr) ? _hr : _hrError) ; }
// ---------------------------------------------------------------------------
// CancelDownload
//
// Do not attempt to obtain the object critical section _cs in this method - in abort cases it
// can be called by a non-callback client thread under the same critical section protecting a
// global list of downloads which the bindsink must itself acquire, resulting in classic deadlock.
// ---------------------------------------------------------------------------
HRESULT CAssemblyDownload::CancelDownload() { // Signal abort; async the cancel. The download will be cancelled
// by the callback thread when it checks the _bAbort flag.
SignalAbort();
if (_pDlg) ShowWindow(_pDlg->_hwndDlg, SW_HIDE);
_hr = S_OK;
DEBUGOUT(_pDbgLog, 0, L"LOG: User Canceled. Aborting Download ...... ");
return _hr; }
// ---------------------------------------------------------------------------
// DoCacheUpdate
// ---------------------------------------------------------------------------
HRESULT CAssemblyDownload::DoCacheUpdate(IBackgroundCopyJob *pJob) { DWORD nCount = 0; BOOL bIsManifestFile = FALSE; IEnumBackgroundCopyFiles *pEnumFiles = NULL; IBackgroundCopyFile *pFile = NULL; IBackgroundCopyJob *pChildJob = NULL; // Commit files to disk
IF_FAILED_EXIT(pJob->Complete());
// Remove in-progress status for job.
IF_FAILED_EXIT(RemoveJobFromRegistry(_pJob, NULL, SHREGDEL_HKCU, 0)); // Decrement _pJob's refcount because
// BITS mysteriously won't release the
// CBitsCallback if _pJob has an additional refcount
SetJobObject(NULL);
// Get the file enumerator.
IF_FAILED_EXIT(pJob->EnumFiles(&pEnumFiles)); IF_FAILED_EXIT(pEnumFiles->GetCount(&nCount));
// Enumerate the files in the job.
for (DWORD i = 0; i < nCount; i++) { IF_FAILED_EXIT(pEnumFiles->Next(1, &pFile, NULL));
// Process manifest file or normal/patch file.
IF_FAILED_EXIT(IsManifestFile(pFile, &bIsManifestFile)); if (bIsManifestFile) IF_FAILED_EXIT(HandleManifest(pFile, &pChildJob)); else IF_FAILED_EXIT(HandleFile(pFile));
SAFERELEASE(pFile); } // If a additional dependencies were found.
if (pChildJob) { // Also update dialog with new job
if (_pDlg) _pDlg->SetJobObject(pChildJob);
// Submit new job.
IF_FAILED_EXIT(pChildJob->Resume()); goto exit; }
// ** Commit/Signal/Return***
// Done. Do all necessary cleanup
// before committing application to cache.
// If patching was used during the download ensure
// the patching temp directory is deleted.
if (_pPatchingInfo) IF_FAILED_EXIT(CleanUpPatchDir()); // If any assemblies were marked for
// global cach install, install them now.
if (_ListGlobalCacheInstall.GetCount()) IF_FAILED_EXIT(InstallGlobalAssemblies());
// Commit application.
if (_pRootEmit) IF_FAILED_EXIT(_pRootEmit->Commit(0));
// registration hack if avalon app.
IF_FAILED_EXIT(DoEvilAvalonRegistrationHack());
// If progress ui terminate it.
if (_pDlg) _pDlg->SetDlgState(DOWNLOADDLG_STATE_ALL_DONE);
// If callback signal.
if (_pBindSink) { IF_FAILED_EXIT(_pBindSink->OnProgress(ASM_NOTIFICATION_DONE, S_OK, NULL, 0, 0, NULL));
// Ensure this is last notification bindsink receives resulting from
// subsequent JobModified notifications.
// DO NOT free the bindsink here.
_pBindSink = NULL; }
exit:
SAFERELEASE(pEnumFiles); SAFERELEASE(pChildJob); SAFERELEASE(pFile); return _hr; }
// ---------------------------------------------------------------------------
// HandleManifest
// ---------------------------------------------------------------------------
HRESULT CAssemblyDownload::HandleManifest(IBackgroundCopyFile *pFile, IBackgroundCopyJob **ppJob) { LPWSTR pwz = NULL; DWORD dwManifestType = MANIFEST_TYPE_UNKNOWN; CString sLocalName(CString::COM_Allocator); CString sRemoteName(CString::COM_Allocator);
IAssemblyManifestImport *pManifestImport = NULL; // Get local manifest file name.
IF_FAILED_EXIT(pFile->GetLocalName(&pwz)); IF_FAILED_EXIT(sLocalName.TakeOwnership(pwz));
// Get remote manifest url
IF_FAILED_EXIT(pFile->GetRemoteName(&pwz)); IF_FAILED_EXIT(sRemoteName.TakeOwnership(pwz));
// Instance a manifest import interface.
IF_FAILED_EXIT(CreateAssemblyManifestImport(&pManifestImport, sLocalName._pwz, _pDbgLog, 0));
// Get the manifest type.
IF_FAILED_EXIT(pManifestImport->ReportManifestType(&dwManifestType));
// Handle either subscription or application manifest
if (dwManifestType == MANIFEST_TYPE_SUBSCRIPTION) { DEBUGOUT1(_pDbgLog, 1, L" LOG: Got subscription manifest from %s ", sRemoteName._pwz);
IF_FAILED_EXIT(HandleSubscriptionManifest(pManifestImport, sLocalName, sRemoteName, ppJob)); } else if (dwManifestType == MANIFEST_TYPE_APPLICATION) { DEBUGOUT1(_pDbgLog, 1, L" LOG: Got App manifest from %s ", sRemoteName._pwz);
IF_FAILED_EXIT(HandleApplicationManifest(pManifestImport, sLocalName, sRemoteName, ppJob)); } else if (dwManifestType == MANIFEST_TYPE_COMPONENT) { DEBUGOUT1(_pDbgLog, 1, L" LOG: Got component manifest from %s ", sRemoteName._pwz);
IF_FAILED_EXIT(HandleComponentManifest(pManifestImport, sLocalName, sRemoteName, ppJob)); } else {
DEBUGOUT1(_pDbgLog, 0, L" ERR: UNknown manifest type in File %s \n", sRemoteName._pwz);
_hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); goto exit; }
// Cleanup manifest temp dir.
SAFERELEASE(pManifestImport);
IF_WIN32_FALSE_EXIT(::DeleteFile(sLocalName._pwz)); IF_FAILED_EXIT(sLocalName.RemoveLastElement()); IF_FAILED_EXIT(RemoveDirectoryAndChildren(sLocalName._pwz));
_hr = S_OK;
exit:
SAFERELEASE(pManifestImport); return _hr; }
// ---------------------------------------------------------------------------
// HandleSubscriptionManifest
// ---------------------------------------------------------------------------
HRESULT CAssemblyDownload::HandleSubscriptionManifest( IAssemblyManifestImport *pManifestImport, CString &sLocalName, CString &sRemoteName, IBackgroundCopyJob **ppJob) {
IManifestInfo *pAppAssemblyInfo = NULL; IAssemblyIdentity *pAppId = NULL;
// If callback signal.
if (_pBindSink) { // BUGBUG: fill in progress?
_hr = _pBindSink->OnProgress(ASM_NOTIFICATION_SUBSCRIPTION_MANIFEST, _hr, sRemoteName._pwz, 0, 0, pManifestImport);
// Bindsink communicates abort via return value.
if (_hr == E_ABORT) _bAbortFromBindSink = TRUE;
// Catches E_ABORT case.
IF_FAILED_EXIT(_hr); }
// If foreground download reset dialog and queue up dependency
if (_pDlg) { _pDlg->InitDialog(_pDlg->_hwndDlg); _pDlg->SetDlgState(DOWNLOADDLG_STATE_GETTING_APP_MANIFEST); IF_FAILED_EXIT(EnqueueDependencies(pManifestImport, sRemoteName, ppJob)); }
// Otherwise background download. Don't submit request if app already
// cached or download is in progress.
else { DWORD cb = 0, dwFlag = 0; // Get the dependent (application) assembly info (0th index)
IF_FAILED_EXIT(pManifestImport->GetNextAssembly(0, &pAppAssemblyInfo));
// Get dependent (application) assembly identity
IF_FAILED_EXIT(pAppAssemblyInfo->Get(MAN_INFO_DEPENDENT_ASM_ID, (LPVOID *)&pAppId, &cb, &dwFlag));
IF_FAILED_EXIT(CAssemblyCache::IsCached(pAppId)); if (_hr == S_FALSE) IF_FAILED_EXIT(EnqueueDependencies(pManifestImport, sRemoteName, ppJob)); }
exit:
SAFERELEASE(pAppId); SAFERELEASE(pAppAssemblyInfo); return _hr; }
// ---------------------------------------------------------------------------
// HandleApplicationManifest
// ---------------------------------------------------------------------------
HRESULT CAssemblyDownload::HandleApplicationManifest( IAssemblyManifestImport *pManifestImport, CString &sLocalName, CString &sRemoteName, IBackgroundCopyJob **ppJob) {
// If callback signal.
if (_pBindSink) { // BUGBUG: fill in progress?
_hr = _pBindSink->OnProgress(ASM_NOTIFICATION_APPLICATION_MANIFEST, _hr, sRemoteName._pwz, 0, 0, pManifestImport);
// Bindsink communicates abort via return value.
if (_hr == E_ABORT) _bAbortFromBindSink = TRUE;
// Catches E_ABORT case.
IF_FAILED_EXIT(_hr); }
// This is the one location where we know the RemoteUrl is the appbase/app.manifest.
// save off the app base.
IF_FAILED_EXIT(_sAppBase.Assign(sRemoteName)); IF_FAILED_EXIT(_sAppBase.RemoveLastElement()); IF_FAILED_EXIT(_sAppBase.Append(L"/"));
// App manifest generically handled by component manifest handler.
IF_FAILED_EXIT(HandleComponentManifest(pManifestImport, sLocalName, sRemoteName, ppJob));
exit: return _hr; }
// ---------------------------------------------------------------------------
// HandleComponentManifest
// ---------------------------------------------------------------------------
HRESULT CAssemblyDownload::HandleComponentManifest( IAssemblyManifestImport *pManifestImport, CString &sLocalName, CString &sRemoteName, IBackgroundCopyJob **ppJob) {
LPWSTR pwz = NULL; DWORD cb = 0, cc= 0; CString sManifestFileName; CString sRelativePath;
IAssemblyIdentity *pIdentity = NULL; IAssemblyCacheEmit *pCacheEmit = NULL; IAssemblyCacheImport *pCacheImport = NULL; IManifestInfo *pAppInfo = NULL; // Reset dialog for now.
if (_pDlg) { _pDlg->InitDialog(_pDlg->_hwndDlg); _pDlg->SetDlgState(DOWNLOADDLG_STATE_GETTING_OTHER_FILES); }
// Generate the cache entry (assemblydir/manifest/<dirs>)
// First callbac, _pRootEmit = NULL;
IF_FAILED_EXIT(CreateAssemblyCacheEmit(&pCacheEmit, _pRootEmit, 0)); // If this is first cache entry created, save as root.
if (!_pRootEmit) { _pRootEmit = pCacheEmit; _pRootEmit->AddRef(); }
// Get the manifest file name from local (staging) path.
IF_FAILED_EXIT(sLocalName.LastElement(sManifestFileName));
// double check its in the app dir.
IF_FAILED_EXIT((sRemoteName.StartsWith(_sAppBase._pwz))); IF_FALSE_EXIT((_hr == S_OK), E_INVALIDARG); // Index into remote url for relative path.
pwz = sRemoteName._pwz + _sAppBase._cc -1; IF_FAILED_EXIT(sRelativePath.Assign(pwz));
// Create the cache entry.
// (x86_foo_1.0.0.0_en-us/foo.manifest/<+extra dirs>)
//BugBug, temporary hack to distinguish between application and component manifests
if (_pRootEmit == pCacheEmit) IF_FAILED_EXIT(pCacheEmit->CopyFile(sLocalName._pwz, sRelativePath._pwz, MANIFEST)); else IF_FAILED_EXIT(pCacheEmit->CopyFile(sLocalName._pwz, sRelativePath._pwz, MANIFEST |COMPONENT));
// displayname is not set until after a copyfile() call
if(_sAppDisplayName._cc == 0) { // _pRootEmit == pCacheEmit
IF_FAILED_EXIT(_pRootEmit->GetDisplayName(&pwz, &cc)); IF_FAILED_EXIT(_sAppDisplayName.TakeOwnership(pwz, cc)); IF_FAILED_EXIT(_pDbgLog->SetAppName(pwz));
// must be an application manifest...
if (_pDlg) { DWORD dwFlag = 0; IF_FAILED_EXIT(pManifestImport->GetManifestApplicationInfo(&pAppInfo)); IF_FALSE_EXIT((_hr == S_OK), E_INVALIDARG); IF_FAILED_EXIT(pAppInfo->Get(MAN_INFO_APPLICATION_FRIENDLYNAME, (LPVOID *)&pwz, &cb, &dwFlag)); if (SUCCEEDED(_hr) && pwz) { // set progress ui title (set once per download)
IF_FAILED_EXIT(_pDlg->SetDlgTitle(pwz)); SAFEDELETEARRAY(pwz); } } }
// QI for the import interface.
IF_FAILED_EXIT(pCacheEmit->QueryInterface(IID_IAssemblyCacheImport, (LPVOID*) &pCacheImport));
// Check if the assembly can be cached globally
// bugbug - same hack to distinguish application/component manifest
if (_pRootEmit != pCacheEmit) { //BUGBUG - verify not an xml manifest for gac install.
IF_FAILED_EXIT(pManifestImport->GetAssemblyIdentity(&pIdentity));
// Known assembly?
BOOL bIsAvalon = FALSE; IF_FAILED_EXIT(IsAvalonAssembly(pIdentity, &bIsAvalon));
if (bIsAvalon) { // notenote: assume no same assembly already in the list for add-ref-ing
// add to the list of assemblies to be installed
CGlobalCacheInstallEntry* pGACInstallEntry = new CGlobalCacheInstallEntry(); IF_ALLOC_FAILED_EXIT(pGACInstallEntry);
pGACInstallEntry->_pICacheImport = pCacheImport; pCacheImport->AddRef(); _ListGlobalCacheInstall.AddHead(pGACInstallEntry); } }
// Line up it's dependencies for download and fire them off.
// We pass the cache import interface for app manifests.
IF_FAILED_EXIT(EnqueueDependencies(pCacheImport, sRemoteName, ppJob)); exit:
SAFERELEASE(pIdentity); SAFERELEASE(pCacheEmit); SAFERELEASE(pCacheImport); SAFERELEASE(pAppInfo);
return _hr; }
// ---------------------------------------------------------------------------
// HandleFile
// ---------------------------------------------------------------------------
HRESULT CAssemblyDownload::HandleFile(IBackgroundCopyFile *pFile) { DWORD cb = 0, dwFlag = 0;
LPWSTR pwz = NULL;
CString sLocalName(CString::COM_Allocator); CString sPatchTempDirectory; // Get local manifest file name.
IF_FAILED_EXIT(pFile->GetLocalName(&pwz)); IF_FAILED_EXIT(sLocalName.TakeOwnership(pwz));
// Begin patch file handling
// if file was a patch file, find the source and target, apply patch to source and move result to target
if (_pPatchingInfo) { // Grab temp file directory
// "C:\Program Files\Application Store\x86_foo_X.X.X.X\PATCH_DIRECTORY\"
IF_FAILED_EXIT(_pPatchingInfo->Get(MAN_INFO_SOURCE_ASM_TEMP_DIR, (LPVOID *)&pwz, &cb, &dwFlag));
IF_FAILED_EXIT(sPatchTempDirectory.TakeOwnership(pwz)); // if local file begins with the manifests patch direcotry, file is a patch file
IF_FAILED_EXIT(sLocalName.StartsWith(sPatchTempDirectory._pwz)); if (_hr== S_OK) { IF_FAILED_EXIT(ApplyPatchFile (sLocalName._pwz)); } } else { // Otherwise no action; assert regular file.
}
exit:
return _hr; }
// ---------------------------------------------------------------------------
// EnqueueDependencies
// ---------------------------------------------------------------------------
HRESULT CAssemblyDownload::EnqueueDependencies(IUnknown* pUnk, CString &sRemoteName, IBackgroundCopyJob **ppJob) {
DWORD cc = 0, dwManifestType = MANIFEST_TYPE_UNKNOWN;
LPWSTR pwz = NULL; CString sDisplayName; IAssemblyIdentity *pIdentity = NULL; IAssemblyManifestImport *pManifestImport = NULL; IAssemblyCacheImport *pCacheImport = NULL; // Get either manifest import or cache import passed in.
_hr = pUnk->QueryInterface(IID_IAssemblyCacheImport, (LPVOID*) &pCacheImport); if ((_hr == S_OK) && pCacheImport) IF_FAILED_EXIT(pCacheImport->GetManifestImport(&pManifestImport)); else IF_FAILED_EXIT(pUnk->QueryInterface(IID_IAssemblyManifestImport, (LPVOID*) &pManifestImport));
// Get the display name for the job.
if (!_sAppDisplayName._cc) { IF_FAILED_EXIT(pManifestImport->GetAssemblyIdentity(&pIdentity)); IF_FAILED_EXIT(pIdentity->GetDisplayName(0, &pwz, &cc)); IF_FAILED_EXIT(_pDbgLog->SetAppName(pwz)); IF_FAILED_EXIT(sDisplayName.TakeOwnership(pwz, cc));
} else IF_FAILED_EXIT(sDisplayName.Assign(_sAppDisplayName)); // Get the manifest type.
IF_FAILED_EXIT(pManifestImport->ReportManifestType(&dwManifestType));
// Handle either subscription or application manifest
if (dwManifestType == MANIFEST_TYPE_SUBSCRIPTION) IF_FAILED_EXIT(EnqueueSubscriptionDependencies(pManifestImport, _sAppBase, sDisplayName, ppJob));
else if (dwManifestType == MANIFEST_TYPE_APPLICATION) IF_FAILED_EXIT(EnqueueApplicationDependencies(pCacheImport, sRemoteName, sDisplayName, ppJob));
else if (dwManifestType == MANIFEST_TYPE_COMPONENT) IF_FAILED_EXIT(EnqueueComponentDependencies(pCacheImport, sRemoteName, sDisplayName, FALSE, ppJob)); else { DEBUGOUT1(_pDbgLog, 0, L" ERR: Unknown manifest type in File %s \n", sRemoteName._pwz);
_hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); } exit:
SAFERELEASE(pCacheImport); SAFERELEASE(pManifestImport); SAFERELEASE(pIdentity); return _hr; }
// ---------------------------------------------------------------------------
// EnqueueSubscriptionDependencies
// ---------------------------------------------------------------------------
HRESULT CAssemblyDownload::EnqueueSubscriptionDependencies( IAssemblyManifestImport *pManifestImport, CString &sCodebase, CString &sDisplayName, IBackgroundCopyJob **ppJob) { DWORD dwFlag = 0, cb = 0, cc = 0; LPWSTR pwz = NULL; CString sAssemblyName; CString sLocalFilePath; CString sRemoteUrl;
IAssemblyIdentity *pIdentity = NULL; IManifestInfo *pDependAsm = NULL; // Get the single dependency
IF_FAILED_EXIT(pManifestImport->GetNextAssembly(0, &pDependAsm)); // Form local cache name (in staging area)....
IF_FAILED_EXIT(pDependAsm->Get(MAN_INFO_DEPENDENT_ASM_ID, (LPVOID *)&pIdentity, &cb, &dwFlag)); // Get the identity name
IF_FAILED_EXIT(pIdentity->GetAttribute(SXS_ASSEMBLY_IDENTITY_STD_ATTRIBUTE_NAME_NAME, &pwz, &cc)); sAssemblyName.TakeOwnership(pwz, cc); // Get codebase from dependency info, if any specified
IF_FAILED_EXIT(pDependAsm->Get(MAN_INFO_DEPENDENT_ASM_CODEBASE, (LPVOID *)&pwz, &cb, &dwFlag)); IF_NULL_EXIT(pwz, E_INVALIDARG);
IF_FAILED_EXIT(sRemoteUrl.TakeOwnership(pwz));
#ifdef DEVMODE
{ DWORD *pdw = NULL;
// is it devMode?
IF_FAILED_EXIT(pDependAsm->Get(MAN_INFO_DEPENDENT_ASM_TYPE, (LPVOID *)&pdw, &cb, &dwFlag)); IF_FALSE_EXIT(pdw != NULL, E_UNEXPECTED);
if (*pdw == DEPENDENT_ASM_INSTALL_TYPE_DEVSYNC) _bIsDevMode = TRUE; SAFEDELETEARRAY(pdw); } #endif
// Form local cache path from download url.
IF_FAILED_EXIT(MakeTempManifestLocation(sRemoteUrl, sLocalFilePath));
// Create new job if necessary.
if (!*ppJob) { IF_FAILED_EXIT(_hr = CreateNewBITSJob(ppJob, sDisplayName));
// add this job to reg.
IF_FAILED_EXIT( _hr = AddJobToRegistry(sRemoteUrl._pwz, sLocalFilePath._pwz, *ppJob, 0)); }
// Submit the job.
IF_FAILED_EXIT((*ppJob)->AddFile(sRemoteUrl._pwz, sLocalFilePath._pwz));
exit: SAFERELEASE(pIdentity); SAFERELEASE(pDependAsm);
return _hr; }
// ---------------------------------------------------------------------------
// EnqueueApplicationDependencies
// ---------------------------------------------------------------------------
HRESULT CAssemblyDownload::EnqueueApplicationDependencies(IAssemblyCacheImport *pCacheImport, CString &sCodebase, CString &sDisplayName, IBackgroundCopyJob **ppJob) { // App dependencies handled generically by component handler.
return EnqueueComponentDependencies(pCacheImport, sCodebase, sDisplayName, TRUE, ppJob); }
// ---------------------------------------------------------------------------
// EnqueueComponentDependencies
// ---------------------------------------------------------------------------
HRESULT CAssemblyDownload::EnqueueComponentDependencies(IAssemblyCacheImport *pCacheImport, CString &sCodebase, CString &sDisplayName, BOOL fRecurse, IBackgroundCopyJob **ppJob) { DWORD n = 0, cb = 0, cc = 0, dwFlag = 0; LPWSTR pwz = NULL;
IAssemblyIdentity *pIdentity = NULL; IAssemblyIdentity *pDepIdentity = NULL;
IAssemblyManifestImport *pManifestImport = NULL;
IAssemblyCacheImport *pMaxCachedImport = NULL; IAssemblyCacheEmit *pCacheEmit = NULL;
IManifestInfo *pAssemblyFile = NULL; IManifestInfo *pDependAsm = NULL;
// Obtain any patching info present in manifest.
// This sets _pPatchingInfo on this object.
if (fRecurse) IF_FAILED_EXIT(LookupPatchInfo(pCacheImport)); // Obtain the ManifestImport interface.
IF_FAILED_EXIT(pCacheImport->GetManifestImport(&pManifestImport));
// Get the asm Id
IF_FAILED_EXIT(pManifestImport->GetAssemblyIdentity(&pIdentity));
// Obtain the cache emit interface
IF_FAILED_EXIT(pCacheImport->QueryInterface(IID_IAssemblyCacheEmit, (LPVOID*) &pCacheEmit));
// Find max completed version, if any
// Init newly created cache import with the highest completed version
// else S_FALSE or E_* and pMaxCachedImport == NULL - no completed version
IF_FAILED_EXIT(CreateAssemblyCacheImport(&pMaxCachedImport, pIdentity, CACHEIMP_CREATE_RETRIEVE_MAX));
///////////////////////////////////////////////////////////////////////////
//
// File enumeration loop
//
///////////////////////////////////////////////////////////////////////////
// Submit files directly into their target dirs.
n = 0;
while(1) { CString sFileName; CString sLocalFilePath; CString sRemoteUrl; BOOL bSkipFile = FALSE;
_hr = pManifestImport->GetNextFile(n++, &pAssemblyFile); // BUGBUG: xml and clr manifest imports return different values at end of enum.
if ((_hr == S_FALSE) || (_hr == HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS))) break; IF_FAILED_EXIT(_hr); // File name parsed from manifest.
IF_FAILED_EXIT(pAssemblyFile->Get(MAN_INFO_ASM_FILE_NAME, (LPVOID*) &pwz, &cb, &dwFlag)); IF_FAILED_EXIT(sFileName.TakeOwnership(pwz));
// DemoHack------------------------------------------------------------------------------
if (_pDlg) { CString sFindingFileMsg; IF_FAILED_EXIT(sFindingFileMsg.Assign(L"Finding Files: ")); IF_FAILED_EXIT(sFindingFileMsg.Append(sFileName)); _pDlg->UpdateDialog(_pDlg->_hwndDlg, sFindingFileMsg._pwz); } // DemoHack------------------------------------------------------------------------------
// Check if file found in max comitted version.
if (pMaxCachedImport) { LPWSTR pwzPath = NULL; IF_FAILED_EXIT(pMaxCachedImport->FindExistMatching(pAssemblyFile, &pwzPath)); if ((_hr == S_OK)) { // Copy from existing cached copy to the new location
// (Non-manifest files)
IF_FAILED_EXIT(pCacheEmit->CopyFile(pwzPath, sFileName._pwz, OTHERFILES));
bSkipFile = TRUE;
SAFEDELETEARRAY(pwzPath); } }
// No previous file found; download.
if (!bSkipFile) { // Form local file path...
// Manifest cache directory
IF_FAILED_EXIT(pCacheImport->GetManifestFileDir(&pwz, &cc)); IF_FAILED_EXIT(sLocalFilePath.TakeOwnership(pwz, cc)); // If patchinginfo was found, check if patch file
// should be submitted. sLocalFilePath will be
// updated in this case.
if (_pPatchingInfo) IF_FAILED_EXIT(ResolveFile(sFileName, sLocalFilePath)); // Form local file path by appending filename.
IF_FAILED_EXIT(sLocalFilePath.Append(sFileName)); IF_FAILED_EXIT(sLocalFilePath.PathNormalize()); // Form remote name
IF_FAILED_EXIT(sRemoteUrl.Assign(sCodebase)); // remote name of manifes
IF_FAILED_EXIT(sRemoteUrl.RemoveLastElement()); // remove manifest file name
IF_FAILED_EXIT(sRemoteUrl.Append(L"/")); // add separator
IF_FAILED_EXIT(sRemoteUrl.Append(sFileName)); // add module file name
// Create new job if necessary.
if (!*ppJob) { IF_FAILED_EXIT(CreateNewBITSJob(ppJob, sDisplayName)); DWORD cc = 0; LPWSTR pwz = NULL;
// Form local file path...
// Manifest cache directory
IF_FAILED_EXIT(pCacheImport->GetManifestFileDir(&pwz, &cc));
// add this job to reg.
IF_FAILED_EXIT(AddJobToRegistry(sCodebase._pwz, pwz, *ppJob, 0)); }
// add the file to the job.
IF_FAILED_EXIT((*ppJob)->AddFile(sRemoteUrl._pwz, sLocalFilePath._pwz)); }
SAFERELEASE(pAssemblyFile); } ///////////////////////////////////////////////////////////////////////////
//
// Dependent assembly enumeration loop
//
///////////////////////////////////////////////////////////////////////////
// Submit assembly manifests into staging area
// Note - we should also get assembly codebase and
// use this instead or adjunctly to display name.
// As is, there is a problem if the ref is partial.
n = 0; while (fRecurse) { CString sAssemblyName; CString sLocalFilePath; CString sRemoteUrl; _hr = pManifestImport->GetNextAssembly(n++, &pDependAsm); // BUGBUG: xml and clr manifest imports return different values at end of enum.
if ((_hr == S_FALSE) || (_hr == HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS))) break; IF_FAILED_EXIT(_hr); // Form local name (in staging area)....
IF_FAILED_EXIT(pDependAsm->Get(MAN_INFO_DEPENDENT_ASM_ID, (LPVOID *)&pDepIdentity, &cb, &dwFlag));
BOOL bIsAvalon = FALSE; IF_FAILED_EXIT(IsAvalonAssembly(pDepIdentity, &bIsAvalon)); #ifdef DEVMODE
if (bIsAvalon && !_bIsDevMode) // download and reinstall anyway if devMode
#else
if (bIsAvalon) #endif
{ CString sCurrentAssemblyPath; IF_FAILED_EXIT(CAssemblyCache::GlobalCacheLookup(pDepIdentity, sCurrentAssemblyPath)); if (_hr == S_OK) { // add to the list of assemblies to be add-ref-ed
CGlobalCacheInstallEntry* pGACInstallEntry = new CGlobalCacheInstallEntry();
IF_ALLOC_FAILED_EXIT(pGACInstallEntry); IF_FAILED_EXIT((pGACInstallEntry->_sCurrentAssemblyPath).Assign(sCurrentAssemblyPath)); _ListGlobalCacheInstall.AddHead(pGACInstallEntry);
SAFERELEASE(pDepIdentity); SAFERELEASE(pDependAsm); continue; } } // Get the identity name
IF_FAILED_EXIT(pDepIdentity->GetAttribute(SXS_ASSEMBLY_IDENTITY_STD_ATTRIBUTE_NAME_NAME, &pwz, &cc)); IF_FAILED_EXIT(sAssemblyName.TakeOwnership(pwz, cc));
// Get dependent asm codebase if any. NOTE - this codebase
// is relative to the appbase
IF_FAILED_EXIT(pDependAsm->Get(MAN_INFO_DEPENDENT_ASM_CODEBASE, (LPVOID *)&pwz, &cb, &dwFlag)); IF_NULL_EXIT(pwz, E_INVALIDARG);
IF_FAILED_EXIT(sRemoteUrl.Assign(sCodebase)); IF_FAILED_EXIT(sRemoteUrl.RemoveLastElement()); // remove manifest file name
IF_FAILED_EXIT(sRemoteUrl.Append(L"/")); // add separator
IF_FAILED_EXIT(sRemoteUrl.Append(pwz)); // Form local cache path from identity name.
IF_FAILED_EXIT(MakeTempManifestLocation(sRemoteUrl, sLocalFilePath)); // Create new job if necessary
if (!*ppJob) { IF_FAILED_EXIT(CreateNewBITSJob(ppJob, sDisplayName));
DWORD cc = 0; LPWSTR pwz = NULL;
// Form local file path...
// Manifest cache directory
IF_FAILED_EXIT(pCacheImport->GetManifestFileDir(&pwz, &cc));
// add this job to reg.
IF_FAILED_EXIT(AddJobToRegistry(sCodebase._pwz, pwz, *ppJob, 0)); }
// Add file to job.
IF_FAILED_EXIT((*ppJob)->AddFile(sRemoteUrl._pwz, sLocalFilePath._pwz));
SAFERELEASE(pDepIdentity); SAFERELEASE(pDependAsm); } _hr = S_OK;
exit:
SAFERELEASE(pIdentity); SAFERELEASE(pManifestImport); SAFERELEASE(pCacheEmit); SAFERELEASE(pMaxCachedImport); SAFERELEASE(pDepIdentity); SAFERELEASE(pDependAsm);
return _hr;
}
// ---------------------------------------------------------------------------
// LookupPatchInfo
// ---------------------------------------------------------------------------
HRESULT CAssemblyDownload::LookupPatchInfo(IAssemblyCacheImport *pCacheImport) { IManifestInfo *pPatchingInfo = NULL; CAssemblyManifestImport *pCManifestImport = NULL; IAssemblyManifestImport *pManifestImport = NULL; IAssemblyIdentity *pIdentity = NULL; IXMLDOMDocument2 *pXMLDoc = NULL;
// Get the manifest import.
IF_FAILED_EXIT(pCacheImport->GetManifestImport(&pManifestImport));
// Get the asm Id
IF_FAILED_EXIT(pManifestImport->GetAssemblyIdentity(&pIdentity));
// Cast IManifestImport to CManifestImport so we can grab the XMLDocument
pCManifestImport = static_cast<CAssemblyManifestImport*> (pManifestImport); IF_NULL_EXIT(pCManifestImport, E_NOINTERFACE); pManifestImport->AddRef();
// Retrieve the top-level xml dom document
IF_FAILED_EXIT(pCManifestImport->GetXMLDoc (&pXMLDoc)); IF_FALSE_EXIT((_hr == S_OK), E_INVALIDARG); //Get patching data if any is available
IF_FAILED_EXIT(CPatchingUtil::CreatePatchingInfo(pXMLDoc, pCacheImport, &pPatchingInfo));
// BUGBUG: CreatePatchingInfo appears to always return S_FALSE, so how did this work?
if (_hr == S_OK) { _pPatchingInfo = pPatchingInfo; _pPatchingInfo->AddRef(); }
exit: SAFERELEASE(pPatchingInfo); SAFERELEASE(pXMLDoc); SAFERELEASE(pCManifestImport); SAFERELEASE(pManifestImport); SAFERELEASE(pIdentity);
return _hr; }
// ---------------------------------------------------------------------------
//ApplyPatchFile
// ---------------------------------------------------------------------------
HRESULT CAssemblyDownload::ApplyPatchFile (LPWSTR pwzPatchFilePath) { int i = 0; LPWSTR pwzBuf; DWORD ccBuf, cbBuf, dwFlag; CString sPatchLocalName; CString sPatchDisplayName; CString sManifestDir, sPatchManifestDir; CString sSourcePath, sTargetPath, sPatchPath; CString sSourceFile, sTargetFile; IManifestInfo *pPatchFileInfo=NULL; IPatchingUtil *pPatchingUtil=NULL; IAssemblyIdentity *pSourceAssemblyId = NULL;
IF_NULL_EXIT(_pPatchingInfo, E_INVALIDARG);
// get patchingutil from patchInfo
IF_FAILED_EXIT(_pPatchingInfo->Get(MAN_INFO_SOURCE_ASM_PATCH_UTIL, (LPVOID *)&pPatchingUtil, &cbBuf, &dwFlag));
// get the manifest Directory
IF_FAILED_EXIT(_pPatchingInfo->Get(MAN_INFO_SOURCE_ASM_INSTALL_DIR, (LPVOID *)&pwzBuf, &cbBuf, &dwFlag)); IF_FAILED_EXIT(sManifestDir.TakeOwnership (pwzBuf));
// get the source assembly directory
IF_FAILED_EXIT(_pPatchingInfo->Get(MAN_INFO_SOURCE_ASM_DIR, (LPVOID *)&pwzBuf, &cbBuf, &dwFlag)); IF_FAILED_EXIT(sPatchManifestDir.TakeOwnership (pwzBuf));
// get SourceAssembly Id from patchInfo
IF_FAILED_EXIT(_pPatchingInfo->Get(MAN_INFO_SOURCE_ASM_ID, (LPVOID *)&pSourceAssemblyId, &cbBuf, &dwFlag)); // Get DisplayName of the Source Assembly
IF_FAILED_EXIT(pSourceAssemblyId->GetDisplayName(ASMID_DISPLAYNAME_NOMANGLING, &pwzBuf, &ccBuf)); IF_FAILED_EXIT(sPatchDisplayName.TakeOwnership(pwzBuf, ccBuf)); //Parse out the local file path from the full file path of the patch file
pwzBuf= StrStr(pwzPatchFilePath, sPatchDisplayName._pwz); IF_NULL_EXIT(pwzBuf, E_FAIL); pwzBuf = StrChr(pwzBuf, L'\\'); IF_NULL_EXIT(pwzBuf, E_FAIL); pwzBuf++;
IF_FAILED_EXIT(sPatchLocalName.Assign(pwzBuf)); IF_FAILED_EXIT(pPatchingUtil->MatchPatch(sPatchLocalName._pwz, &pPatchFileInfo));
IF_FAILED_EXIT(pPatchFileInfo->Get(MAN_INFO_PATCH_INFO_SOURCE, (LPVOID *)&pwzBuf, &cbBuf, &dwFlag));
IF_FAILED_EXIT(sSourceFile.TakeOwnership(pwzBuf));
IF_FAILED_EXIT(pPatchFileInfo->Get(MAN_INFO_PATCH_INFO_TARGET, (LPVOID *)&pwzBuf, &cbBuf, &dwFlag)); IF_FAILED_EXIT(sTargetFile.TakeOwnership(pwzBuf));
IF_FAILED_EXIT(sSourcePath.Append(sPatchManifestDir)); IF_FAILED_EXIT(sSourcePath.Append(sSourceFile)); // set up Target path
IF_FAILED_EXIT(sTargetPath.Assign(sManifestDir)); IF_FAILED_EXIT(sTargetPath.Append(sTargetFile)); // set up Patch path
IF_FAILED_EXIT(sPatchPath.Assign(pwzPatchFilePath));
//Apply patchfile to sSource (grab from patch directory) and copy to path specified by sTarget
IF_WIN32_FALSE_EXIT(ApplyPatchToFile((LPCWSTR)sPatchPath._pwz, (LPCWSTR)sSourcePath._pwz, (LPCWSTR)sTargetPath._pwz, 0)); exit: SAFERELEASE(pPatchFileInfo); SAFERELEASE(pSourceAssemblyId); SAFERELEASE(pPatchingUtil); return _hr; }
// ---------------------------------------------------------------------------
// ResolveFile
// ---------------------------------------------------------------------------
HRESULT CAssemblyDownload::ResolveFile(CString &sFileName, CString &sLocalFilePath) { LPWSTR pwzBuf; DWORD cbBuf, dwFlag; IPatchingUtil *pPatchingUtil=NULL; IManifestInfo *pPatchFileInfo=NULL; CString sPatchFileName; CString sTempDirectoryPath;
IF_NULL_EXIT(_pPatchingInfo, E_INVALIDARG); //grab the patchingUtil from the _pPatchingInfo
IF_FAILED_EXIT(_pPatchingInfo->Get(MAN_INFO_SOURCE_ASM_PATCH_UTIL, (LPVOID *)&pPatchingUtil, &cbBuf, &dwFlag));
//Check to see if the file referenced by sFileName has an available patch
// if it does, download the patch by overriding sFileName with the patchFile name
// and override the sLocalFilePath with the temporary directory to store the patch file
IF_FAILED_EXIT(pPatchingUtil->MatchTarget(sFileName._pwz, &pPatchFileInfo));
// BUGBUG- want to exit but not break out in debugger here.
IF_FALSE_EXIT((_hr == S_OK), S_FALSE); IF_FAILED_EXIT(pPatchFileInfo->Get(MAN_INFO_PATCH_INFO_PATCH, (LPVOID *)&pwzBuf, &cbBuf, &dwFlag)); IF_FAILED_EXIT(sPatchFileName.TakeOwnership(pwzBuf));
IF_FAILED_EXIT(_pPatchingInfo->Get(MAN_INFO_SOURCE_ASM_TEMP_DIR, (LPVOID *)&pwzBuf, &cbBuf, &dwFlag)); IF_FAILED_EXIT(sTempDirectoryPath.TakeOwnership(pwzBuf));
IF_FAILED_EXIT(sFileName.Assign (sPatchFileName));
// Assign the patch directory to local file path
IF_FAILED_EXIT( sLocalFilePath.Assign(sTempDirectoryPath)); IF_FAILED_EXIT(::CreateDirectoryHierarchy(sLocalFilePath._pwz, sFileName._pwz));
exit:
SAFERELEASE(pPatchFileInfo); SAFERELEASE(pPatchingUtil);
return _hr; }
// ---------------------------------------------------------------------------
// CleanUpPatchDir
// ---------------------------------------------------------------------------
HRESULT CAssemblyDownload::CleanUpPatchDir() { LPWSTR pwz = NULL; DWORD cb = 0, dwFlag = 0; CString sTempPatchDirectory;
IF_NULL_EXIT(_pPatchingInfo, E_INVALIDARG);
IF_FAILED_EXIT(_pPatchingInfo->Get(MAN_INFO_SOURCE_ASM_TEMP_DIR, (LPVOID *)&pwz, &cb, &dwFlag)); IF_NULL_EXIT(pwz, E_INVALIDARG); IF_FAILED_EXIT(sTempPatchDirectory.TakeOwnership(pwz)); IF_FAILED_EXIT(sTempPatchDirectory.RemoveLastElement()); IF_FAILED_EXIT(sTempPatchDirectory.RemoveLastElement()); IF_FAILED_EXIT(RemoveDirectoryAndChildren(sTempPatchDirectory._pwz));
exit: return _hr; }
// ---------------------------------------------------------------------------
// CreateNewBITSJob
// ---------------------------------------------------------------------------
HRESULT CAssemblyDownload::CreateNewBITSJob(IBackgroundCopyJob **ppJob, CString &sDisplayName) { GUID guid = {0};
// Connect to BITS if not already connected.
IF_FAILED_EXIT(InitBITS());
// Create the job.
IF_FAILED_EXIT(g_pBITSManager->CreateJob(sDisplayName._pwz, BG_JOB_TYPE_DOWNLOAD, &guid, ppJob));
// Set job in dialog object.
// Note - potential race condition if job methods are called before the
// dialog references it since we can immediately begin to get
// callbacks
if (_pDlg) _pDlg->SetJobObject(*ppJob); // Construct and pass in callback object.
CBitsCallback *pBCB = new CBitsCallback(this); IF_ALLOC_FAILED_EXIT(pBCB); IF_FAILED_EXIT((*ppJob)->SetNotifyInterface(static_cast<IBackgroundCopyCallback*> (pBCB))); pBCB->Release();
// Set job config info.
IF_FAILED_EXIT((*ppJob)->SetNotifyFlags(BG_NOTIFY_JOB_MODIFICATION | BG_NOTIFY_JOB_TRANSFERRED | BG_NOTIFY_JOB_ERROR));
//The default priority level for a job is BG_JOB_PRIORITY_NORMAL (background).
if (_pDlg) IF_FAILED_EXIT((*ppJob)->SetPriority(BG_JOB_PRIORITY_FOREGROUND));
SetJobObject(*ppJob);
exit: return _hr; }
// ---------------------------------------------------------------------------
// MakeTempManifestLocation
// ALL manifests are first downloaded to a location generated in this method.
// ---------------------------------------------------------------------------
HRESULT CAssemblyDownload::MakeTempManifestLocation(CString &sRemoteUrlName, CString& sManifestFilePath) { WCHAR wzRandom[8+1]={0}; CString sRelativePath; CString sTempDirPath; /* C:\Documents and Settings\<user>\Local Settings\My Programs\__temp__\__manifests__\ */ IF_FAILED_EXIT(CAssemblyCache::GetCacheRootDir(sManifestFilePath, CAssemblyCache::Manifests));
// Create a randomized directory name.
// sRelativePath is simply the manifest file name
// in the case that no appbase is available (Subscription manifest case).
// \_temp__\__manifests__\xyz123\subscription.manifest
// if (!_sAppBase._pwz) // ******* Relative path Dir is to be done in Dest dir and not in temp-man-location
IF_FAILED_EXIT(sRemoteUrlName.LastElement(sRelativePath));
// Otherwise we extract the relative path based on the appbase.
// This is important because sManifestFilePath is persisted in the BITS
// job and the relative path is extracted from this and used for commit
// to cache.
// \_temp__\__manifests__\xyz123\foo.manifest
// \_temp__\__manifests__\xyz123\bar\bar.dll
// ^^^^^^^
/*
else { // http://foo/appbase/
// http://foo/appbase/bar/bar.dll
// ^^^^^^^
IF_FAILED_EXIT(sRemoteUrlName.StartsWith(_sAppBase._pwz)); IF_FALSE_EXIT((_hr==S_OK), E_INVALIDARG); pwzBuf = sRemoteUrlName._pwz + _sAppBase._cc - 1; IF_FAILED_EXIT(sRelativePath.Assign(pwzBuf)); } */
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(sRelativePath)); IF_FAILED_EXIT(sManifestFilePath.PathNormalize());
IF_FAILED_EXIT(::CreateDirectoryHierarchy(NULL, sManifestFilePath._pwz));
exit:
return _hr; }
// ---------------------------------------------------------------------------
// IsManifestFile
//
// This is somewhat hacky - we rely on the local target path
// ---------------------------------------------------------------------------
HRESULT CAssemblyDownload::IsManifestFile(IBackgroundCopyFile *pFile, BOOL *pbIsManifestFile) { LPWSTR pwz = NULL; CString sManifestStagingDir; CString sLocalName(CString::COM_Allocator); // Get local manifest file name.
IF_FAILED_EXIT(pFile->GetLocalName(&pwz)); IF_FAILED_EXIT(sLocalName.TakeOwnership(pwz)); IF_FAILED_EXIT(CAssemblyCache::GetCacheRootDir(sManifestStagingDir, CAssemblyCache::Manifests)); IF_FAILED_EXIT(sLocalName.StartsWith(sManifestStagingDir._pwz));
if (_hr == S_OK) *pbIsManifestFile = TRUE; else if (_hr == S_FALSE) *pbIsManifestFile = FALSE; exit:
return _hr; }
// ---------------------------------------------------------------------------
// InstallGlobalAssemblies
// ---------------------------------------------------------------------------
HRESULT CAssemblyDownload::InstallGlobalAssemblies() { // Needed for template list.
LISTNODE pos; CGlobalCacheInstallEntry *pEntry = NULL; LPWSTR pwz = NULL; DWORD cc = 0;
// Walk list; install each assembly.
pos = _ListGlobalCacheInstall.GetHeadPosition(); while (pos && (pEntry = _ListGlobalCacheInstall.GetNext(pos))) { CString sManifestFilePath;
// Install/addref each assembly. If the ICacheImport is available, it means
// that install take place from appbase, else addref using current GAC assembly path.
IF_FAILED_EXIT(CAssemblyCache::GlobalCacheInstall(pEntry->_pICacheImport, pEntry->_sCurrentAssemblyPath, _sAppDisplayName)); // Get the assembly path if under the appbase.
if (pEntry->_pICacheImport != NULL) { IF_FAILED_EXIT(pEntry->_pICacheImport->GetManifestFilePath(&pwz, &cc)); IF_FAILED_EXIT(sManifestFilePath.TakeOwnership(pwz)); }
// this releases interface pointers
delete pEntry;
// we should call delete only after releasing interfaces....
if(sManifestFilePath._cc > 1) IF_FAILED_EXIT(CAssemblyCache::DeleteAssemblyAndModules(sManifestFilePath._pwz)); }
exit: // Free all the list nodes.
_ListGlobalCacheInstall.RemoveAll();
return _hr; }
// ---------------------------------------------------------------------------
// SetJobObject
// ---------------------------------------------------------------------------
VOID CAssemblyDownload::SetJobObject(IBackgroundCopyJob *pJob) { SAFERELEASE(_pJob);
if (pJob) { _pJob = pJob; _pJob->AddRef(); } }
// ---------------------------------------------------------------------------
// FinishDownload
// ---------------------------------------------------------------------------
HRESULT CAssemblyDownload::FinishDownload() { KillTimer(_pDlg->_hwndDlg, 0); DestroyWindow(_pDlg->_hwndDlg); return S_OK; }
// ---------------------------------------------------------------------------
// SignalAbort
// ---------------------------------------------------------------------------
HRESULT CAssemblyDownload::SignalAbort() { InterlockedIncrement((LONG*) &_bAbort); return S_OK; }
// ---------------------------------------------------------------------------
// DoEvilAvalonRegistrationHack
// ---------------------------------------------------------------------------
HRESULT CAssemblyDownload::DoEvilAvalonRegistrationHack() { HINSTANCE hInst = 0; INT iCompare = 0; LPWSTR pwz = NULL; DWORD cc = 0; IAssemblyManifestImport *pManifestImport = NULL; IAssemblyIdentity *pAppIdentity = NULL; CString sAppName, sBatchFilePath, sDisplayName, sAppDir;
if (!_pRootEmit) goto exit; IF_FAILED_EXIT(_pRootEmit->GetManifestImport(&pManifestImport)); IF_FAILED_EXIT(pManifestImport->GetAssemblyIdentity(&pAppIdentity));
IF_FAILED_EXIT(pAppIdentity->GetAttribute(SXS_ASSEMBLY_IDENTITY_STD_ATTRIBUTE_NAME_NAME, &pwz, &cc));
IF_FAILED_EXIT(sAppName.TakeOwnership(pwz)); iCompare = CompareString(LOCALE_USER_DEFAULT, 0, sAppName._pwz, -1, L"Microsoft.Avalon.AvPad", -1);
IF_WIN32_FALSE_EXIT(iCompare); if (iCompare == CSTR_EQUAL) { IF_FAILED_EXIT(pAppIdentity->GetDisplayName(0, &pwz, &cc)); IF_FAILED_EXIT(sDisplayName.TakeOwnership(pwz));
IF_FAILED_EXIT(CAssemblyCache::GetCacheRootDir(sAppDir, CAssemblyCache::Base)); IF_FAILED_EXIT(sAppDir.Append(sDisplayName));
IF_FAILED_EXIT(sBatchFilePath.Assign(sAppDir)); IF_FAILED_EXIT(sBatchFilePath.Append(L"\\doi.bat"));
hInst = ShellExecute(NULL, L"open", sBatchFilePath._pwz, NULL, sAppDir._pwz, SW_HIDE);
if ((DWORD_PTR) hInst <= 32) _hr = HRESULT_FROM_WIN32((DWORD_PTR) hInst); else _hr = S_OK; }
exit:
SAFERELEASE(pManifestImport); SAFERELEASE(pAppIdentity);
return _hr;
}
// ---------------------------------------------------------------------------
// IsAvalonAssembly
// ---------------------------------------------------------------------------
HRESULT CAssemblyDownload::IsAvalonAssembly(IAssemblyIdentity *pId, BOOL *pbIsAvalon) { INT iCompare = 0; LPWSTR pwz = NULL; DWORD cc = 0; CString sPublicKeyToken;
// System Public key tokens;
// One of these is the ECMA key, I can't remember which.
const LPWSTR wzNDPToken1 = L"b03f5f7f11d50a3a"; const LPWSTR wzNDPToken2 = L"b77a5c561934e089";
// Trusted avalon public key token.
const LPWSTR wzAvalonToken = L"a29c01bbd4e39ac5";
LPWSTR wzTokens[] = {wzNDPToken1, wzNDPToken2, wzAvalonToken}; *pbIsAvalon = FALSE; // Get the public key token in string form.
// bugbuG: COULD USE IF_TRUE_EXIT MACRO.
_hr = pId->GetAttribute(SXS_ASSEMBLY_IDENTITY_STD_ATTRIBUTE_NAME_PUBLIC_KEY_TOKEN, &pwz, &cc); if (_hr == HRESULT_FROM_WIN32(ERROR_NOT_FOUND)) { _hr = S_FALSE; goto exit; } IF_FAILED_EXIT(_hr); IF_FAILED_EXIT(sPublicKeyToken.TakeOwnership(pwz)); // Check for trusted assembly
_hr = S_FALSE; for (int i = 0; i < ( sizeof(wzTokens) / sizeof(wzTokens[0]) ); i++) { iCompare = CompareString(LOCALE_USER_DEFAULT, 0, (LPCWSTR) wzTokens[i], -1, sPublicKeyToken._pwz, -1);
IF_WIN32_FALSE_EXIT(iCompare); if (iCompare == CSTR_EQUAL) { _hr = S_OK; *pbIsAvalon = TRUE; break; } }
exit:
return _hr; }
// ---------------------------------------------------------------------------
// InitBits
// BUGBUG: not thread safe.
// ---------------------------------------------------------------------------
HRESULT CAssemblyDownload::InitBITS() { HRESULT hr = S_OK; MAKE_ERROR_MACROS_STATIC(hr); // Connect to BITS if not already connected.
// BUGBUG - possibly leaking ptr if race condition.
if (!g_pBITSManager) { IF_FAILED_EXIT(CoCreateInstance(CLSID_BackgroundCopyManager, NULL, CLSCTX_LOCAL_SERVER, IID_IBackgroundCopyManager, (void**) &g_pBITSManager)); }
exit:
return hr; }
HRESULT CAssemblyDownload::GetBITSErrorMsg(IBackgroundCopyError *pError, CString &sMessage) { HRESULT hrBITSError = S_OK; HRESULT hr = S_OK; MAKE_ERROR_MACROS_STATIC(hr); LPWSTR pwz = NULL;
CString sDescription(CString::COM_Allocator); CString sContext(CString::COM_Allocator); CString sRemoteName(CString::COM_Allocator); CString sLocalName(CString::COM_Allocator);
IBackgroundCopyFile *pFile = NULL; BG_ERROR_CONTEXT eCtx;
// Get the BITS error.
IF_FAILED_EXIT(pError->GetError(&eCtx, &hrBITSError));
// Get the error description
IF_FAILED_EXIT(pError->GetErrorDescription( LANGIDFROMLCID( GetThreadLocale() ), &pwz)); IF_FAILED_EXIT(sDescription.TakeOwnership(pwz));
// Get the error context
IF_FAILED_EXIT(pError->GetErrorContextDescription( LANGIDFROMLCID( GetThreadLocale() ), &pwz)); IF_FAILED_EXIT(sContext.TakeOwnership(pwz));
// Form UI message.
IF_FAILED_EXIT(sMessage.Assign(sDescription)); IF_FAILED_EXIT(sMessage.Append(sContext));
// If error due to remote or local file, indicate this in message.
if ((BG_ERROR_CONTEXT_LOCAL_FILE == eCtx) || (BG_ERROR_CONTEXT_REMOTE_FILE == eCtx)) { IF_FAILED_EXIT(pError->GetFile(&pFile)); IF_FAILED_EXIT(pFile->GetRemoteName(&pwz)); IF_FAILED_EXIT(sRemoteName.TakeOwnership(pwz)); IF_FAILED_EXIT(pFile->GetLocalName(&pwz)); IF_FAILED_EXIT(sLocalName.TakeOwnership(pwz));
IF_FAILED_EXIT(sMessage.Append(L"\r\nURL: ")); IF_FAILED_EXIT(sMessage.Append(sRemoteName));
IF_FAILED_EXIT(sMessage.Append(L"\r\n\r\nFile: ")); IF_FAILED_EXIT(sMessage.Append(sLocalName)); }
exit :
SAFERELEASE(pFile); return hr; }
// ---------------------------------------------------------------------------
// CleanUpTempFilesOnError
// ---------------------------------------------------------------------------
HRESULT CAssemblyDownload::CleanUpTempFilesOnError(IBackgroundCopyJob *pJob) { // Return codes in this function don't affect
// last error (this->_hr).
HRESULT hr = S_OK; MAKE_ERROR_MACROS_STATIC(hr);
DWORD nCount = 0; LPWSTR pwz=NULL; IEnumBackgroundCopyFiles *pEnumFiles = NULL; IBackgroundCopyFile *pFile = NULL;
CString sLocalName(CString::COM_Allocator);
// Get the file enumerator.
IF_FAILED_EXIT(pJob->EnumFiles(&pEnumFiles)); IF_FAILED_EXIT(pEnumFiles->GetCount(&nCount));
// Enumerate the files in the job.
for (DWORD i = 0; i < nCount; i++) { IF_FAILED_EXIT(pEnumFiles->Next(1, &pFile, NULL)); // Get local file name.
IF_FAILED_EXIT(pFile->GetLocalName(&pwz)); IF_FAILED_EXIT(sLocalName.TakeOwnership(pwz)); pwz = NULL;
IF_FAILED_EXIT(sLocalName.RemoveLastElement()); IF_FAILED_EXIT(RemoveDirectoryAndChildren(sLocalName._pwz));
SAFERELEASE(pFile); }
exit:
SAFERELEASE(pEnumFiles); SAFERELEASE(pFile);
if(pwz) CoTaskMemFree(pwz); return hr; }
// ---------------------------------------------------------------------------
// HandleError
// ---------------------------------------------------------------------------
HRESULT CAssemblyDownload::HandleError(IBackgroundCopyError *pError, IBackgroundCopyJob *pJob) { // Return codes in this function don't affect
// last error (this->_hr).
HRESULT hr = S_OK; MAKE_ERROR_MACROS_STATIC(hr); CString sMessage(CString::COM_Allocator);
// HandleError can be called multiple times on different threads
// but error is handled only once per lifetime of the object.
if (!(InterlockedIncrement((LONG*) &_bErrorHandled) == 1)) goto exit;
SetErrorCode(STG_E_TERMINATED);
DEBUGOUT1(_pDbgLog, 1, L" LOG: hr = %x in HandleError()", this->_hr);
// If an IBackgroundCopyError ptr is provided.
if ( pError) { IF_FAILED_EXIT(GetBITSErrorMsg(pError, sMessage));
DEBUGOUT(_pDbgLog, 1, L" LOG: BITS error msg follows. ");
DEBUGOUT1(_pDbgLog, 0, L" ERR: %s \n", sMessage._pwz);
}
if(_sAppBase._pwz) DEBUGOUT1(_pDbgLog, 1, L" LOG: AppBase = %s ", _sAppBase._pwz);
if(_sAppDisplayName._pwz) DEBUGOUT1(_pDbgLog, 1, L" LOG: DisplayName = %s ", _sAppDisplayName._pwz);
if (_pJob) { // Cancel job.
IF_FAILED_EXIT(_pJob->Cancel());
// Cleanup registry state associated with job.
IF_FAILED_EXIT(RemoveJobFromRegistry(_pJob, NULL, SHREGDEL_HKCU, RJFR_DELETE_FILES));
// Clean-up temp files from the job
CleanUpTempFilesOnError(_pJob);
SetJobObject(NULL); } else if (pJob) { // Clean-up for specified job, only if _pJob == NULL, only applies for JobTransferred case
CleanUpTempFilesOnError(pJob); }
// Notify bindsink
if (_pBindSink) { if (_bAbort || (_hr == E_ABORT)) _pBindSink->OnProgress(ASM_NOTIFICATION_ABORT, _hr, NULL, 0, 0, NULL); else _pBindSink->OnProgress(ASM_NOTIFICATION_ERROR, _hr, NULL, 0, 0, NULL);
// Ensure this is last notification bindsink receives resulting from
// subsequent JobModified notifications.
// DO NOT free the bindsink here.
_pBindSink = NULL; }
// Terminate UI.
if (_pDlg) PostMessage(_pDlg->_hwndDlg, WM_FINISH_DOWNLOAD, 0, 0);
exit:
// Return error which generated handling.
// DownloadManifestAndDependencies will return this.
return _hr; }
HRESULT CAssemblyDownload::SetErrorCode(HRESULT dwHr) { BOOL bSetError = FALSE;
::EnterCriticalSection(&_cs);
if(SUCCEEDED(_hrError)) { _hrError = dwHr; bSetError = TRUE; }
::LeaveCriticalSection(&_cs);
if(!bSetError) { // We couldn't set error code atleast write some log.
DEBUGOUT1(_pDbgLog, 1, L" LOG : Could not set error code hr = %x ", dwHr); }
return S_OK; }
// IBackgroundCopyCallback methods
// ---------------------------------------------------------------------------
// JobTransferred
// ---------------------------------------------------------------------------
HRESULT CAssemblyDownload::JobTransferred(IBackgroundCopyJob *pJob) { ASSERT(pJob == _pJob);
// Serialize all calls to object.
::EnterCriticalSection(&_cs);
// Check the abort flag first.
IF_TRUE_EXIT(_bAbort, E_ABORT);
// Job is complete; process results.
IF_FAILED_EXIT(DoCacheUpdate(pJob));
exit:
// Handle any error.
if (FAILED(_hr)) HandleError(NULL, pJob); // pJob only applies after _pJob is set to NULL in DoCacheUpdate()
::LeaveCriticalSection(&_cs); // Always return success to BITS.
return S_OK; }
// ---------------------------------------------------------------------------
// JobError
// ---------------------------------------------------------------------------
HRESULT CAssemblyDownload::JobError(IBackgroundCopyJob *pJob, IBackgroundCopyError *pError) { // Serialize all calls to object.
::EnterCriticalSection(&_cs);
// Handle any error. Presumeably, if this is concurrent with an abort,
// the error handling code can do the right thing.
HandleError(pError, NULL); ::LeaveCriticalSection(&_cs);
// Always return success to BITS.
return S_OK; }
// ---------------------------------------------------------------------------
// JobModification
// ---------------------------------------------------------------------------
HRESULT CAssemblyDownload::JobModification(IBackgroundCopyJob *pJob, DWORD dwReserved) { ::EnterCriticalSection(&_cs);
IBackgroundCopyError *pError = NULL;
// Check the abort flag first.
IF_TRUE_EXIT(_bAbort, E_ABORT);
// JobModification can still be called a few times after abort.
IF_TRUE_EXIT(_hr == E_ABORT, _hr);
if (_pDlg) IF_FAILED_EXIT(_pDlg->HandleUpdate());
BG_JOB_STATE state;
IF_FAILED_EXIT(pJob->GetState( &state ));
if(state == BG_JOB_STATE_TRANSIENT_ERROR) { if(pJob->GetError(&pError) == S_OK) { if(_pDlg) { HandleError(pError, NULL); } else { CString sMessage(CString::COM_Allocator);
IF_FAILED_EXIT(GetBITSErrorMsg(pError, sMessage));
DEBUGOUT1(_pDbgLog, 0, L"LOG: TRANSIENT ERROR from BITS. Error msg is : %s", sMessage._pwz); } } }
exit:
// Handle any error.
if (FAILED(_hr)) HandleError(NULL, NULL);
::LeaveCriticalSection(&_cs); SAFERELEASE(pError);
return S_OK; }
// Privates
// IUnknown methods
// ---------------------------------------------------------------------------
// CAssemblyDownload::QI
// ---------------------------------------------------------------------------
STDMETHODIMP CAssemblyDownload::QueryInterface(REFIID riid, void** ppvObj) { if ( IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IAssemblyDownload) ) { *ppvObj = static_cast<IAssemblyDownload*> (this); AddRef(); return S_OK; } else if (IsEqualIID(riid, IID_IBackgroundCopyCallback)) { *ppvObj = static_cast<IBackgroundCopyCallback*> (this); AddRef(); return S_OK; } else { *ppvObj = NULL; return E_NOINTERFACE; } }
// ---------------------------------------------------------------------------
// CAssemblyDownload::AddRef
// ---------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CAssemblyDownload::AddRef() { return InterlockedIncrement ((LONG*) &_cRef); }
// ---------------------------------------------------------------------------
// CAssemblyDownload::Release
// ---------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CAssemblyDownload::Release() { ULONG lRet = InterlockedDecrement ((LONG*) &_cRef); if (!lRet) delete this; return lRet; }
///////////////////////////////////////////////////////////////////////////////
// CBitsCallback
//
// ---------------------------------------------------------------------------
// ctor
// ---------------------------------------------------------------------------
CBitsCallback::CBitsCallback(IAssemblyDownload *pDownload) : _cRef(1), _dwSig(' BCB'), _hr(S_OK) { _pDownload = pDownload; _pDownload->AddRef(); }
// ---------------------------------------------------------------------------
// dtor
// ---------------------------------------------------------------------------
CBitsCallback::~CBitsCallback() { SAFERELEASE(_pDownload); }
// IBitsCallback methods
// ---------------------------------------------------------------------------
// JobTransferred
// ---------------------------------------------------------------------------
HRESULT CBitsCallback::JobTransferred(IBackgroundCopyJob *pJob) { return _pDownload->JobTransferred(pJob); }
// ---------------------------------------------------------------------------
// JobError
// ---------------------------------------------------------------------------
HRESULT CBitsCallback::JobError(IBackgroundCopyJob *pJob, IBackgroundCopyError *pError) { return _pDownload->JobError(pJob, pError); }
// ---------------------------------------------------------------------------
// JobModification
// ---------------------------------------------------------------------------
HRESULT CBitsCallback::JobModification(IBackgroundCopyJob *pJob, DWORD dwReserved) { return _pDownload->JobModification(pJob, dwReserved); }
// Privates
// IUnknown methods
// ---------------------------------------------------------------------------
// CBitsCallback::QI
// ---------------------------------------------------------------------------
STDMETHODIMP CBitsCallback::QueryInterface(REFIID riid, void** ppvObj) { if ( IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IBackgroundCopyCallback) ) { *ppvObj = static_cast<IBackgroundCopyCallback*> (this); AddRef(); return S_OK; } else { *ppvObj = NULL; return E_NOINTERFACE; } }
// ---------------------------------------------------------------------------
// CBitsCallback::AddRef
// ---------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CBitsCallback::AddRef() { return InterlockedIncrement ((LONG*) &_cRef); }
// ---------------------------------------------------------------------------
// CBitsCallback::Release
// ---------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CBitsCallback::Release() { ULONG lRet = InterlockedDecrement ((LONG*) &_cRef); if (!lRet) delete this; return lRet; }
///////////////////////////////////////////////////////////////////////////////
// CGlobalCacheInstallEntry
//
// ---------------------------------------------------------------------------
// ctor
// ---------------------------------------------------------------------------
CGlobalCacheInstallEntry::CGlobalCacheInstallEntry() : _dwSig('ECAG') { _pICacheImport = NULL; }
// ---------------------------------------------------------------------------
// dtor
// ---------------------------------------------------------------------------
CGlobalCacheInstallEntry::~CGlobalCacheInstallEntry() { SAFERELEASE(_pICacheImport); }
|