You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2066 lines
67 KiB
2066 lines
67 KiB
|
|
#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);
|
|
}
|
|
|