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

  1. #include <fusenetincludes.h>
  2. #include <sxsapi.h>
  3. #include <versionmanagement.h>
  4. // note: this class should potentially reside in fusenet.dll or server.exe...
  5. // text for uninstall subkey
  6. const WCHAR* pwzUninstallSubKey = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
  7. // Update services
  8. #include "server.h"
  9. DEFINE_GUID(IID_IAssemblyUpdate,
  10. 0x301b3415,0xf52d,0x4d40,0xbd,0xf7,0x31,0xd8,0x27,0x12,0xc2,0xdc);
  11. DEFINE_GUID(CLSID_CAssemblyUpdate,
  12. 0x37b088b8,0x70ef,0x4ecf,0xb1,0x1e,0x1f,0x3f,0x4d,0x10,0x5f,0xdd);
  13. // copied from fusion.h
  14. //#include <fusion.h>
  15. DEFINE_GUID(FUSION_REFCOUNT_OPAQUE_STRING_GUID, 0x2ec93463, 0xb0c3, 0x45e1, 0x83, 0x64, 0x32, 0x7e, 0x96, 0xae, 0xa8, 0x56);
  16. // ---------------------------------------------------------------------------
  17. // CreateVersionManagement
  18. // ---------------------------------------------------------------------------
  19. STDAPI
  20. CreateVersionManagement(
  21. LPVERSION_MANAGEMENT *ppVersionManagement,
  22. DWORD dwFlags)
  23. {
  24. HRESULT hr = S_OK;
  25. MAKE_ERROR_MACROS_STATIC(hr);
  26. CVersionManagement *pVerMan = NULL;
  27. IF_ALLOC_FAILED_EXIT(pVerMan = new(CVersionManagement));
  28. exit:
  29. *ppVersionManagement = pVerMan;//static_cast<IVersionManagement*> (pVerMan);
  30. return hr;
  31. }
  32. // ---------------------------------------------------------------------------
  33. // ctor
  34. // ---------------------------------------------------------------------------
  35. CVersionManagement::CVersionManagement()
  36. : _dwSig('namv'), _cRef(1), _hr(S_OK), _pFusionAsmCache(NULL)
  37. {}
  38. // ---------------------------------------------------------------------------
  39. // dtor
  40. // ---------------------------------------------------------------------------
  41. CVersionManagement::~CVersionManagement()
  42. {
  43. SAFERELEASE(_pFusionAsmCache);
  44. }
  45. // BUGBUG: look for the Open verb and its command string in the registry and execute that instead
  46. // rundll32.exe should be in c:\windows\system32
  47. // BUGBUG: security hole with CreateProcess- consider using full path with ""
  48. #define WZ_RUNDLL32_STRING L"rundll32.exe \"" // note ending space
  49. #define WZ_FNSSHELL_STRING L"adfshell.dll"
  50. #define WZ_UNINSTALL_STRING L"\",Uninstall \""//%s\" \"%s\""
  51. #define WZ_ROLLBACK_STRING L"\",DisableCurrentVersion \""//%s\""
  52. // ---------------------------------------------------------------------------
  53. // CVersionManagement::RegisterInstall
  54. //
  55. // pwzDesktopManifestFilePath can be NULL
  56. // ---------------------------------------------------------------------------
  57. HRESULT CVersionManagement::RegisterInstall(LPASSEMBLY_MANIFEST_IMPORT pManImport, LPCWSTR pwzDesktopManifestFilePath)
  58. {
  59. // take a man import, create registry uninstall info only if necessary
  60. // note: may need registry HKLM write access
  61. HKEY hkey = NULL;
  62. HKEY hkeyApp = NULL;
  63. LONG lReturn = 0;
  64. DWORD dwDisposition = 0;
  65. DWORD dwManifestType = MANIFEST_TYPE_UNKNOWN;
  66. LPASSEMBLY_IDENTITY pAsmId = NULL;
  67. LPASSEMBLY_IDENTITY pAsmIdMask = NULL;
  68. LPMANIFEST_INFO pAppInfo = NULL;
  69. LPWSTR pwz = NULL;
  70. LPWSTR pwzString = NULL;
  71. DWORD ccString = 0;
  72. DWORD dwCount = 0;
  73. DWORD dwFlag = 0;
  74. LPWSTR pwzFnsshellFilePath = NULL;
  75. CString sDisplayName;
  76. CString sDisplayVersion;
  77. CString sUninstallString;
  78. CString sModifyPath;
  79. IF_NULL_EXIT(pManImport, E_INVALIDARG);
  80. // get the manifest type
  81. pManImport->ReportManifestType(&dwManifestType);
  82. // has to be an application manifest in order to get the exact version number of the app which has just installed
  83. IF_FALSE_EXIT(dwManifestType == MANIFEST_TYPE_APPLICATION, E_INVALIDARG);
  84. IF_FAILED_EXIT(pManImport->GetAssemblyIdentity(&pAsmId));
  85. IF_FAILED_EXIT(CloneAssemblyIdentity(pAsmId, &pAsmIdMask));
  86. IF_FAILED_EXIT(pAsmIdMask->GetAttribute(SXS_ASSEMBLY_IDENTITY_STD_ATTRIBUTE_NAME_VERSION, &pwzString, &ccString));
  87. // assume Version == major.minor.build.rev
  88. // NTRAID#NTBUG9-588036-2002/03/27-felixybc version string validation needed, should not allow "major"
  89. pwz = wcschr(pwzString, L'.');
  90. if (pwz == NULL || *(pwz+1) == L'\0')
  91. {
  92. // if "major" || "major." -> append "*"
  93. // check overflow
  94. IF_FALSE_EXIT(ccString+1 > ccString, HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW));
  95. pwz = new WCHAR[ccString+1];
  96. IF_ALLOC_FAILED_EXIT(pwz);
  97. memcpy(pwz, pwzString, ccString * sizeof(WCHAR));
  98. *(pwz+ccString-1) = L'*';
  99. *(pwz+ccString) = L'\0';
  100. delete [] pwzString;
  101. pwzString = pwz;
  102. }
  103. else
  104. {
  105. *(pwz+1) = L'*';
  106. *(pwz+2) = L'\0';
  107. }
  108. IF_FAILED_EXIT(sDisplayVersion.TakeOwnership(pwzString));
  109. pwzString = NULL;
  110. // set Version major.minor.build.rev to be major.wildcard
  111. IF_FAILED_EXIT(pAsmIdMask->SetAttribute(SXS_ASSEMBLY_IDENTITY_STD_ATTRIBUTE_NAME_VERSION,
  112. sDisplayVersion._pwz, sDisplayVersion._cc));
  113. // get displayname as is
  114. IF_FAILED_EXIT(pAsmIdMask->GetDisplayName(ASMID_DISPLAYNAME_NOMANGLING, &pwzString, &ccString));
  115. IF_FAILED_EXIT(sDisplayName.TakeOwnership(pwzString, ccString));
  116. pwzString = NULL;
  117. // open uninstall key
  118. lReturn = RegOpenKeyEx(HKEY_LOCAL_MACHINE, pwzUninstallSubKey, 0,
  119. KEY_CREATE_SUB_KEY | DELETE, &hkey);
  120. IF_WIN32_FAILED_EXIT(lReturn);
  121. lReturn = RegCreateKeyEx(hkey, sDisplayName._pwz, 0, NULL, REG_OPTION_NON_VOLATILE,
  122. KEY_SET_VALUE, NULL, &hkeyApp, &dwDisposition);
  123. IF_WIN32_FAILED_EXIT(lReturn);
  124. // check if already exists
  125. IF_TRUE_EXIT(dwDisposition == REG_OPENED_EXISTING_KEY, S_FALSE); // already there, nothing to do
  126. // get path to adfshell.dll
  127. // BUGBUG: current process must have adfshell.dll loaded
  128. HMODULE hFnsshell = NULL;
  129. // assume adfshell.dll is never freed, thus HMODULE always valid
  130. hFnsshell = GetModuleHandle(WZ_FNSSHELL_STRING);
  131. IF_WIN32_FALSE_EXIT((hFnsshell != NULL));
  132. IF_ALLOC_FAILED_EXIT(pwzFnsshellFilePath = new WCHAR[MAX_PATH]);
  133. IF_WIN32_FALSE_EXIT(GetModuleFileName(hFnsshell, pwzFnsshellFilePath, MAX_PATH));
  134. // "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\""
  135. IF_FAILED_EXIT(sUninstallString.Assign(WZ_RUNDLL32_STRING));
  136. IF_FAILED_EXIT(sUninstallString.Append(pwzFnsshellFilePath));
  137. IF_FAILED_EXIT(sUninstallString.Append(WZ_UNINSTALL_STRING));
  138. IF_FAILED_EXIT(sUninstallString.Append(sDisplayName));
  139. IF_FAILED_EXIT(sUninstallString.Append(L"\" \""));
  140. if (pwzDesktopManifestFilePath != NULL)
  141. {
  142. IF_FAILED_EXIT(sUninstallString.Append((LPWSTR)pwzDesktopManifestFilePath));
  143. }
  144. IF_FAILED_EXIT(sUninstallString.Append(L"\""));
  145. // set UninstallString
  146. lReturn = RegSetValueEx(hkeyApp, L"UninstallString", 0, REG_SZ,
  147. (const BYTE *)sUninstallString._pwz, sUninstallString._cc*sizeof(WCHAR));
  148. IF_WIN32_FAILED_EXIT(lReturn);
  149. // "ModifyPath"="rundll32.exe adfshell.dll,DisableCurrentVersion \"x86_microsoft.webapps.msn6_EAED21A64CF3CD39_6.*_en\""
  150. IF_FAILED_EXIT(sModifyPath.Assign(WZ_RUNDLL32_STRING));
  151. IF_FAILED_EXIT(sModifyPath.Append(pwzFnsshellFilePath));
  152. IF_FAILED_EXIT(sModifyPath.Append(WZ_ROLLBACK_STRING));
  153. IF_FAILED_EXIT(sModifyPath.Append(sDisplayName));
  154. IF_FAILED_EXIT(sModifyPath.Append(L"\""));
  155. // set ModifyPath
  156. lReturn = RegSetValueEx(hkeyApp, L"ModifyPath", 0, REG_SZ,
  157. (const BYTE *)sModifyPath._pwz, sModifyPath._cc*sizeof(WCHAR));
  158. IF_WIN32_FAILED_EXIT(lReturn);
  159. // "DisplayVersion"="6.*"
  160. // set DisplayVersion
  161. lReturn = RegSetValueEx(hkeyApp, L"DisplayVersion", 0, REG_SZ,
  162. (const BYTE *)sDisplayVersion._pwz, sDisplayVersion._cc*sizeof(WCHAR));
  163. IF_WIN32_FAILED_EXIT(lReturn);
  164. // get application info
  165. IF_FAILED_EXIT(pManImport->GetManifestApplicationInfo(&pAppInfo));
  166. IF_FALSE_EXIT(_hr == S_OK, E_FAIL); // can't continue without this...
  167. // "DisplayIcon"="" //full path to icon exe
  168. IF_FAILED_EXIT(pAppInfo->Get(MAN_INFO_APPLICATION_ICONFILE, (LPVOID *)&pwzString, &dwCount, &dwFlag));
  169. if (pwzString != NULL)
  170. {
  171. CString sIconFile;
  172. BOOL bExists = FALSE;
  173. // note: similar code in shell\shortcut\extricon.cpp.
  174. IF_FAILED_EXIT(CheckFileExistence(pwzString, &bExists));
  175. if (!bExists)
  176. {
  177. // if the file specified by iconfile does not exist, try again in working dir
  178. // it can be a relative path...
  179. LPASSEMBLY_CACHE_IMPORT pCacheImport = NULL;
  180. IF_FAILED_EXIT(CreateAssemblyCacheImport(&pCacheImport, pAsmId, CACHEIMP_CREATE_RETRIEVE));
  181. if (_hr == S_OK)
  182. {
  183. LPWSTR pwzWorkingDir = NULL;
  184. // get app root dir
  185. _hr = pCacheImport->GetManifestFileDir(&pwzWorkingDir, &dwCount);
  186. pCacheImport->Release();
  187. IF_FAILED_EXIT(_hr);
  188. _hr = sIconFile.TakeOwnership(pwzWorkingDir, dwCount);
  189. if (SUCCEEDED(_hr))
  190. {
  191. IF_FAILED_EXIT(sIconFile.Append(pwzString)); // pwzWorkingDir ends with '\'
  192. IF_FAILED_EXIT(CheckFileExistence(sIconFile._pwz, &bExists));
  193. if (!bExists)
  194. sIconFile.FreeBuffer();
  195. }
  196. else
  197. {
  198. SAFEDELETEARRAY(pwzWorkingDir);
  199. ASSERT(PREDICATE);
  200. goto exit;
  201. }
  202. }
  203. delete [] pwzString;
  204. pwzString = NULL;
  205. }
  206. else
  207. {
  208. IF_FAILED_EXIT(sIconFile.TakeOwnership(pwzString));
  209. pwzString = NULL;
  210. }
  211. if (sIconFile._cc != 0)
  212. {
  213. // set DisplayIcon
  214. // BUGBUG: should it set DisplayIcon using iconFile?
  215. lReturn = RegSetValueEx(hkeyApp, L"DisplayIcon", 0, REG_SZ,
  216. (const BYTE *)sIconFile._pwz, sIconFile._cc*sizeof(WCHAR));
  217. IF_WIN32_FAILED_EXIT(lReturn);
  218. }
  219. }
  220. // "DisplayName"="MSN Explorer 6"
  221. IF_FAILED_EXIT(pAppInfo->Get(MAN_INFO_APPLICATION_FRIENDLYNAME, (LPVOID *)&pwzString, &dwCount, &dwFlag));
  222. // BUGBUG: should somehow continue even w/o a friendly name? name conflict?
  223. IF_NULL_EXIT(pwzString, E_FAIL);
  224. // set DisplayName ( == Friendly name)
  225. lReturn = RegSetValueEx(hkeyApp, L"DisplayName", 0, REG_SZ,
  226. (const BYTE *)pwzString, dwCount);
  227. IF_WIN32_FAILED_EXIT(lReturn);
  228. _hr = S_OK;
  229. exit:
  230. //delete app key created if failed
  231. if (FAILED(_hr) && (hkeyApp != NULL))
  232. {
  233. lReturn = RegCloseKey(hkeyApp); // check return value?
  234. hkeyApp = NULL;
  235. //ignore return value
  236. lReturn = RegDeleteKey(hkey, sDisplayName._pwz);
  237. }
  238. SAFERELEASE(pAppInfo);
  239. SAFERELEASE(pAsmId);
  240. SAFERELEASE(pAsmIdMask);
  241. SAFEDELETEARRAY(pwzString);
  242. SAFEDELETEARRAY(pwzFnsshellFilePath);
  243. if (hkeyApp)
  244. {
  245. lReturn = RegCloseKey(hkeyApp);
  246. if (SUCCEEDED(_hr))
  247. _hr = (HRESULT_FROM_WIN32(lReturn));
  248. }
  249. if (hkey)
  250. {
  251. lReturn = RegCloseKey(hkey);
  252. if (SUCCEEDED(_hr))
  253. _hr = (HRESULT_FROM_WIN32(lReturn));
  254. }
  255. return _hr;
  256. }
  257. // ---------------------------------------------------------------------------
  258. // CVersionManagement::Uninstall
  259. //
  260. // pwzDesktopManifestFilePath can be NULL or ""
  261. // return: S_FALSE if not found
  262. // ---------------------------------------------------------------------------
  263. HRESULT CVersionManagement::Uninstall(LPCWSTR pwzDisplayNameMask, LPCWSTR pwzDesktopManifestFilePath)
  264. {
  265. // take a displayname mask, enumerate all applicable versions, delete desktop manifest,
  266. // remove subscription, uninstall assemblies from GAC, delete app files/dirs, delete registry uninstall info
  267. // note: need registry HKLM write access
  268. HKEY hkey = NULL;
  269. LONG lReturn = 0;
  270. LPASSEMBLY_IDENTITY pAsmIdMask = NULL;
  271. LPASSEMBLY_CACHE_ENUM pCacheEnum = NULL;
  272. LPASSEMBLY_CACHE_IMPORT pCacheImport = NULL;
  273. LPWSTR pwzName = NULL;
  274. LPWSTR pwzAppDir = NULL;
  275. DWORD dwCount = 0;
  276. IF_NULL_EXIT(pwzDisplayNameMask, E_INVALIDARG);
  277. IF_FALSE_EXIT(pwzDisplayNameMask[0] != L'\0', E_INVALIDARG);
  278. IF_FAILED_EXIT(CreateAssemblyIdentityEx(&pAsmIdMask, 0, (LPWSTR)pwzDisplayNameMask));
  279. // get all applicable versions
  280. IF_FAILED_EXIT(CreateAssemblyCacheEnum(&pCacheEnum, pAsmIdMask, 0));
  281. // found nothing, cannot continue
  282. if (_hr == S_FALSE)
  283. goto exit;
  284. /* pCacheEnum->GetCount(&dwCount);
  285. if (dwCount > 1)
  286. {
  287. // multiple versions.... prompt/UI?
  288. }*/
  289. // delete desktop manifest
  290. if (pwzDesktopManifestFilePath != NULL && pwzDesktopManifestFilePath[0] != L'\0')
  291. IF_WIN32_FALSE_EXIT(DeleteFile(pwzDesktopManifestFilePath));
  292. // remove subscription
  293. IF_FAILED_EXIT(pAsmIdMask->GetAttribute(SXS_ASSEMBLY_IDENTITY_STD_ATTRIBUTE_NAME_NAME, &pwzName, &dwCount));
  294. IF_FALSE_EXIT(_hr == S_OK, E_FAIL);
  295. {
  296. IAssemblyUpdate *pAssemblyUpdate = NULL;
  297. // register for updates
  298. _hr = CoCreateInstance(CLSID_CAssemblyUpdate, NULL, CLSCTX_LOCAL_SERVER,
  299. IID_IAssemblyUpdate, (void**)&pAssemblyUpdate);
  300. if (SUCCEEDED(_hr))
  301. {
  302. _hr = pAssemblyUpdate->UnRegisterAssemblySubscription(pwzName);
  303. pAssemblyUpdate->Release();
  304. }
  305. if (FAILED(_hr)) // _hr from CoCreateInstance or UnRegisterAssemblySubscription
  306. {
  307. // UI?
  308. MessageBox(NULL, L"Error in update services. Cannot unregister update subscription.", L"ClickOnce",
  309. MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
  310. //goto exit; do not terminate!
  311. }
  312. // BUGBUG: need a way to recover from this and unregister later
  313. delete[] pwzName;
  314. }
  315. // uninstall assemblies from GAC and
  316. // delete app files/dirs
  317. while (TRUE)
  318. {
  319. IF_FAILED_EXIT(pCacheEnum->GetNext(&pCacheImport));
  320. if (_hr == S_FALSE)
  321. break;
  322. IF_NULL_EXIT(pCacheImport, E_UNEXPECTED); // cacheimport cannot be created (app dir may have been deleted)
  323. IF_FAILED_EXIT(UninstallGACAssemblies(pCacheImport));
  324. IF_FAILED_EXIT(pCacheImport->GetManifestFileDir(&pwzAppDir, &dwCount));
  325. IF_FALSE_EXIT(dwCount >= 2, E_FAIL);
  326. // remove last L'\\'
  327. if (*(pwzAppDir+dwCount-2) == L'\\')
  328. *(pwzAppDir+dwCount-2) = L'\0';
  329. //PathRemoveBackslash(pwzAppDir);
  330. IF_FAILED_EXIT(RemoveDirectoryAndChildren(pwzAppDir));
  331. SAFEDELETEARRAY(pwzAppDir);
  332. SAFERELEASE(pCacheImport);
  333. }
  334. // last step: delete registry uninstall info
  335. // open uninstall key
  336. lReturn = RegOpenKeyEx(HKEY_LOCAL_MACHINE, pwzUninstallSubKey, 0,
  337. DELETE, &hkey);
  338. IF_WIN32_FAILED_EXIT(lReturn);
  339. lReturn = RegDeleteKey(hkey, pwzDisplayNameMask);
  340. IF_WIN32_FAILED_EXIT(lReturn);
  341. _hr = S_OK;
  342. exit:
  343. SAFEDELETEARRAY(pwzAppDir);
  344. SAFERELEASE(pCacheImport);
  345. SAFERELEASE(pCacheEnum);
  346. SAFERELEASE(pAsmIdMask);
  347. if (hkey)
  348. {
  349. lReturn = RegCloseKey(hkey);
  350. if (SUCCEEDED(_hr))
  351. _hr = (HRESULT_FROM_WIN32(lReturn));
  352. }
  353. return _hr;
  354. }
  355. // ---------------------------------------------------------------------------
  356. // CVersionManagement::UninstallGACAssemblies
  357. // ---------------------------------------------------------------------------
  358. HRESULT CVersionManagement::UninstallGACAssemblies(LPASSEMBLY_CACHE_IMPORT pCacheImport)
  359. {
  360. LPASSEMBLY_MANIFEST_IMPORT pManImport = NULL;
  361. LPASSEMBLY_IDENTITY pIdentity = NULL;
  362. LPMANIFEST_INFO pDependAsm = NULL;
  363. LPWSTR pwz = NULL;
  364. DWORD dwCount = 0;
  365. DWORD n = 0, dwFlag = 0;
  366. CString sAppAssemblyId;
  367. IF_FAILED_EXIT(pCacheImport->GetManifestFilePath(&pwz, &dwCount));
  368. // open to read from the application manifest file
  369. IF_FAILED_EXIT(CreateAssemblyManifestImport(&pManImport, pwz, NULL, 0));
  370. SAFEDELETEARRAY(pwz);
  371. // get the app assembly id
  372. IF_FAILED_EXIT(pManImport->GetAssemblyIdentity(&pIdentity));
  373. IF_FAILED_EXIT(pIdentity->GetDisplayName(0, &pwz, &dwCount));
  374. IF_FAILED_EXIT(sAppAssemblyId.TakeOwnership(pwz, dwCount));
  375. pwz = NULL;
  376. SAFERELEASE(pIdentity);
  377. // uninstall all dependent assemblies that are installed to the GAC
  378. while (TRUE)
  379. {
  380. IF_FAILED_EXIT(pManImport->GetNextAssembly(n++, &pDependAsm));
  381. if (_hr == S_FALSE)
  382. break;
  383. IF_FAILED_EXIT(pDependAsm->Get(MAN_INFO_DEPENDENT_ASM_ID, (LPVOID *)&pIdentity, &dwCount, &dwFlag));
  384. IF_NULL_EXIT(pIdentity, E_UNEXPECTED);
  385. IF_FAILED_EXIT(::IsKnownAssembly(pIdentity, KNOWN_TRUSTED_ASSEMBLY));
  386. if (_hr == S_FALSE)
  387. {
  388. // ISSUE-2002/07/12-felixybc This has to be cleaned up to use the same mechanism as the download path
  389. IF_FAILED_EXIT(::IsKnownAssembly(pIdentity, KNOWN_SYSTEM_ASSEMBLY));
  390. }
  391. if (_hr == S_OK)
  392. {
  393. CString sAssemblyName;
  394. FUSION_INSTALL_REFERENCE fiRef = {0};
  395. ULONG ulDisposition = 0;
  396. // avalon assemblies are installed to the GAC
  397. // lazy init
  398. if (_pFusionAsmCache == NULL)
  399. IF_FAILED_EXIT(CreateFusionAssemblyCacheEx(&_pFusionAsmCache));
  400. IF_FAILED_EXIT(pIdentity->GetCLRDisplayName(0, &pwz, &dwCount));
  401. IF_FAILED_EXIT(sAssemblyName.TakeOwnership(pwz, dwCount));
  402. pwz = NULL;
  403. // setup the necessary reference struct
  404. fiRef.cbSize = sizeof(FUSION_INSTALL_REFERENCE);
  405. fiRef.dwFlags = 0;
  406. fiRef.guidScheme = FUSION_REFCOUNT_OPAQUE_STRING_GUID;
  407. fiRef.szIdentifier = sAppAssemblyId._pwz;
  408. fiRef.szNonCannonicalData = NULL;
  409. // remove from GAC
  410. IF_FAILED_EXIT(_pFusionAsmCache->UninstallAssembly(0, sAssemblyName._pwz, &fiRef, &ulDisposition));
  411. // BUGBUG: need to recover from the STILL_IN_USE case
  412. IF_FALSE_EXIT(ulDisposition != IASSEMBLYCACHE_UNINSTALL_DISPOSITION_STILL_IN_USE
  413. && ulDisposition != IASSEMBLYCACHE_UNINSTALL_DISPOSITION_REFERENCE_NOT_FOUND, E_FAIL);
  414. }
  415. SAFERELEASE(pIdentity);
  416. SAFERELEASE(pDependAsm);
  417. }
  418. exit:
  419. SAFERELEASE(pDependAsm);
  420. SAFERELEASE(pIdentity);
  421. SAFERELEASE(pManImport);
  422. SAFEDELETEARRAY(pwz);
  423. return _hr;
  424. }
  425. // ---------------------------------------------------------------------------
  426. // CVersionManagement::Rollback
  427. // return: S_FALSE if not found, E_ABORT if aborted
  428. // ---------------------------------------------------------------------------
  429. HRESULT CVersionManagement::Rollback(LPCWSTR pwzDisplayNameMask)
  430. {
  431. // take a displayname mask, make the latest version not visible
  432. // note: a per user setting
  433. // rollback does not check integrity of app cached. if only 2 app dirs exist and all app files deleted,
  434. // rollback still reports success
  435. // timing window: depends on the timing of this and check for max version in cache in app start....
  436. HKEY hkey = NULL;
  437. LPASSEMBLY_IDENTITY pAsmIdMask = NULL;
  438. LPASSEMBLY_CACHE_ENUM pCacheEnum = NULL;
  439. LPASSEMBLY_CACHE_IMPORT pCacheImport = NULL;
  440. DWORD dwCount = 0;
  441. CString sRegKeyString;
  442. LPWSTR pwzDisplayName = NULL;
  443. LPWSTR pwzCacheDir = NULL;
  444. LONG lResult = 0;
  445. IF_NULL_EXIT(pwzDisplayNameMask, E_INVALIDARG);
  446. IF_FALSE_EXIT(pwzDisplayNameMask[0] != L'\0', E_INVALIDARG);
  447. IF_FAILED_EXIT(CreateAssemblyIdentityEx(&pAsmIdMask, 0, (LPWSTR)pwzDisplayNameMask));
  448. // get all applicable, visible versions
  449. IF_FAILED_EXIT(CreateAssemblyCacheEnum(&pCacheEnum, pAsmIdMask, CACHEENUM_RETRIEVE_VISIBLE));
  450. // found nothing, cannot continue
  451. if (_hr == S_FALSE)
  452. goto exit;
  453. // count must be >= 1
  454. pCacheEnum->GetCount(&dwCount);
  455. if (dwCount == 1)
  456. {
  457. MessageBox(NULL, L"Only one active version of this application in the system. Use 'Remove' to remove this application and unregister its subscription.",
  458. L"ClickOnce", MB_OK | MB_ICONINFORMATION | MB_TASKMODAL);
  459. _hr = E_ABORT;
  460. goto exit;
  461. }
  462. // multiple versions, count > 1
  463. // prompt/UI? ask confirmation to continue
  464. IF_TRUE_EXIT(MessageBox(NULL,
  465. 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?",
  466. L"ClickOnce", MB_YESNO | MB_ICONQUESTION | MB_TASKMODAL) != IDYES, E_ABORT);
  467. // get max cached
  468. // BUGBUG: sort cache enum so that max cached is at index 0, and use that instead
  469. // notenote: a timing window - a version can turn invisible or a new version can complete
  470. // between cache enum (a snapshot) above and CreateAsmCacheImport(RESOLVE_REF) below
  471. IF_FAILED_EXIT(CreateAssemblyCacheImport(&pCacheImport, pAsmIdMask, CACHEIMP_CREATE_RESOLVE_REF));
  472. // MessageBox(NULL, L"Error retrieving cached version. Cannot continue.", L"ClickOnce",
  473. // MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
  474. IF_FALSE_EXIT(_hr == S_OK, E_FAIL);
  475. IF_FAILED_EXIT(pCacheImport->GetManifestFileDir(&pwzCacheDir, &dwCount));
  476. IF_FALSE_EXIT(dwCount >= 2, E_FAIL);
  477. // remove last L'\\'
  478. if (*(pwzCacheDir+dwCount-2) == L'\\')
  479. *(pwzCacheDir+dwCount-2) = L'\0';
  480. // find the name to use from the cache path
  481. pwzDisplayName = wcsrchr(pwzCacheDir, L'\\');
  482. IF_NULL_EXIT(pwzDisplayName, E_FAIL);
  483. // BUGBUG: use CAssemblyCache::SetStatus()
  484. // this has to be the same as how assemblycache does it!
  485. IF_FAILED_EXIT(sRegKeyString.Assign(L"Software\\Microsoft\\Fusion\\Installer\\1.0.0.0\\Cache\\"));
  486. IF_FAILED_EXIT(sRegKeyString.Append(pwzDisplayName));
  487. // create key if not exist, ignore disposition information
  488. lResult = RegCreateKeyEx(HKEY_CURRENT_USER, sRegKeyString._pwz, 0, NULL,
  489. REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hkey, NULL);
  490. IF_WIN32_FAILED_EXIT(lResult);
  491. if (lResult == ERROR_SUCCESS)
  492. {
  493. DWORD dwValue = 0;
  494. // set to 0 to make it not visible so that StartW/host/cache will ignore it
  495. // when executing the app but keep the dir name so that download
  496. // will assume it is handled - assemblycache.cpp & assemblydownload.cpp's check
  497. lResult = RegSetValueEx(hkey, L"Visible", NULL, REG_DWORD,
  498. (PBYTE) &dwValue, sizeof(dwValue));
  499. IF_WIN32_FAILED_EXIT(lResult);
  500. if (lResult == ERROR_SUCCESS)
  501. {
  502. MessageBox(NULL, L"Current version disabled. Next time another version of the application will run instead.", L"ClickOnce",
  503. MB_OK | MB_ICONINFORMATION | MB_TASKMODAL);
  504. }
  505. }
  506. exit:
  507. SAFEDELETEARRAY(pwzCacheDir);
  508. SAFERELEASE(pCacheImport);
  509. SAFERELEASE(pCacheEnum);
  510. SAFERELEASE(pAsmIdMask);
  511. if (hkey)
  512. {
  513. lResult = RegCloseKey(hkey);
  514. if (SUCCEEDED(_hr))
  515. _hr = (HRESULT_FROM_WIN32(lResult));
  516. }
  517. return _hr;
  518. }
  519. // IUnknown methods
  520. // ---------------------------------------------------------------------------
  521. // CVersionManagement::QI
  522. // ---------------------------------------------------------------------------
  523. STDMETHODIMP
  524. CVersionManagement::QueryInterface(REFIID riid, void** ppvObj)
  525. {
  526. if ( IsEqualIID(riid, IID_IUnknown)
  527. // || IsEqualIID(riid, IID_IVersionManagement)
  528. )
  529. {
  530. *ppvObj = this; //static_cast<IVersionManagement*> (this);
  531. AddRef();
  532. return S_OK;
  533. }
  534. else
  535. {
  536. *ppvObj = NULL;
  537. return E_NOINTERFACE;
  538. }
  539. }
  540. // ---------------------------------------------------------------------------
  541. // CVersionManagement::AddRef
  542. // ---------------------------------------------------------------------------
  543. STDMETHODIMP_(ULONG)
  544. CVersionManagement::AddRef()
  545. {
  546. return InterlockedIncrement ((LONG*) &_cRef);
  547. }
  548. // ---------------------------------------------------------------------------
  549. // CVersionManagement::Release
  550. // ---------------------------------------------------------------------------
  551. STDMETHODIMP_(ULONG)
  552. CVersionManagement::Release()
  553. {
  554. ULONG lRet = InterlockedDecrement ((LONG*) &_cRef);
  555. if (!lRet)
  556. delete this;
  557. return lRet;
  558. }