#include #include #include // 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 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 (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 (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; }