Leaked source code of windows server 2003
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.
 
 
 
 
 
 

704 lines
25 KiB

#include <fusenetincludes.h>
#include <sxsapi.h>
#include <versionmanagement.h>
// note: this class should potentially reside in fusenet.dll or server.exe...
// text for uninstall subkey
const WCHAR* pwzUninstallSubKey = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
// Update services
#include "server.h"
DEFINE_GUID(IID_IAssemblyUpdate,
0x301b3415,0xf52d,0x4d40,0xbd,0xf7,0x31,0xd8,0x27,0x12,0xc2,0xdc);
DEFINE_GUID(CLSID_CAssemblyUpdate,
0x37b088b8,0x70ef,0x4ecf,0xb1,0x1e,0x1f,0x3f,0x4d,0x10,0x5f,0xdd);
// copied from fusion.h
//#include <fusion.h>
DEFINE_GUID(FUSION_REFCOUNT_OPAQUE_STRING_GUID, 0x2ec93463, 0xb0c3, 0x45e1, 0x83, 0x64, 0x32, 0x7e, 0x96, 0xae, 0xa8, 0x56);
// ---------------------------------------------------------------------------
// CreateVersionManagement
// ---------------------------------------------------------------------------
STDAPI
CreateVersionManagement(
LPVERSION_MANAGEMENT *ppVersionManagement,
DWORD dwFlags)
{
HRESULT hr = S_OK;
MAKE_ERROR_MACROS_STATIC(hr);
CVersionManagement *pVerMan = NULL;
IF_ALLOC_FAILED_EXIT(pVerMan = new(CVersionManagement));
exit:
*ppVersionManagement = pVerMan;//static_cast<IVersionManagement*> (pVerMan);
return hr;
}
// ---------------------------------------------------------------------------
// ctor
// ---------------------------------------------------------------------------
CVersionManagement::CVersionManagement()
: _dwSig('namv'), _cRef(1), _hr(S_OK), _pFusionAsmCache(NULL)
{}
// ---------------------------------------------------------------------------
// dtor
// ---------------------------------------------------------------------------
CVersionManagement::~CVersionManagement()
{
SAFERELEASE(_pFusionAsmCache);
}
// BUGBUG: look for the Open verb and its command string in the registry and execute that instead
// rundll32.exe should be in c:\windows\system32
// BUGBUG: security hole with CreateProcess- consider using full path with ""
#define WZ_RUNDLL32_STRING L"rundll32.exe \"" // note ending space
#define WZ_FNSSHELL_STRING L"adfshell.dll"
#define WZ_UNINSTALL_STRING L"\",Uninstall \""//%s\" \"%s\""
#define WZ_ROLLBACK_STRING L"\",DisableCurrentVersion \""//%s\""
// ---------------------------------------------------------------------------
// CVersionManagement::RegisterInstall
//
// pwzDesktopManifestFilePath can be NULL
// ---------------------------------------------------------------------------
HRESULT CVersionManagement::RegisterInstall(LPASSEMBLY_MANIFEST_IMPORT pManImport, LPCWSTR pwzDesktopManifestFilePath)
{
// take a man import, create registry uninstall info only if necessary
// note: may need registry HKLM write access
HKEY hkey = NULL;
HKEY hkeyApp = NULL;
LONG lReturn = 0;
DWORD dwDisposition = 0;
DWORD dwManifestType = MANIFEST_TYPE_UNKNOWN;
LPASSEMBLY_IDENTITY pAsmId = NULL;
LPASSEMBLY_IDENTITY pAsmIdMask = NULL;
LPMANIFEST_INFO pAppInfo = NULL;
LPWSTR pwz = NULL;
LPWSTR pwzString = NULL;
DWORD ccString = 0;
DWORD dwCount = 0;
DWORD dwFlag = 0;
LPWSTR pwzFnsshellFilePath = NULL;
CString sDisplayName;
CString sDisplayVersion;
CString sUninstallString;
CString sModifyPath;
IF_NULL_EXIT(pManImport, E_INVALIDARG);
// get the manifest type
pManImport->ReportManifestType(&dwManifestType);
// has to be an application manifest in order to get the exact version number of the app which has just installed
IF_FALSE_EXIT(dwManifestType == MANIFEST_TYPE_APPLICATION, E_INVALIDARG);
IF_FAILED_EXIT(pManImport->GetAssemblyIdentity(&pAsmId));
IF_FAILED_EXIT(CloneAssemblyIdentity(pAsmId, &pAsmIdMask));
IF_FAILED_EXIT(pAsmIdMask->GetAttribute(SXS_ASSEMBLY_IDENTITY_STD_ATTRIBUTE_NAME_VERSION, &pwzString, &ccString));
// assume Version == major.minor.build.rev
// NTRAID#NTBUG9-588036-2002/03/27-felixybc version string validation needed, should not allow "major"
pwz = wcschr(pwzString, L'.');
if (pwz == NULL || *(pwz+1) == L'\0')
{
// if "major" || "major." -> append "*"
// check overflow
IF_FALSE_EXIT(ccString+1 > ccString, HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW));
pwz = new WCHAR[ccString+1];
IF_ALLOC_FAILED_EXIT(pwz);
memcpy(pwz, pwzString, ccString * sizeof(WCHAR));
*(pwz+ccString-1) = L'*';
*(pwz+ccString) = L'\0';
delete [] pwzString;
pwzString = pwz;
}
else
{
*(pwz+1) = L'*';
*(pwz+2) = L'\0';
}
IF_FAILED_EXIT(sDisplayVersion.TakeOwnership(pwzString));
pwzString = NULL;
// set Version major.minor.build.rev to be major.wildcard
IF_FAILED_EXIT(pAsmIdMask->SetAttribute(SXS_ASSEMBLY_IDENTITY_STD_ATTRIBUTE_NAME_VERSION,
sDisplayVersion._pwz, sDisplayVersion._cc));
// get displayname as is
IF_FAILED_EXIT(pAsmIdMask->GetDisplayName(ASMID_DISPLAYNAME_NOMANGLING, &pwzString, &ccString));
IF_FAILED_EXIT(sDisplayName.TakeOwnership(pwzString, ccString));
pwzString = NULL;
// open uninstall key
lReturn = RegOpenKeyEx(HKEY_LOCAL_MACHINE, pwzUninstallSubKey, 0,
KEY_CREATE_SUB_KEY | DELETE, &hkey);
IF_WIN32_FAILED_EXIT(lReturn);
lReturn = RegCreateKeyEx(hkey, sDisplayName._pwz, 0, NULL, REG_OPTION_NON_VOLATILE,
KEY_SET_VALUE, NULL, &hkeyApp, &dwDisposition);
IF_WIN32_FAILED_EXIT(lReturn);
// check if already exists
IF_TRUE_EXIT(dwDisposition == REG_OPENED_EXISTING_KEY, S_FALSE); // already there, nothing to do
// get path to adfshell.dll
// BUGBUG: current process must have adfshell.dll loaded
HMODULE hFnsshell = NULL;
// assume adfshell.dll is never freed, thus HMODULE always valid
hFnsshell = GetModuleHandle(WZ_FNSSHELL_STRING);
IF_WIN32_FALSE_EXIT((hFnsshell != NULL));
IF_ALLOC_FAILED_EXIT(pwzFnsshellFilePath = new WCHAR[MAX_PATH]);
IF_WIN32_FALSE_EXIT(GetModuleFileName(hFnsshell, pwzFnsshellFilePath, MAX_PATH));
// "UninstallString"="rundll32.exe adfshell.dll,Uninstall \"x86_microsoft.webapps.msn6_EAED21A64CF3CD39_6.*_en\" \"C:\\Documents and Settings\\user\\Start Menu\\Programs\\MSN Explorer 6.manifest\""
IF_FAILED_EXIT(sUninstallString.Assign(WZ_RUNDLL32_STRING));
IF_FAILED_EXIT(sUninstallString.Append(pwzFnsshellFilePath));
IF_FAILED_EXIT(sUninstallString.Append(WZ_UNINSTALL_STRING));
IF_FAILED_EXIT(sUninstallString.Append(sDisplayName));
IF_FAILED_EXIT(sUninstallString.Append(L"\" \""));
if (pwzDesktopManifestFilePath != NULL)
{
IF_FAILED_EXIT(sUninstallString.Append((LPWSTR)pwzDesktopManifestFilePath));
}
IF_FAILED_EXIT(sUninstallString.Append(L"\""));
// set UninstallString
lReturn = RegSetValueEx(hkeyApp, L"UninstallString", 0, REG_SZ,
(const BYTE *)sUninstallString._pwz, sUninstallString._cc*sizeof(WCHAR));
IF_WIN32_FAILED_EXIT(lReturn);
// "ModifyPath"="rundll32.exe adfshell.dll,DisableCurrentVersion \"x86_microsoft.webapps.msn6_EAED21A64CF3CD39_6.*_en\""
IF_FAILED_EXIT(sModifyPath.Assign(WZ_RUNDLL32_STRING));
IF_FAILED_EXIT(sModifyPath.Append(pwzFnsshellFilePath));
IF_FAILED_EXIT(sModifyPath.Append(WZ_ROLLBACK_STRING));
IF_FAILED_EXIT(sModifyPath.Append(sDisplayName));
IF_FAILED_EXIT(sModifyPath.Append(L"\""));
// set ModifyPath
lReturn = RegSetValueEx(hkeyApp, L"ModifyPath", 0, REG_SZ,
(const BYTE *)sModifyPath._pwz, sModifyPath._cc*sizeof(WCHAR));
IF_WIN32_FAILED_EXIT(lReturn);
// "DisplayVersion"="6.*"
// set DisplayVersion
lReturn = RegSetValueEx(hkeyApp, L"DisplayVersion", 0, REG_SZ,
(const BYTE *)sDisplayVersion._pwz, sDisplayVersion._cc*sizeof(WCHAR));
IF_WIN32_FAILED_EXIT(lReturn);
// get application info
IF_FAILED_EXIT(pManImport->GetManifestApplicationInfo(&pAppInfo));
IF_FALSE_EXIT(_hr == S_OK, E_FAIL); // can't continue without this...
// "DisplayIcon"="" //full path to icon exe
IF_FAILED_EXIT(pAppInfo->Get(MAN_INFO_APPLICATION_ICONFILE, (LPVOID *)&pwzString, &dwCount, &dwFlag));
if (pwzString != NULL)
{
CString sIconFile;
BOOL bExists = FALSE;
// note: similar code in shell\shortcut\extricon.cpp.
IF_FAILED_EXIT(CheckFileExistence(pwzString, &bExists));
if (!bExists)
{
// if the file specified by iconfile does not exist, try again in working dir
// it can be a relative path...
LPASSEMBLY_CACHE_IMPORT pCacheImport = NULL;
IF_FAILED_EXIT(CreateAssemblyCacheImport(&pCacheImport, pAsmId, CACHEIMP_CREATE_RETRIEVE));
if (_hr == S_OK)
{
LPWSTR pwzWorkingDir = NULL;
// get app root dir
_hr = pCacheImport->GetManifestFileDir(&pwzWorkingDir, &dwCount);
pCacheImport->Release();
IF_FAILED_EXIT(_hr);
_hr = sIconFile.TakeOwnership(pwzWorkingDir, dwCount);
if (SUCCEEDED(_hr))
{
IF_FAILED_EXIT(sIconFile.Append(pwzString)); // pwzWorkingDir ends with '\'
IF_FAILED_EXIT(CheckFileExistence(sIconFile._pwz, &bExists));
if (!bExists)
sIconFile.FreeBuffer();
}
else
{
SAFEDELETEARRAY(pwzWorkingDir);
ASSERT(PREDICATE);
goto exit;
}
}
delete [] pwzString;
pwzString = NULL;
}
else
{
IF_FAILED_EXIT(sIconFile.TakeOwnership(pwzString));
pwzString = NULL;
}
if (sIconFile._cc != 0)
{
// set DisplayIcon
// BUGBUG: should it set DisplayIcon using iconFile?
lReturn = RegSetValueEx(hkeyApp, L"DisplayIcon", 0, REG_SZ,
(const BYTE *)sIconFile._pwz, sIconFile._cc*sizeof(WCHAR));
IF_WIN32_FAILED_EXIT(lReturn);
}
}
// "DisplayName"="MSN Explorer 6"
IF_FAILED_EXIT(pAppInfo->Get(MAN_INFO_APPLICATION_FRIENDLYNAME, (LPVOID *)&pwzString, &dwCount, &dwFlag));
// BUGBUG: should somehow continue even w/o a friendly name? name conflict?
IF_NULL_EXIT(pwzString, E_FAIL);
// set DisplayName ( == Friendly name)
lReturn = RegSetValueEx(hkeyApp, L"DisplayName", 0, REG_SZ,
(const BYTE *)pwzString, dwCount);
IF_WIN32_FAILED_EXIT(lReturn);
_hr = S_OK;
exit:
//delete app key created if failed
if (FAILED(_hr) && (hkeyApp != NULL))
{
lReturn = RegCloseKey(hkeyApp); // check return value?
hkeyApp = NULL;
//ignore return value
lReturn = RegDeleteKey(hkey, sDisplayName._pwz);
}
SAFERELEASE(pAppInfo);
SAFERELEASE(pAsmId);
SAFERELEASE(pAsmIdMask);
SAFEDELETEARRAY(pwzString);
SAFEDELETEARRAY(pwzFnsshellFilePath);
if (hkeyApp)
{
lReturn = RegCloseKey(hkeyApp);
if (SUCCEEDED(_hr))
_hr = (HRESULT_FROM_WIN32(lReturn));
}
if (hkey)
{
lReturn = RegCloseKey(hkey);
if (SUCCEEDED(_hr))
_hr = (HRESULT_FROM_WIN32(lReturn));
}
return _hr;
}
// ---------------------------------------------------------------------------
// CVersionManagement::Uninstall
//
// pwzDesktopManifestFilePath can be NULL or ""
// return: S_FALSE if not found
// ---------------------------------------------------------------------------
HRESULT CVersionManagement::Uninstall(LPCWSTR pwzDisplayNameMask, LPCWSTR pwzDesktopManifestFilePath)
{
// take a displayname mask, enumerate all applicable versions, delete desktop manifest,
// remove subscription, uninstall assemblies from GAC, delete app files/dirs, delete registry uninstall info
// note: need registry HKLM write access
HKEY hkey = NULL;
LONG lReturn = 0;
LPASSEMBLY_IDENTITY pAsmIdMask = NULL;
LPASSEMBLY_CACHE_ENUM pCacheEnum = NULL;
LPASSEMBLY_CACHE_IMPORT pCacheImport = NULL;
LPWSTR pwzName = NULL;
LPWSTR pwzAppDir = NULL;
DWORD dwCount = 0;
IF_NULL_EXIT(pwzDisplayNameMask, E_INVALIDARG);
IF_FALSE_EXIT(pwzDisplayNameMask[0] != L'\0', E_INVALIDARG);
IF_FAILED_EXIT(CreateAssemblyIdentityEx(&pAsmIdMask, 0, (LPWSTR)pwzDisplayNameMask));
// get all applicable versions
IF_FAILED_EXIT(CreateAssemblyCacheEnum(&pCacheEnum, pAsmIdMask, 0));
// found nothing, cannot continue
if (_hr == S_FALSE)
goto exit;
/* pCacheEnum->GetCount(&dwCount);
if (dwCount > 1)
{
// multiple versions.... prompt/UI?
}*/
// delete desktop manifest
if (pwzDesktopManifestFilePath != NULL && pwzDesktopManifestFilePath[0] != L'\0')
IF_WIN32_FALSE_EXIT(DeleteFile(pwzDesktopManifestFilePath));
// remove subscription
IF_FAILED_EXIT(pAsmIdMask->GetAttribute(SXS_ASSEMBLY_IDENTITY_STD_ATTRIBUTE_NAME_NAME, &pwzName, &dwCount));
IF_FALSE_EXIT(_hr == S_OK, E_FAIL);
{
IAssemblyUpdate *pAssemblyUpdate = NULL;
// register for updates
_hr = CoCreateInstance(CLSID_CAssemblyUpdate, NULL, CLSCTX_LOCAL_SERVER,
IID_IAssemblyUpdate, (void**)&pAssemblyUpdate);
if (SUCCEEDED(_hr))
{
_hr = pAssemblyUpdate->UnRegisterAssemblySubscription(pwzName);
pAssemblyUpdate->Release();
}
if (FAILED(_hr)) // _hr from CoCreateInstance or UnRegisterAssemblySubscription
{
// UI?
MessageBox(NULL, L"Error in update services. Cannot unregister update subscription.", L"ClickOnce",
MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
//goto exit; do not terminate!
}
// BUGBUG: need a way to recover from this and unregister later
delete[] pwzName;
}
// uninstall assemblies from GAC and
// delete app files/dirs
while (TRUE)
{
IF_FAILED_EXIT(pCacheEnum->GetNext(&pCacheImport));
if (_hr == S_FALSE)
break;
IF_NULL_EXIT(pCacheImport, E_UNEXPECTED); // cacheimport cannot be created (app dir may have been deleted)
IF_FAILED_EXIT(UninstallGACAssemblies(pCacheImport));
IF_FAILED_EXIT(pCacheImport->GetManifestFileDir(&pwzAppDir, &dwCount));
IF_FALSE_EXIT(dwCount >= 2, E_FAIL);
// remove last L'\\'
if (*(pwzAppDir+dwCount-2) == L'\\')
*(pwzAppDir+dwCount-2) = L'\0';
//PathRemoveBackslash(pwzAppDir);
IF_FAILED_EXIT(RemoveDirectoryAndChildren(pwzAppDir));
SAFEDELETEARRAY(pwzAppDir);
SAFERELEASE(pCacheImport);
}
// last step: delete registry uninstall info
// open uninstall key
lReturn = RegOpenKeyEx(HKEY_LOCAL_MACHINE, pwzUninstallSubKey, 0,
DELETE, &hkey);
IF_WIN32_FAILED_EXIT(lReturn);
lReturn = RegDeleteKey(hkey, pwzDisplayNameMask);
IF_WIN32_FAILED_EXIT(lReturn);
_hr = S_OK;
exit:
SAFEDELETEARRAY(pwzAppDir);
SAFERELEASE(pCacheImport);
SAFERELEASE(pCacheEnum);
SAFERELEASE(pAsmIdMask);
if (hkey)
{
lReturn = RegCloseKey(hkey);
if (SUCCEEDED(_hr))
_hr = (HRESULT_FROM_WIN32(lReturn));
}
return _hr;
}
// ---------------------------------------------------------------------------
// CVersionManagement::UninstallGACAssemblies
// ---------------------------------------------------------------------------
HRESULT CVersionManagement::UninstallGACAssemblies(LPASSEMBLY_CACHE_IMPORT pCacheImport)
{
LPASSEMBLY_MANIFEST_IMPORT pManImport = NULL;
LPASSEMBLY_IDENTITY pIdentity = NULL;
LPMANIFEST_INFO pDependAsm = NULL;
LPWSTR pwz = NULL;
DWORD dwCount = 0;
DWORD n = 0, dwFlag = 0;
CString sAppAssemblyId;
IF_FAILED_EXIT(pCacheImport->GetManifestFilePath(&pwz, &dwCount));
// open to read from the application manifest file
IF_FAILED_EXIT(CreateAssemblyManifestImport(&pManImport, pwz, NULL, 0));
SAFEDELETEARRAY(pwz);
// get the app assembly id
IF_FAILED_EXIT(pManImport->GetAssemblyIdentity(&pIdentity));
IF_FAILED_EXIT(pIdentity->GetDisplayName(0, &pwz, &dwCount));
IF_FAILED_EXIT(sAppAssemblyId.TakeOwnership(pwz, dwCount));
pwz = NULL;
SAFERELEASE(pIdentity);
// uninstall all dependent assemblies that are installed to the GAC
while (TRUE)
{
IF_FAILED_EXIT(pManImport->GetNextAssembly(n++, &pDependAsm));
if (_hr == S_FALSE)
break;
IF_FAILED_EXIT(pDependAsm->Get(MAN_INFO_DEPENDENT_ASM_ID, (LPVOID *)&pIdentity, &dwCount, &dwFlag));
IF_NULL_EXIT(pIdentity, E_UNEXPECTED);
IF_FAILED_EXIT(::IsKnownAssembly(pIdentity, KNOWN_TRUSTED_ASSEMBLY));
if (_hr == S_FALSE)
{
// ISSUE-2002/07/12-felixybc This has to be cleaned up to use the same mechanism as the download path
IF_FAILED_EXIT(::IsKnownAssembly(pIdentity, KNOWN_SYSTEM_ASSEMBLY));
}
if (_hr == S_OK)
{
CString sAssemblyName;
FUSION_INSTALL_REFERENCE fiRef = {0};
ULONG ulDisposition = 0;
// avalon assemblies are installed to the GAC
// lazy init
if (_pFusionAsmCache == NULL)
IF_FAILED_EXIT(CreateFusionAssemblyCacheEx(&_pFusionAsmCache));
IF_FAILED_EXIT(pIdentity->GetCLRDisplayName(0, &pwz, &dwCount));
IF_FAILED_EXIT(sAssemblyName.TakeOwnership(pwz, dwCount));
pwz = NULL;
// setup the necessary reference struct
fiRef.cbSize = sizeof(FUSION_INSTALL_REFERENCE);
fiRef.dwFlags = 0;
fiRef.guidScheme = FUSION_REFCOUNT_OPAQUE_STRING_GUID;
fiRef.szIdentifier = sAppAssemblyId._pwz;
fiRef.szNonCannonicalData = NULL;
// remove from GAC
IF_FAILED_EXIT(_pFusionAsmCache->UninstallAssembly(0, sAssemblyName._pwz, &fiRef, &ulDisposition));
// BUGBUG: need to recover from the STILL_IN_USE case
IF_FALSE_EXIT(ulDisposition != IASSEMBLYCACHE_UNINSTALL_DISPOSITION_STILL_IN_USE
&& ulDisposition != IASSEMBLYCACHE_UNINSTALL_DISPOSITION_REFERENCE_NOT_FOUND, E_FAIL);
}
SAFERELEASE(pIdentity);
SAFERELEASE(pDependAsm);
}
exit:
SAFERELEASE(pDependAsm);
SAFERELEASE(pIdentity);
SAFERELEASE(pManImport);
SAFEDELETEARRAY(pwz);
return _hr;
}
// ---------------------------------------------------------------------------
// CVersionManagement::Rollback
// return: S_FALSE if not found, E_ABORT if aborted
// ---------------------------------------------------------------------------
HRESULT CVersionManagement::Rollback(LPCWSTR pwzDisplayNameMask)
{
// take a displayname mask, make the latest version not visible
// note: a per user setting
// rollback does not check integrity of app cached. if only 2 app dirs exist and all app files deleted,
// rollback still reports success
// timing window: depends on the timing of this and check for max version in cache in app start....
HKEY hkey = NULL;
LPASSEMBLY_IDENTITY pAsmIdMask = NULL;
LPASSEMBLY_CACHE_ENUM pCacheEnum = NULL;
LPASSEMBLY_CACHE_IMPORT pCacheImport = NULL;
DWORD dwCount = 0;
CString sRegKeyString;
LPWSTR pwzDisplayName = NULL;
LPWSTR pwzCacheDir = NULL;
LONG lResult = 0;
IF_NULL_EXIT(pwzDisplayNameMask, E_INVALIDARG);
IF_FALSE_EXIT(pwzDisplayNameMask[0] != L'\0', E_INVALIDARG);
IF_FAILED_EXIT(CreateAssemblyIdentityEx(&pAsmIdMask, 0, (LPWSTR)pwzDisplayNameMask));
// get all applicable, visible versions
IF_FAILED_EXIT(CreateAssemblyCacheEnum(&pCacheEnum, pAsmIdMask, CACHEENUM_RETRIEVE_VISIBLE));
// found nothing, cannot continue
if (_hr == S_FALSE)
goto exit;
// count must be >= 1
pCacheEnum->GetCount(&dwCount);
if (dwCount == 1)
{
MessageBox(NULL, L"Only one active version of this application in the system. Use 'Remove' to remove this application and unregister its subscription.",
L"ClickOnce", MB_OK | MB_ICONINFORMATION | MB_TASKMODAL);
_hr = E_ABORT;
goto exit;
}
// multiple versions, count > 1
// prompt/UI? ask confirmation to continue
IF_TRUE_EXIT(MessageBox(NULL,
L"This application has been updated. If it is not working correctly you can disable the current version. Do you want to go back to a previous version of this application?",
L"ClickOnce", MB_YESNO | MB_ICONQUESTION | MB_TASKMODAL) != IDYES, E_ABORT);
// get max cached
// BUGBUG: sort cache enum so that max cached is at index 0, and use that instead
// notenote: a timing window - a version can turn invisible or a new version can complete
// between cache enum (a snapshot) above and CreateAsmCacheImport(RESOLVE_REF) below
IF_FAILED_EXIT(CreateAssemblyCacheImport(&pCacheImport, pAsmIdMask, CACHEIMP_CREATE_RESOLVE_REF));
// MessageBox(NULL, L"Error retrieving cached version. Cannot continue.", L"ClickOnce",
// MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
IF_FALSE_EXIT(_hr == S_OK, E_FAIL);
IF_FAILED_EXIT(pCacheImport->GetManifestFileDir(&pwzCacheDir, &dwCount));
IF_FALSE_EXIT(dwCount >= 2, E_FAIL);
// remove last L'\\'
if (*(pwzCacheDir+dwCount-2) == L'\\')
*(pwzCacheDir+dwCount-2) = L'\0';
// find the name to use from the cache path
pwzDisplayName = wcsrchr(pwzCacheDir, L'\\');
IF_NULL_EXIT(pwzDisplayName, E_FAIL);
// BUGBUG: use CAssemblyCache::SetStatus()
// this has to be the same as how assemblycache does it!
IF_FAILED_EXIT(sRegKeyString.Assign(L"Software\\Microsoft\\Fusion\\Installer\\1.0.0.0\\Cache\\"));
IF_FAILED_EXIT(sRegKeyString.Append(pwzDisplayName));
// create key if not exist, ignore disposition information
lResult = RegCreateKeyEx(HKEY_CURRENT_USER, sRegKeyString._pwz, 0, NULL,
REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hkey, NULL);
IF_WIN32_FAILED_EXIT(lResult);
if (lResult == ERROR_SUCCESS)
{
DWORD dwValue = 0;
// set to 0 to make it not visible so that StartW/host/cache will ignore it
// when executing the app but keep the dir name so that download
// will assume it is handled - assemblycache.cpp & assemblydownload.cpp's check
lResult = RegSetValueEx(hkey, L"Visible", NULL, REG_DWORD,
(PBYTE) &dwValue, sizeof(dwValue));
IF_WIN32_FAILED_EXIT(lResult);
if (lResult == ERROR_SUCCESS)
{
MessageBox(NULL, L"Current version disabled. Next time another version of the application will run instead.", L"ClickOnce",
MB_OK | MB_ICONINFORMATION | MB_TASKMODAL);
}
}
exit:
SAFEDELETEARRAY(pwzCacheDir);
SAFERELEASE(pCacheImport);
SAFERELEASE(pCacheEnum);
SAFERELEASE(pAsmIdMask);
if (hkey)
{
lResult = RegCloseKey(hkey);
if (SUCCEEDED(_hr))
_hr = (HRESULT_FROM_WIN32(lResult));
}
return _hr;
}
// IUnknown methods
// ---------------------------------------------------------------------------
// CVersionManagement::QI
// ---------------------------------------------------------------------------
STDMETHODIMP
CVersionManagement::QueryInterface(REFIID riid, void** ppvObj)
{
if ( IsEqualIID(riid, IID_IUnknown)
// || IsEqualIID(riid, IID_IVersionManagement)
)
{
*ppvObj = this; //static_cast<IVersionManagement*> (this);
AddRef();
return S_OK;
}
else
{
*ppvObj = NULL;
return E_NOINTERFACE;
}
}
// ---------------------------------------------------------------------------
// CVersionManagement::AddRef
// ---------------------------------------------------------------------------
STDMETHODIMP_(ULONG)
CVersionManagement::AddRef()
{
return InterlockedIncrement ((LONG*) &_cRef);
}
// ---------------------------------------------------------------------------
// CVersionManagement::Release
// ---------------------------------------------------------------------------
STDMETHODIMP_(ULONG)
CVersionManagement::Release()
{
ULONG lRet = InterlockedDecrement ((LONG*) &_cRef);
if (!lRet)
delete this;
return lRet;
}