windows-nt/Source/XPSP1/NT/base/win32/fusion/installer/download/assemblydownload.cpp
2020-09-26 16:20:57 +08:00

994 lines
34 KiB
C++

#include <fusenetincludes.h>
#include <bits.h>
#include <assemblycache.h>
#include "dialog.h"
#include <assemblydownload.h>
#include "..\id\sxsid.h"
#include ".\patchapi.h"
// Update services
#include "server.h"
#define DOWNLOAD_FLAGS_INTERNAL_TRAVERSE_LINK (DOWNLOAD_FLAGS_NOTIFY_COMPLETION + 1)
#define PATCH_DIRECTORY L"__patch__\\"
IBackgroundCopyManager* CAssemblyDownload::g_pManager = NULL;
// ---------------------------------------------------------------------------
// CreateAssemblyDownload
// ---------------------------------------------------------------------------
STDAPI CreateAssemblyDownload(IAssemblyDownload** ppDownload)
{
HRESULT hr = S_OK;
CAssemblyDownload *pDownload = new(CAssemblyDownload);
if (!pDownload)
{
hr = E_OUTOFMEMORY;
goto exit;
}
exit:
*ppDownload = (IAssemblyDownload*) pDownload;
return hr;
}
// ---------------------------------------------------------------------------
// ctor
// ---------------------------------------------------------------------------
CAssemblyDownload::CAssemblyDownload()
: _dwSig('DLND'), _cRef(1), _hr(S_OK), _pRootEmit(NULL), _hNamedEvent(NULL),
_pDlg(NULL)
{}
// ---------------------------------------------------------------------------
// dtor
// ---------------------------------------------------------------------------
CAssemblyDownload::~CAssemblyDownload()
{
SAFERELEASE(_pRootEmit);
SAFEDELETE(_pDlg);
// BUGBUG: Do proper ref counting and release here
//SAFERELEASE(g_pManager);
}
// IAssemblyDownload methods
void MakeSequentialFileName(CString& sPath)
{
int iSeqNum = 1;
int iIndex = sPath._cc;
// BUGBUG: hack up name generation code
sPath.Append(L"[1]");
while (GetFileAttributes(sPath._pwz) != (DWORD)-1)
{
// keep incrementing till unique...
iSeqNum++;
// BUGBUG: note string len limitation
WCHAR buffer[20];
_itow(iSeqNum, buffer, 10);
sPath[iIndex] = L'/';
sPath.RemoveLastElement();
sPath.Append(buffer);
sPath.Append(L"]");
// BUGBUG: MAX_PATH restriction?
}
}
// ---------------------------------------------------------------------------
// DownloadManifestAndDependencies
// ---------------------------------------------------------------------------
HRESULT CAssemblyDownload::DownloadManifestAndDependencies(
LPWSTR pwzApplicationManifestUrl, HANDLE hNamedEvent, DWORD dwFlags)
{
HRESULT hr = S_OK;
LPWSTR pwz = NULL;
IBackgroundCopyJob *pJob = NULL;
GUID guid = {0};
CString sTempManifestPath;
CString sAppUrl;
CString sManifestFileName;
// Create temporary manifest path from url.
sAppUrl.Assign(pwzApplicationManifestUrl);
sAppUrl.LastElement(sManifestFileName);
CAssemblyCache::GetCacheRootDir(sTempManifestPath, CAssemblyCache::Staging);
sTempManifestPath.Append(sManifestFileName._pwz);
MakeSequentialFileName(sTempManifestPath);
// BUGBUG - do real check
if (!g_pManager)
{
CoCreateInstance(CLSID_BackgroundCopyManager, NULL, CLSCTX_LOCAL_SERVER,
IID_IBackgroundCopyManager, (void**) &g_pManager);
}
// just give it a location name
hr = g_pManager->CreateJob(pwzApplicationManifestUrl,
BG_JOB_TYPE_DOWNLOAD, &guid, &pJob);
// Init dialog object with job.
if (dwFlags == DOWNLOAD_FLAGS_PROGRESS_UI)
{
hr = CreateDialogObject(&_pDlg, pJob);
//felixybc no need _pDlg->_pDownload = this; // bugbug - if addref, circular refcount
}
else if ((dwFlags == DOWNLOAD_FLAGS_INTERNAL_TRAVERSE_LINK) && _pDlg)
_pDlg->SetJobObject(pJob);
else if (dwFlags == DOWNLOAD_FLAGS_NOTIFY_COMPLETION)
_hNamedEvent = hNamedEvent;
// Set job config info.
hr = pJob->SetNotifyInterface(static_cast<IBackgroundCopyCallback*> (this));
hr = pJob->SetNotifyFlags(BG_NOTIFY_JOB_MODIFICATION | BG_NOTIFY_JOB_TRANSFERRED | BG_NOTIFY_JOB_ERROR);
hr = pJob->SetPriority(BG_JOB_PRIORITY_FOREGROUND);
// Ensure local dir path exists.
CAssemblyCache::CreateDirectoryHierarchy(NULL, sTempManifestPath._pwz);
// Do the download;
pJob->AddFile(pwzApplicationManifestUrl, sTempManifestPath._pwz);
pJob->Resume();
// We're releasing the job but BITS addrefs it.
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_FINISH_DOWNLOAD || dwLow == WM_CANCEL_DOWNLOAD)
{
DestroyWindow(_pDlg->_hwndDlg);
// BUGBUG: delete all committed files and app dir if canceling!
if (dwLow == WM_CANCEL_DOWNLOAD)
hr = E_ABORT;
break;
}
if (bRet == -1)
{
dwError = GetLastError();
DebugBreak();
}
if (!IsDialogMessage(_pDlg->_hwndDlg, &msg))
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
}
}
return hr;
}
// ---------------------------------------------------------------------------
// DoCacheUpdate
// ---------------------------------------------------------------------------
HRESULT CAssemblyDownload::DoCacheUpdate(IBackgroundCopyJob *pJob)
{
LPWSTR pwz = NULL;
DWORD nCount = 0, cc = 0;
SERIALIZED_LIST ManifestList = {0};
IEnumBackgroundCopyFiles *pEnumFiles = NULL;
IBackgroundCopyFile *pFile = NULL;
IAssemblyCacheEmit *pEmit = NULL;
IAssemblyCacheImport *pCacheImport = NULL;
IBackgroundCopyJob *pChildJob = NULL;
CString sDisplayName;
CString sManifestStagingDir;
CString sManifestFilePath, sManifestPatchFilePath;
LPASSEMBLY_IDENTITY pPatchAssemblyId = NULL;
BOOL fAdditionalDependencies = FALSE;
CAssemblyCache::GetCacheRootDir(sManifestStagingDir, CAssemblyCache::Staging);
// Commit files to disk
pJob->Complete();
// Get the file enumerator.
pJob->EnumFiles(&pEnumFiles);
pEnumFiles->GetCount(&nCount);
for (DWORD i = 0; i < nCount; i++)
{
CString sLocalName(CString::COM_Allocator);
pEnumFiles->Next(1, &pFile, NULL);
pFile->GetLocalName(&pwz);
sLocalName.TakeOwnership(pwz);
// This is somewhat hacky - we rely on the local target path
// returned from BITS to figure out if a manifest file.
if (sLocalName.PathPrefixMatch(sManifestStagingDir._pwz) == S_OK)
{
// First thing we need to do is figure out if this
// is a subscription manifest which we have to indirect
// through
LPASSEMBLY_MANIFEST_IMPORT pManifestImport = NULL;
LPDEPENDENT_ASSEMBLY_INFO pDependAsmInfo = NULL;
CString sManifestFileName;
CString sDependantASMCodebase;
DWORD dwManifestType = MANIFEST_TYPE_UNKNOWN;
CreateAssemblyManifestImport(&pManifestImport, sLocalName._pwz);
pManifestImport->ReportManifestType(&dwManifestType);
if (dwManifestType == MANIFEST_TYPE_SUBSCRIPTION)
{
// BUGBUG: the hardcoded index '0'
pManifestImport->GetNextAssembly(0, &pDependAsmInfo);
}
if (pDependAsmInfo)
{
// We know its a subscription
// just transit to the referenced codebase.
// BUGBUG - need to clean up the old manifest in staging dir.
_hr = pDependAsmInfo->Get(DEPENDENT_ASM_CODEBASE, &pwz, &cc);
sDependantASMCodebase.TakeOwnership(pwz, cc);
if (sDependantASMCodebase._pwz)
{
LPASSEMBLY_IDENTITY pAsmId = NULL;
// subscription's dependent asm's id == app's asm id (fully qualified)
if ((_hr = pDependAsmInfo->GetAssemblyIdentity(&pAsmId)) == S_OK)
{
CString sName;
_hr = pAsmId->GetAttribute(SXS_ASSEMBLY_IDENTITY_STD_ATTRIBUTE_NAME_NAME, &pwz, &cc);
sName.TakeOwnership(pwz, cc);
// _hr from above GetAttribute
if (_hr == S_OK)
{
IAssemblyUpdate *pAssemblyUpdate = NULL;
// register for updates
if (SUCCEEDED(_hr = CoCreateInstance(CLSID_CAssemblyUpdate, NULL, CLSCTX_LOCAL_SERVER,
IID_IAssemblyUpdate, (void**)&pAssemblyUpdate)))
{
CString sRemoteName(CString::COM_Allocator);
LPWSTR pwzSubscriptionManifestCodebase = NULL;
DWORD pollingInterval;
pFile->GetRemoteName(&pwzSubscriptionManifestCodebase);
sRemoteName.TakeOwnership(pwzSubscriptionManifestCodebase);
// Get subscription polling interval from manifest
_hr = pManifestImport->GetPollingInterval (&pollingInterval);
_hr = pAssemblyUpdate->RegisterAssemblySubscription(sName._pwz,
sRemoteName._pwz, pollingInterval);
SAFERELEASE(pAssemblyUpdate);
}
// else
// Error in update services. Cannot register subscription for updates - fail gracefully
// BUGBUG: need a way to recover from this and register later
}
// else
// Error in retrieving assembly name. Cannot register subscription for updates - fail gracefully
// BUGBUG: This should not be allowed!
// check if this download is necessary
// download only if not in cache
if ((_hr=CreateAssemblyCacheImport(&pCacheImport, pAsmId, CACHEIMP_CREATE_RETRIEVE_EXIST)) == S_FALSE)
{
if (_pDlg)
{
_pDlg->InitDialog(_pDlg->_hwndDlg);
_pDlg->SetDlgState(DOWNLOADDLG_STATE_GETTING_APP_MANIFEST);
}
_hr = DownloadManifestAndDependencies(sDependantASMCodebase._pwz, NULL, DOWNLOAD_FLAGS_INTERNAL_TRAVERSE_LINK);
}
// else
// assume it's being handled or it's done
SAFERELEASE(pCacheImport);
}
// BUGBUG: should check file integrity
SAFERELEASE(pAsmId);
}
else
// redirected to a manifest which has no dependentassembly/codebase
_hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
SAFERELEASE(pDependAsmInfo);
// We're done with the subscription manifest now
// and can release the interface and delete it from the manifest staging dir.
SAFERELEASE(pManifestImport);
::DeleteFile(sLocalName._pwz);
goto exit;
}
SAFERELEASE(pManifestImport);
// Not a subscription manifest - pull down dependencies.
fAdditionalDependencies = TRUE;
// Generate the cache entry (assemblydir/manifest/<dirs>)
// First callbac, _pRootEmit = NULL;
CreateAssemblyCacheEmit(&pEmit, _pRootEmit, 0);
// Generate manifest file codebase directory
// used for enqueuing parsed dependencies.
CString sCodebase(CString::COM_Allocator);
pFile->GetRemoteName(&pwz);
sCodebase.TakeOwnership(pwz);
sCodebase.LastElement(sManifestFileName);
sCodebase.RemoveLastElement();
sCodebase.Append(L"/");
// Create the cache entry.
// (x86_foo_1.0.0.0_en-us/foo.manifest/<+extra dirs>)
pEmit->CopyFile(sLocalName._pwz, sManifestFileName._pwz, MANIFEST);
// If this is first cache entry created, save as root.
if (!_pRootEmit)
{
_pRootEmit = pEmit;
_pRootEmit->AddRef();
}
// QI for the import interface.
pEmit->QueryInterface(IID_IAssemblyCacheImport, (LPVOID*) &pCacheImport);
// First time through loop get the display name
if (!i)
{
pCacheImport->GetDisplayName(&pwz, &cc);
sDisplayName.TakeOwnership(pwz, cc);
}
// Line up it's dependencies for download and fire them off.
// We pass the cache import interface which provides the
// manifest enumeration. We could just as easily passed a manifest
// interface but already have one in the pCacheImport
EnqueueDependencies(pCacheImport, sCodebase, sDisplayName, &pChildJob);
SAFERELEASE(pEmit);
SAFERELEASE(pCacheImport);
}
// if file was a patch file, find the source and target, apply patch to source and move result to target
// or if file was compressed, uncompress file
else
{
// Grab mainfest file directory and append on PATCH_DIRECTORY
// "C:\Program Files\Application Store\x86_foo_X.X.X.X\PATCH_DIRECTORY\"
_pRootEmit->GetManifestFileDir(&pwz, &cc);
sManifestFilePath.TakeOwnership(pwz, cc);
sManifestPatchFilePath.Assign(sManifestFilePath);
sManifestPatchFilePath.Append(PATCH_DIRECTORY);
// if local file begins with the manifests patch direcotry, file is a patch file
if (sLocalName.PathPrefixMatch(sManifestPatchFilePath._pwz) == S_OK)
{
CString sPatchDisplayName;
// init pPatchAssemblyId only once
if (!pPatchAssemblyId)
{
_hr = GetPatchDisplayNameFromFilePath (sLocalName, sPatchDisplayName);
CreateAssemblyIdentityEx(&pPatchAssemblyId, 0, sPatchDisplayName._pwz);
}
// Check to see if file is a cab file (have to revamp IsCABbed to handle this by passing in the AssemblyId)
// If it is CABbed, then have to call the FDI functions and pass in base directory (should have relative paths in
// the cab .ddf file (done by tool).
// using the patch file path, step through the manifest to to find
// the source and target files associated with the patch file and
// apply the patch file to the the source file to create the target file.
_hr = ApplyPatchFile (pPatchAssemblyId, sLocalName._pwz);
}
}
SAFERELEASE(pFile);
}
// if patched, delete patch directory
if (pPatchAssemblyId)
_hr = RemoveDirectoryAndChildren(sManifestPatchFilePath._pwz);
SAFERELEASE(pEnumFiles);
// Submit the job.
if (pChildJob)
{
if (_pDlg)
{
_pDlg->InitDialog(_pDlg->_hwndDlg);
_pDlg->SetDlgState(DOWNLOADDLG_STATE_GETTING_OTHER_FILES);
_pDlg->SetJob(pChildJob);
}
pChildJob->Resume();
SAFERELEASE(pChildJob);
}
// If no additional jobs
if (!fAdditionalDependencies)
{
// If we have a dialog then DoFinish will commit bits.
// BUGBUG - formalize done semantics
_pRootEmit->Commit(0);
if (_hNamedEvent)
SetEvent(_hNamedEvent);
_pDlg->SetDlgState(DOWNLOADDLG_STATE_ALL_DONE);
}
exit:
SAFERELEASE(pPatchAssemblyId);
return S_OK;
}
// ---------------------------------------------------------------------------
//GetPatchDisplayNameFromFilePath
// ---------------------------------------------------------------------------
HRESULT CAssemblyDownload::GetPatchDisplayNameFromFilePath ( CString &sPatchFilePath, CString &sDisplayName)
{
CString pwzFilePath;
LPWSTR pwzStart, pwzEnd;
pwzFilePath.Assign(sPatchFilePath);
// Search file path for the PATCH_DIRECTORY
pwzStart = StrStr(pwzFilePath._pwz, PATCH_DIRECTORY);
// Set start pointer the one directory below the PATCH_DIRECTORY
// This is the Beginning of the Patch DisplayNameb
pwzStart = StrChr(pwzStart, L'\\');
pwzStart++;
// Set end pointer to the end of the Patch DisplayName and null out the character
pwzEnd = StrChr(pwzStart, L'\\');
(*pwzEnd) = L'\0';
sDisplayName.Assign(pwzStart);
return S_OK;
}
// ---------------------------------------------------------------------------
//ApplyPatchFile
// ---------------------------------------------------------------------------
HRESULT CAssemblyDownload::ApplyPatchFile ( LPASSEMBLY_IDENTITY pPatchAssemblyId, LPWSTR pwzPatchFilePath)
{
int i = 0;
LPWSTR pwzSource = NULL, pwzTarget = NULL;
LPWSTR pwzBuf;
DWORD ccBuf;
CString sPatchLocalName;
CString sPatchDisplayName;
CString sManifestDir, sPatchManifestDir;
CString sSourcePath, sTargetPath, sPatchPath;
IAssemblyManifestImport *pManifestImport = NULL;
LPASSEMBLY_IDENTITY pTempPatchAssemblyId = NULL;
LPASSEMBLY_CACHE_IMPORT pPatchImport = NULL;
// Get DisplayName of the "Patch From" Assembly
_hr = pPatchAssemblyId->GetDisplayName(ASMID_DISPLAYNAME_NOMANGLING, &pwzBuf, &ccBuf);
sPatchDisplayName.TakeOwnership(pwzBuf, ccBuf);
//Parse out the local file path from the full file path of the patch file
pwzBuf= StrStr (pwzPatchFilePath, sPatchDisplayName._pwz);
pwzBuf = StrChr(pwzBuf, L'\\');
//Following commented out code needed for NULL patching
/*
// Take care of patching from "itself"
if (StrStr (pwzPatchFilePath, sPatchDisplayName._pwz) != NULL)
{
pwzBuf= StrStr (pwzBuf, sPatchDisplayName._pwz);
pwzBuf = StrChr(pwzBuf, L'\\');
}
*/
pwzBuf++;
sPatchLocalName.Assign(pwzBuf);
_pRootEmit->GetManifestImport(&pManifestImport);
_pRootEmit->GetManifestFileDir(&pwzBuf, &ccBuf);
sManifestDir.TakeOwnership (pwzBuf, ccBuf);
// set up the patchAssemblyNode in the manifestimport
while ((_hr = pManifestImport->GetNextPatchAssemblyId (i, &pTempPatchAssemblyId)) == S_OK)
{
if(FAILED(_hr =pPatchAssemblyId->IsEqual(pTempPatchAssemblyId)))
goto exit;
else if (_hr == S_OK)
{
_hr = pManifestImport->SetPatchAssemblyNode(i);
// get the cacheImport for the "patch from" assembly
if ((_hr = CreateAssemblyCacheImport(&pPatchImport, pPatchAssemblyId, CACHEIMP_CREATE_RETRIEVE_EXIST_COMPLETED))!= S_OK)
goto exit;
break;
}
i++;
SAFERELEASE(pTempPatchAssemblyId);
}
// there has to be a matching patchassembly node.
if (_hr != S_OK)
goto exit;
pPatchImport->GetManifestFileDir(&pwzBuf, &ccBuf);
sPatchManifestDir.TakeOwnership (pwzBuf, ccBuf);
if((_hr = pManifestImport->GetPatchFilePatchMapping(sPatchLocalName._pwz, &pwzSource, &pwzTarget)) != S_OK)
goto exit;
// Set up paths of source, target and patch files
// set up Source path
// If NULL patching, must add code to call sSourcePath.FreeBuffer is pwzSource is NULL
/*
sSourcePath.Assign(sManifestDir);
sSourcePath.Append(PATCH_DIRECTORY);
sSourcePath.Append(sPatchDisplayName);
sSourcePath.Append(L"\\");
*/
sSourcePath.Append(sPatchManifestDir);
sSourcePath.Append(pwzSource);
// set up Target path
sTargetPath.Assign(sManifestDir);
sTargetPath.Append(pwzTarget);
// set up Patch path
sPatchPath.Assign(pwzPatchFilePath);
//Apply patchfile to sSource (grab from patch directory) and copy to path specified by sTarget
if (!(ApplyPatchToFile((LPCWSTR)sPatchPath._pwz, (LPCWSTR)sSourcePath._pwz, (LPCWSTR)sTargetPath._pwz, 0)))
_hr = E_FAIL;
goto exit;
exit:
SAFEDELETEARRAY(pwzSource);
SAFEDELETEARRAY(pwzTarget);
SAFERELEASE(pTempPatchAssemblyId);
SAFERELEASE(pManifestImport);
SAFERELEASE(pPatchImport);
return _hr;
}
// ---------------------------------------------------------------------------
// EnqueueDependencies
// ---------------------------------------------------------------------------
HRESULT CAssemblyDownload::EnqueueDependencies(LPASSEMBLY_CACHE_IMPORT
pCacheImport, CString &sCodebase, CString &sDisplayName, IBackgroundCopyJob **ppJob)
{
LPWSTR pwzBuf = NULL;
LPWSTR pwzPatchFile, pwzSource;
DWORD ccBuf = 0;
DWORD n = 0, i=0;
BOOL patchAvailable = FALSE;
BOOL CABbed = FALSE;
CString sLocalPatchDirectoryPath, sPatchAssemblyDisplayName, sPatchManifestDirectory;
CString sManifestDirectory;
GUID guid = {0};
IBackgroundCopyJob *pJob = NULL;
IAssemblyManifestImport *pManifestImport = NULL;
IAssemblyIdentity *pIdentity = NULL;
IAssemblyFileInfo *pAssemblyFile = NULL;
LPDEPENDENT_ASSEMBLY_INFO pDependAsm = NULL;
LPASSEMBLY_CACHE_EMIT pCacheEmit = NULL;
LPASSEMBLY_CACHE_IMPORT pMaxCachedImport = NULL;
LPASSEMBLY_CACHE_IMPORT pMaxPatchImport = NULL;
LPASSEMBLY_IDENTITY pPatchAssemblyId = NULL;
// Create a new job
if (*ppJob)
pJob = *ppJob;
else
{
g_pManager->CreateJob(sDisplayName._pwz, BG_JOB_TYPE_DOWNLOAD, &guid, &pJob);
pJob->SetNotifyInterface(static_cast<IBackgroundCopyCallback*> (this));
_hr = pJob->SetNotifyFlags(BG_NOTIFY_JOB_MODIFICATION | BG_NOTIFY_JOB_TRANSFERRED | BG_NOTIFY_JOB_ERROR);
_hr = pJob->SetPriority(BG_JOB_PRIORITY_FOREGROUND);
}
// Get the cache import's manifest interface
pCacheImport->GetManifestImport(&pManifestImport);
// Get the asm Id
_hr = pManifestImport->GetAssemblyIdentity(&pIdentity);
// 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
_hr = CreateAssemblyCacheImport(&pMaxCachedImport, pIdentity, CACHEIMP_CREATE_RETRIEVE_MAX_COMPLETED);
// Check to see if there is a suitable upgradable version to patch from already in cache
while (pManifestImport->GetNextPatchAssemblyId (i, &pPatchAssemblyId) == S_OK)
{
if (FAILED(_hr = CreateAssemblyCacheImport(&pMaxPatchImport, pPatchAssemblyId, CACHEIMP_CREATE_RETRIEVE_EXIST_COMPLETED)))
goto exit;
if (_hr == S_FALSE)
{
if(FAILED(_hr =pPatchAssemblyId->IsEqual(pIdentity)))
goto exit;
else if (_hr == S_OK)
{
pMaxPatchImport = pCacheImport;
pMaxPatchImport->AddRef();
}
}
if (_hr == S_OK)
{
// Set the patch assembly node
pManifestImport->SetPatchAssemblyNode(i);
// grab the manifest directory
pCacheImport->GetManifestFileDir(&pwzBuf, &ccBuf);
sManifestDirectory.TakeOwnership(pwzBuf, ccBuf);
// get the manifest directory of the "patch from" directory
pMaxPatchImport->GetManifestFileDir(&pwzBuf, &ccBuf);
sPatchManifestDirectory.TakeOwnership(pwzBuf, ccBuf);
//get display name of patch assembly identity
_hr = (pPatchAssemblyId->GetDisplayName(ASMID_DISPLAYNAME_NOMANGLING, &pwzBuf, &ccBuf));
sPatchAssemblyDisplayName.TakeOwnership (pwzBuf, ccBuf);
//get the local path of the "patch to" directory
sLocalPatchDirectoryPath.Assign(PATCH_DIRECTORY);
sLocalPatchDirectoryPath.PathCombine(sPatchAssemblyDisplayName);
sLocalPatchDirectoryPath.Append(L"\\");
sLocalPatchDirectoryPath.PathNormalize();
//create the patch directory
CAssemblyCache::CreateDirectoryHierarchy(sManifestDirectory._pwz, sLocalPatchDirectoryPath._pwz);
patchAvailable = TRUE;
break;
}
i++;
// Release pPatchAssemblyId every time through the loop
// If the break is executed, the last pPatchAssemblyId is Released at end of the function
SAFERELEASE (pPatchAssemblyId);
}
SAFERELEASE(pIdentity);
// Lazy init. QI for the emit interface.
if (!pCacheEmit)
_hr = pCacheImport->QueryInterface(IID_IAssemblyCacheEmit, (LPVOID*) &pCacheEmit);
if (!pCacheEmit)
{
_hr = E_FAIL;
goto exit;
}
//Check to see if files are contained in a CAB. If so, entire pjob is the on cab file.
if (patchAvailable)
{
LPWSTR pwzCabName;
if ((_hr =pManifestImport->IsCABbed(&pwzCabName)) == S_OK)
{
n=0;
CString sFileName;
CString sLocalFilePath;
CString sRemoteUrl;
sFileName.TakeOwnership(pwzCabName);
// Form local file path
pCacheImport->GetManifestFileDir(&pwzBuf, &ccBuf);
sLocalFilePath.TakeOwnership(pwzBuf, ccBuf);
// Combine and ensure backslashes.
sLocalFilePath.PathCombine(sFileName);
sLocalFilePath.PathNormalize();
// Form remote name
sRemoteUrl.Assign(sCodebase);
sRemoteUrl.UrlCombine(sFileName);
// add the file to the job.
pJob->AddFile(sRemoteUrl._pwz, sLocalFilePath._pwz);
CABbed = TRUE;
}
}
// Submit files directly into their target dirs.
while ((!CABbed) && (pManifestImport->GetNextFile(n++, &pAssemblyFile) == S_OK))
{
CString sFileName;
CString sLocalFilePath;
CString sRemoteUrl;
BOOL bSkipFile = FALSE;
// File name parsed from manifest.
pAssemblyFile->Get(ASM_FILE_NAME, &pwzBuf, &ccBuf);
sFileName.TakeOwnership(pwzBuf, ccBuf);
// Check against the max committed version
if (pMaxCachedImport)
{
LPWSTR pwzPath = NULL;
if ((_hr = pMaxCachedImport->FindExistMatching(pAssemblyFile, &pwzPath)) == S_OK)
{
// Copy from existing cached copy to the new location
// (Non-manifest files)
if (SUCCEEDED(_hr = pCacheEmit->CopyFile(pwzPath, sFileName._pwz, OTHERFILES)))
bSkipFile = TRUE;
SAFEDELETEARRAY(pwzPath);
}
}
if (!bSkipFile)
{
// Form local file path...
// Manifest cache directory
pCacheImport->GetManifestFileDir(&pwzBuf, &ccBuf);
sLocalFilePath.TakeOwnership(pwzBuf, ccBuf);
if (patchAvailable)
{
if(FAILED(_hr = pManifestImport->GetTargetPatchMapping(sFileName._pwz, &pwzSource, &pwzPatchFile)))
goto exit;
else if (_hr == S_OK)
{
CString sLocalPatchDirFileName, sOrigFileName;
// Set up path of source file to be copied
sOrigFileName.Assign(sPatchManifestDirectory);
sOrigFileName.PathCombine(pwzSource);
// Set up local path of where the source file will be copied to.
sLocalPatchDirFileName.Assign(sLocalPatchDirectoryPath);
sLocalPatchDirFileName.PathCombine(pwzSource);
// Copy Source File into path directory
// _hr = pCacheEmit->CopyFile(sOrigFileName._pwz, sLocalPatchDirFileName._pwz, OTHERFILES);
sFileName.Assign (pwzPatchFile);
// Append the patch directory to local file path
sLocalFilePath.Append(sLocalPatchDirectoryPath);
CAssemblyCache::CreateDirectoryHierarchy(sLocalFilePath._pwz, sFileName._pwz);
SAFEDELETEARRAY(pwzSource);
SAFEDELETEARRAY(pwzPatchFile);
}
}
// Form local file path continued from above if statement
// Combine and ensure backslashes.
sLocalFilePath.PathCombine(sFileName);
sLocalFilePath.PathNormalize();
// Form remote name
sRemoteUrl.Assign(sCodebase);
sRemoteUrl.UrlCombine(sFileName);
// add the file to the job.
pJob->AddFile(sRemoteUrl._pwz, sLocalFilePath._pwz);
}
SAFERELEASE(pAssemblyFile);
}
// 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 (pManifestImport->GetNextAssembly(n, &pDependAsm) == S_OK)
{
CString sAssemblyName;
CString sLocalFilePath;
CString sRemoteUrl;
// Form local name (in staging area)....
pDependAsm->GetAssemblyIdentity(&pIdentity);
// Get the identity name
pIdentity->GetAttribute(SXS_ASSEMBLY_IDENTITY_STD_ATTRIBUTE_NAME_NAME,
&pwzBuf, &ccBuf);
sAssemblyName.TakeOwnership(pwzBuf, ccBuf);
// Form local cache path from identity name.
// BUG?
CAssemblyCache::GetCacheRootDir(sLocalFilePath, CAssemblyCache::Staging);
sLocalFilePath.Append(sAssemblyName);
sLocalFilePath.Append(L".manifest");
MakeSequentialFileName(sLocalFilePath);
// Get remote name, if any specified
pDependAsm->Get(DEPENDENT_ASM_CODEBASE, &pwzBuf, &ccBuf);
if (pwzBuf != NULL)
sRemoteUrl.TakeOwnership(pwzBuf, ccBuf);
else
{
// Form remote name - probing, in effect.
sRemoteUrl.Assign(sCodebase);
sRemoteUrl.UrlCombine(sAssemblyName);
sRemoteUrl.Append(L"/");
sRemoteUrl.Append(sAssemblyName);
sRemoteUrl.Append(L".manifest");
}
pJob->AddFile(sRemoteUrl._pwz, sLocalFilePath._pwz);
SAFERELEASE(pIdentity);
SAFERELEASE(pDependAsm);
n++;
}
*ppJob = pJob;
_hr = S_OK;
exit:
SAFERELEASE(pManifestImport);
SAFERELEASE(pCacheEmit);
SAFERELEASE(pMaxCachedImport);
SAFERELEASE(pPatchAssemblyId);
SAFERELEASE(pMaxPatchImport );
return _hr;
}
// IBackgroundCopyCallback methods
// ---------------------------------------------------------------------------
// JobTransferred
// ---------------------------------------------------------------------------
HRESULT CAssemblyDownload::JobTransferred(IBackgroundCopyJob *pJob)
{
if (_pDlg)
_pDlg->HandleCOMCallback(pJob, TRUE);
_hr = DoCacheUpdate(pJob);
return _hr;
}
// ---------------------------------------------------------------------------
// JobError
// ---------------------------------------------------------------------------
HRESULT CAssemblyDownload::JobError(IBackgroundCopyJob *pJob, IBackgroundCopyError *pError)
{
LPWSTR pwstr = NULL;
pError->GetErrorDescription(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), &pwstr);
// BUGBUG - need to do CoTaskMemFree on pwstr (or use a cstring)
// _pDlg->HandleCOMCallback(pJob, TRUE);
return S_OK;
}
// ---------------------------------------------------------------------------
// JobModification
// ---------------------------------------------------------------------------
HRESULT CAssemblyDownload::JobModification(IBackgroundCopyJob *pJob, DWORD dwReserved)
{
if (_pDlg)
_pDlg->HandleCOMCallback(pJob, TRUE);
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;
}