#include #include #include #include "dialog.h" #include #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 (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/) // 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 (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 (this); AddRef(); return S_OK; } else if (IsEqualIID(riid, IID_IBackgroundCopyCallback)) { *ppvObj = static_cast (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; }