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
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;
|
|
}
|
|
|