Source code of Windows XP (NT5)
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.

724 lines
24 KiB

  1. #include "stdinc.h"
  2. #include "sxsapi.h"
  3. #include "recover.h"
  4. #include "sxsinstall.h"
  5. BOOL
  6. pDeleteFileOrDirectoryHelper(
  7. IN const CBaseStringBuffer &rcbuffFileName
  8. )
  9. /*++
  10. Purpose:
  11. When you need a filesystem object gone, call us.
  12. Parameters:
  13. The absolute name of the thing being killed.
  14. Returns:
  15. TRUE if the object was deleted, false if it (or any subobjects) wasn't.
  16. --*/
  17. {
  18. //
  19. // Maybe this is a directory. Trying this won't hurt.
  20. //
  21. if ( !SxspDeleteDirectory(rcbuffFileName) )
  22. {
  23. //
  24. // Clear the attributes
  25. //
  26. SetFileAttributesW(rcbuffFileName, FILE_ATTRIBUTE_NORMAL);
  27. return DeleteFileW(rcbuffFileName);
  28. }
  29. else
  30. {
  31. return TRUE;
  32. }
  33. }
  34. BOOL
  35. pRemovePotentiallyEmptyDirectory(
  36. IN const CBaseStringBuffer &buffDirName
  37. )
  38. {
  39. FN_PROLOG_WIN32
  40. DWORD dwAttribs = ::GetFileAttributesW(buffDirName);
  41. if (dwAttribs == INVALID_FILE_ATTRIBUTES)
  42. {
  43. DWORD dwLastError = ::FusionpGetLastWin32Error();
  44. switch (dwLastError)
  45. {
  46. case ERROR_SUCCESS:
  47. dwLastError = ERROR_INTERNAL_ERROR; // bogus?!?
  48. break;
  49. case ERROR_FILE_NOT_FOUND:
  50. case ERROR_PATH_NOT_FOUND:
  51. dwLastError = ERROR_SUCCESS;
  52. break;
  53. }
  54. if (dwLastError != ERROR_SUCCESS)
  55. ORIGINATE_WIN32_FAILURE_AND_EXIT(GetFileAttributesW, dwLastError);
  56. dwAttribs = 0;
  57. }
  58. //
  59. // Were we able to find this directory?
  60. //
  61. if (dwAttribs & FILE_ATTRIBUTE_DIRECTORY)
  62. {
  63. BOOL fDumpBoolean;
  64. IFW32FALSE_ORIGINATE_AND_EXIT_UNLESS(
  65. ::SetFileAttributesW(
  66. buffDirName,
  67. FILE_ATTRIBUTE_NORMAL),
  68. FILE_OR_PATH_NOT_FOUND(::FusionpGetLastWin32Error()),
  69. fDumpBoolean);
  70. if (!fDumpBoolean)
  71. {
  72. IFW32FALSE_ORIGINATE_AND_EXIT_UNLESS2(
  73. ::RemoveDirectoryW(buffDirName),
  74. LIST_4(ERROR_FILE_NOT_FOUND, ERROR_PATH_NOT_FOUND, ERROR_DIR_NOT_EMPTY, ERROR_SHARING_VIOLATION),
  75. fDumpBoolean);
  76. }
  77. }
  78. FN_EPILOG
  79. }
  80. BOOL
  81. pCleanUpAssemblyData(
  82. IN const PCASSEMBLY_IDENTITY pcAsmIdent,
  83. OUT BOOL &rfWasRemovedProperly
  84. )
  85. /*++
  86. Purpose:
  87. Deletes registry and filesystem information about the assembly indicated.
  88. Removes installation data from the registry first, so as to avoid SFP
  89. interactions.
  90. Parameters:
  91. pcAsmIdent - Identity of the assembly to be destroyed
  92. rfWasRemovedProperly- Flag to indicate whether or not all the assembly
  93. data was actually removed.
  94. Returns:
  95. FALSE if "anything bad" happened while deleting registry data. See
  96. rfWasRemovedProperly for actual status.
  97. --*/
  98. {
  99. FN_PROLOG_WIN32
  100. BOOL fDumpBoolean;
  101. BOOL fPolicy;
  102. CSmallStringBuffer buffSxsStore;
  103. CStringBuffer buffScratchSpace;
  104. CFusionRegKey hkAsmInstallInfo;
  105. CFusionRegKey hkSingleAsmInfo;
  106. //
  107. // Cleanup happens in two phases:
  108. //
  109. // 1 - The registry data is whacked from rhkAsmInstallInfo. Since we're
  110. // uninstalling an assembly, there's no reason to keep anything in it,
  111. // especially because it's got no references. Use DestroyKeyTree and
  112. // then DeleteKey to remove it.
  113. //
  114. // 2 - Delete as many of the on-disk files as possible, esp. the manifest
  115. // and catalog.
  116. //
  117. PARAMETER_CHECK(pcAsmIdent != NULL);
  118. //
  119. // Start this out at true, we'll call it false later on.
  120. //
  121. rfWasRemovedProperly = TRUE;
  122. IFW32FALSE_EXIT(::SxspDetermineAssemblyType(pcAsmIdent, fPolicy));
  123. IFW32FALSE_EXIT(::SxspGetAssemblyRootDirectory(buffSxsStore));
  124. //
  125. // Bye-bye to the registry first
  126. //
  127. IFW32FALSE_EXIT(::SxspOpenAssemblyInstallationKey(0 , KEY_ALL_ACCESS, hkAsmInstallInfo));
  128. IFW32FALSE_EXIT(::SxspGenerateAssemblyNameInRegistry(pcAsmIdent, buffScratchSpace));
  129. IFW32FALSE_EXIT(hkAsmInstallInfo.OpenSubKey(hkSingleAsmInfo, buffScratchSpace, KEY_ALL_ACCESS, 0));
  130. if ( hkSingleAsmInfo != CFusionRegKey::GetInvalidValue() )
  131. {
  132. //
  133. // Failure here isn't so bad...
  134. //
  135. IFW32FALSE_EXIT_UNLESS2(
  136. hkSingleAsmInfo.DestroyKeyTree(),
  137. LIST_3(ERROR_FILE_NOT_FOUND, ERROR_PATH_NOT_FOUND, ERROR_KEY_DELETED),
  138. fDumpBoolean);
  139. if ( !fDumpBoolean )
  140. {
  141. IFW32FALSE_EXIT_UNLESS2(
  142. hkAsmInstallInfo.DeleteKey(buffScratchSpace),
  143. LIST_3(ERROR_FILE_NOT_FOUND, ERROR_PATH_NOT_FOUND, ERROR_KEY_DELETED),
  144. fDumpBoolean);
  145. }
  146. }
  147. //
  148. // Both policies and normal assemblies have a manifest and catalog.
  149. //
  150. IFW32FALSE_EXIT(
  151. ::SxspGenerateSxsPath(
  152. 0,
  153. fPolicy ? SXSP_GENERATE_SXS_PATH_PATHTYPE_POLICY : SXSP_GENERATE_SXS_PATH_PATHTYPE_MANIFEST,
  154. buffSxsStore,
  155. buffSxsStore.Cch(),
  156. pcAsmIdent,
  157. buffScratchSpace));
  158. rfWasRemovedProperly = rfWasRemovedProperly && ::pDeleteFileOrDirectoryHelper(buffScratchSpace);
  159. IFW32FALSE_EXIT(buffScratchSpace.Win32ChangePathExtension(
  160. FILE_EXTENSION_CATALOG,
  161. FILE_EXTENSION_CATALOG_CCH,
  162. eErrorIfNoExtension));
  163. rfWasRemovedProperly = rfWasRemovedProperly && pDeleteFileOrDirectoryHelper(buffScratchSpace);
  164. //
  165. // Clean up data
  166. //
  167. if (!fPolicy)
  168. {
  169. //
  170. // This just poofs the assembly member files.
  171. // If the delete fails, we'll try to rename the directory to something else.
  172. //
  173. IFW32FALSE_EXIT(
  174. ::SxspGenerateSxsPath(
  175. 0,
  176. SXSP_GENERATE_SXS_PATH_PATHTYPE_ASSEMBLY,
  177. buffSxsStore,
  178. buffSxsStore.Cch(),
  179. pcAsmIdent,
  180. buffScratchSpace));
  181. rfWasRemovedProperly = rfWasRemovedProperly && ::pDeleteFileOrDirectoryHelper(buffScratchSpace);
  182. }
  183. else
  184. {
  185. //
  186. // The policy file above should already have been deleted, so we should
  187. // attempt to remove the actual policy directory if it's empty. The
  188. // directory name is still in buffScratchSpace, if we just yank off the
  189. // last path element.
  190. //
  191. buffScratchSpace.RemoveLastPathElement();
  192. rfWasRemovedProperly = rfWasRemovedProperly && ::pRemovePotentiallyEmptyDirectory(buffScratchSpace);
  193. }
  194. //
  195. // Once we've killed all the assembly information, if the Manifests or the
  196. // Policies directory is left empty, go clean them up as well.
  197. //
  198. IFW32FALSE_EXIT(::SxspGetAssemblyRootDirectory(buffScratchSpace));
  199. IFW32FALSE_EXIT(buffScratchSpace.Win32AppendPathElement(
  200. (fPolicy? POLICY_ROOT_DIRECTORY_NAME : MANIFEST_ROOT_DIRECTORY_NAME),
  201. (fPolicy? NUMBER_OF(POLICY_ROOT_DIRECTORY_NAME) - 1 : NUMBER_OF(MANIFEST_ROOT_DIRECTORY_NAME) - 1)));
  202. IFW32FALSE_EXIT(::pRemovePotentiallyEmptyDirectory(buffScratchSpace));
  203. FN_EPILOG
  204. }
  205. static inline bool IsCharacterNulOrInSet(WCHAR ch, PCWSTR set)
  206. {
  207. return (ch == 0 || wcschr(set, ch) != NULL);
  208. }
  209. BOOL
  210. pAnalyzeLogfileForUninstall(
  211. PCWSTR lpcwszLogFileName
  212. )
  213. {
  214. FN_PROLOG_WIN32
  215. CFusionFile File;
  216. CFileMapping FileMapping;
  217. CMappedViewOfFile MappedViewOfFile;
  218. PCWSTR pCursor = NULL;
  219. ULONGLONG ullFileSize, ullFileCharacters, ullCursorPos;
  220. const static WCHAR wchLineDividers[] = { L'\r', L'\n', 0xFEFF, 0 };
  221. CStringBuffer buffAssemblyIdentity, buffAssemblyReference;
  222. ULONG ullPairsEncountered = 0;
  223. IFW32FALSE_EXIT(File.Win32CreateFile(lpcwszLogFileName, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING));
  224. IFW32FALSE_EXIT(File.Win32GetSize(ullFileSize));
  225. ASSERT(ullFileSize % sizeof(WCHAR) == 0);
  226. ullFileCharacters = ullFileSize / sizeof(WCHAR);
  227. IFW32FALSE_EXIT(FileMapping.Win32CreateFileMapping(File, PAGE_READONLY));
  228. IFW32FALSE_EXIT(MappedViewOfFile.Win32MapViewOfFile(FileMapping, FILE_MAP_READ));
  229. pCursor = reinterpret_cast<PCWSTR>(static_cast<const VOID*>(MappedViewOfFile));
  230. #define SKIP_BREAKERS while ((ullCursorPos < ullFileCharacters) && IsCharacterNulOrInSet(pCursor[ullCursorPos], wchLineDividers)) ullCursorPos++;
  231. #define FIND_NEXT_BREAKER while ((ullCursorPos < ullFileCharacters) && !IsCharacterNulOrInSet(pCursor[ullCursorPos], wchLineDividers)) ullCursorPos++;
  232. #define ENSURE_NOT_EOF if (ullCursorPos >= ullFileCharacters) break;
  233. for ( ullCursorPos = 0; ullCursorPos < ullFileCharacters; ++ullCursorPos )
  234. {
  235. PCWSTR pcwszIdentityStart, pcwszIdentityEnd, pcwszReferenceStart, pcwszReferenceEnd;
  236. SXS_UNINSTALLW Uninstall;
  237. CSmallStringBuffer buffIdentity, buffReference;
  238. SKIP_BREAKERS
  239. ENSURE_NOT_EOF
  240. pcwszIdentityStart = pCursor + ullCursorPos;
  241. FIND_NEXT_BREAKER
  242. ENSURE_NOT_EOF
  243. pcwszIdentityEnd = pCursor + ullCursorPos;
  244. SKIP_BREAKERS
  245. ENSURE_NOT_EOF
  246. pcwszReferenceStart = pCursor + ullCursorPos;
  247. FIND_NEXT_BREAKER
  248. ENSURE_NOT_EOF
  249. pcwszReferenceEnd = pCursor + ullCursorPos;
  250. ullPairsEncountered++;
  251. IFW32FALSE_EXIT(buffIdentity.Win32Assign(
  252. pcwszIdentityStart,
  253. pcwszIdentityEnd - pcwszIdentityStart));
  254. IFW32FALSE_EXIT(buffReference.Win32Assign(
  255. pcwszReferenceStart,
  256. pcwszReferenceEnd - pcwszReferenceStart));
  257. ZeroMemory(&Uninstall, sizeof(Uninstall));
  258. Uninstall.cbSize = sizeof(Uninstall);
  259. Uninstall.dwFlags = SXS_UNINSTALL_FLAG_REFERENCE_VALID | SXS_UNINSTALL_FLAG_REFERENCE_COMPUTED;
  260. Uninstall.lpAssemblyIdentity = buffIdentity;
  261. Uninstall.lpInstallReference = reinterpret_cast<PCSXS_INSTALL_REFERENCEW>(static_cast<PCWSTR>(buffReference));
  262. IFW32FALSE_EXIT(::SxsUninstallW(&Uninstall, NULL));
  263. }
  264. PARAMETER_CHECK(ullPairsEncountered != 0);
  265. FN_EPILOG
  266. }
  267. BOOL
  268. WINAPI
  269. SxsUninstallW(
  270. IN PCSXS_UNINSTALLW pcUnInstallData,
  271. OUT DWORD *pdwDisposition
  272. )
  273. /*++
  274. Parameters:
  275. pcUnInstallData - Contains uninstallation data about the assembly being
  276. removed from the system, including the calling application's reference
  277. to the assembly.
  278. cbSize - Size, in bytes, of the structure pointed to by
  279. pcUnInstallData
  280. dwFlags - Indicates the state of the members of this reference,
  281. showing which of the following fields are valid.
  282. Allowed bitflags are:
  283. SXS_UNINSTALL_FLAG_REFERENCE_VALID
  284. SXS_UNINSTALL_FLAG_FORCE_DELETE
  285. lpAssemblyIdentity - Textual representation of the assembly's identity
  286. as installed by the application.
  287. lpInstallReference - Pointer to a SXS_INSTALL_REFERENCEW structure
  288. that contains the reference information for this
  289. application.
  290. pdwDisposition - Points to a DWORD that will return status about what was
  291. done to the assembly; whether it was uninstalled or not,
  292. and whether the reference given was removed.
  293. Returns:
  294. TRUE if the assembly was able to be uninstalled, FALSE otherwise. If the
  295. uninstall failed, lasterror is set to the probable cause.
  296. --*/
  297. {
  298. BOOL fSuccess = FALSE;
  299. FN_TRACE_WIN32(fSuccess);
  300. PCWSTR pcwszUninstallIdentity = NULL;
  301. PCSXS_INSTALL_REFERENCEW pcInstallReference = NULL;
  302. CFusionRegKey hkAllInstallInfo;
  303. CFusionRegKey hkAsmInstallInfo;
  304. CFusionRegKey hkReferences;
  305. CStringBuffer buffAsmNameInRegistry;
  306. BOOL fDoRemoveActualBits = FALSE;
  307. CSxsPointerWithNamedDestructor<ASSEMBLY_IDENTITY, SxsDestroyAssemblyIdentity> AssemblyIdentity;
  308. if (pdwDisposition != NULL)
  309. *pdwDisposition = 0;
  310. //
  311. // The parameter must be non-null, and must have at least dwFlags and the
  312. // assemblyidentity.
  313. //
  314. PARAMETER_CHECK(pcUnInstallData != NULL);
  315. PARAMETER_CHECK(RTL_CONTAINS_FIELD(pcUnInstallData, pcUnInstallData->cbSize, dwFlags) &&
  316. RTL_CONTAINS_FIELD(pcUnInstallData, pcUnInstallData->cbSize, lpAssemblyIdentity));
  317. //
  318. // Check flags
  319. //
  320. PARAMETER_CHECK((pcUnInstallData->dwFlags &
  321. ~(SXS_UNINSTALL_FLAG_FORCE_DELETE |
  322. SXS_UNINSTALL_FLAG_REFERENCE_VALID |
  323. SXS_UNINSTALL_FLAG_USE_INSTALL_LOG |
  324. SXS_UNINSTALL_FLAG_REFERENCE_COMPUTED)) == 0);
  325. //
  326. // If you specify the uninstall log, then that's the only thing that can be set. XOR
  327. // them together, so only one of the two will be set.
  328. //
  329. PARAMETER_CHECK(
  330. ((pcUnInstallData->dwFlags & SXS_UNINSTALL_FLAG_USE_INSTALL_LOG) == 0) ||
  331. ((pcUnInstallData->dwFlags & (SXS_UNINSTALL_FLAG_REFERENCE_COMPUTED|SXS_UNINSTALL_FLAG_REFERENCE_VALID|SXS_UNINSTALL_FLAG_FORCE_DELETE)) == 0));
  332. //
  333. // If the reference flag was set, then the member has to be present, and
  334. // non-null as well.
  335. //
  336. PARAMETER_CHECK(((pcUnInstallData->dwFlags & SXS_UNINSTALL_FLAG_REFERENCE_VALID) == 0) ||
  337. (RTL_CONTAINS_FIELD(pcUnInstallData, pcUnInstallData->cbSize, lpInstallReference) &&
  338. (pcUnInstallData->lpInstallReference != NULL)));
  339. //
  340. // If the log file is not present, the assembly identity can't be a zero-length string, and it can't be null - it's
  341. // required.
  342. //
  343. PARAMETER_CHECK((pcUnInstallData->dwFlags & SXS_UNINSTALL_FLAG_USE_INSTALL_LOG) || ((pcUnInstallData->lpAssemblyIdentity != NULL) && (pcUnInstallData->lpAssemblyIdentity[0] != UNICODE_NULL)));
  344. //
  345. // If the install log flag was set, then the member needs to be set and non-null
  346. //
  347. PARAMETER_CHECK(((pcUnInstallData->dwFlags & SXS_UNINSTALL_FLAG_USE_INSTALL_LOG) == 0) ||
  348. (RTL_CONTAINS_FIELD(pcUnInstallData, pcUnInstallData->cbSize, lpInstallLogFile) &&
  349. ((pcUnInstallData->lpInstallLogFile != NULL) && (pcUnInstallData->lpInstallLogFile[0] != UNICODE_NULL))));
  350. if ( pcUnInstallData->dwFlags & SXS_UNINSTALL_FLAG_USE_INSTALL_LOG )
  351. {
  352. IFW32FALSE_EXIT(pAnalyzeLogfileForUninstall(pcUnInstallData->lpInstallLogFile));
  353. }
  354. else
  355. {
  356. //
  357. // And the reference scheme must not be SXS_INSTALL_REFERENCE_SCHEME_OSINSTALL,
  358. // as you can't "uninstall" OS-installed assemblies!
  359. //
  360. if (pcUnInstallData->dwFlags & SXS_UNINSTALL_FLAG_REFERENCE_VALID)
  361. {
  362. if (pcUnInstallData->dwFlags & SXS_UNINSTALL_FLAG_REFERENCE_COMPUTED)
  363. {
  364. PCWSTR pcwszReferenceString;
  365. PCWSTR pcwszEndOfString;
  366. GUID gTheGuid;
  367. pcwszReferenceString = reinterpret_cast<PCWSTR>(pcUnInstallData->lpInstallReference);
  368. //
  369. // Non-null, non-zero-length
  370. //
  371. PARAMETER_CHECK((pcwszReferenceString != NULL) && (pcwszReferenceString[0] != L'\0'));
  372. //
  373. // Parse the displayed guid. If there's no _, then ensure that the guid
  374. // is not the os-installed guid.
  375. //
  376. pcwszEndOfString = wcschr(pcwszReferenceString, SXS_REFERENCE_CHUNK_SEPERATOR[0]);
  377. if ( pcwszEndOfString == NULL )
  378. {
  379. pcwszEndOfString = pcwszReferenceString + ::wcslen(pcwszReferenceString);
  380. IFW32FALSE_EXIT(
  381. ::SxspParseGUID(
  382. pcwszReferenceString,
  383. pcwszEndOfString - pcwszReferenceString,
  384. gTheGuid));
  385. PARAMETER_CHECK(gTheGuid != SXS_INSTALL_REFERENCE_SCHEME_OSINSTALL);
  386. }
  387. }
  388. else
  389. {
  390. PARAMETER_CHECK(pcUnInstallData->lpInstallReference->guidScheme != SXS_INSTALL_REFERENCE_SCHEME_OSINSTALL);
  391. }
  392. }
  393. //
  394. // Let's turn the identity back into a real identity object
  395. //
  396. IFW32FALSE_EXIT(
  397. ::SxspCreateAssemblyIdentityFromTextualString(
  398. pcUnInstallData->lpAssemblyIdentity,
  399. &AssemblyIdentity));
  400. IFW32FALSE_EXIT(
  401. ::SxspValidateIdentity(
  402. SXSP_VALIDATE_IDENTITY_FLAG_VERSION_REQUIRED,
  403. ASSEMBLY_IDENTITY_TYPE_REFERENCE,
  404. AssemblyIdentity));
  405. //
  406. // And go open the registry key that corresponds to it
  407. //
  408. IFW32FALSE_EXIT(::SxspOpenAssemblyInstallationKey(
  409. 0,
  410. KEY_ALL_ACCESS,
  411. hkAllInstallInfo));
  412. IFW32FALSE_EXIT(::SxspGenerateAssemblyNameInRegistry(
  413. AssemblyIdentity,
  414. buffAsmNameInRegistry));
  415. IFW32FALSE_EXIT(hkAllInstallInfo.OpenSubKey(
  416. hkAsmInstallInfo,
  417. buffAsmNameInRegistry,
  418. KEY_ALL_ACCESS,
  419. 0));
  420. //
  421. // If the assembly didn't have registry data, then obviously nobody cares
  422. // about it at all. Delete it with great vigor.
  423. //
  424. if (hkAsmInstallInfo == CFusionRegKey::GetInvalidValue())
  425. {
  426. fDoRemoveActualBits = TRUE;
  427. }
  428. else
  429. {
  430. DWORD dwReferenceCount;
  431. BOOL fTempFlag = FALSE;
  432. //
  433. // We're going to need the references key in just a second...
  434. //
  435. IFW32FALSE_EXIT(
  436. hkAsmInstallInfo.OpenOrCreateSubKey(
  437. hkReferences,
  438. WINSXS_INSTALLATION_REFERENCES_SUBKEY,
  439. KEY_ALL_ACCESS,
  440. 0, NULL, NULL));
  441. //
  442. // If we were given an uninstall reference, then attempt to remove it.
  443. //
  444. if (pcUnInstallData->dwFlags & SXS_UNINSTALL_FLAG_REFERENCE_VALID)
  445. {
  446. SMARTPTR(CAssemblyInstallReferenceInformation) AssemblyReference;
  447. BOOL fWasDeleted = FALSE;
  448. //
  449. // Opened the references key OK?
  450. //
  451. if (hkReferences != CFusionRegKey::GetInvalidValue())
  452. {
  453. IFW32FALSE_EXIT(AssemblyReference.Win32Allocate(__FILE__, __LINE__));
  454. //
  455. // Did the user precompute the reference string?
  456. //
  457. if (pcUnInstallData->dwFlags & SXS_UNINSTALL_FLAG_REFERENCE_COMPUTED)
  458. IFW32FALSE_EXIT(AssemblyReference->ForceReferenceData(reinterpret_cast<PCWSTR>(pcUnInstallData->lpInstallReference)));
  459. else
  460. IFW32FALSE_EXIT(AssemblyReference->Initialize(pcUnInstallData->lpInstallReference));
  461. IFW32FALSE_EXIT(AssemblyReference->DeleteReferenceFrom(hkReferences, fWasDeleted));
  462. }
  463. if (fWasDeleted)
  464. {
  465. //
  466. // and delete the codebase
  467. //
  468. CFusionRegKey CodeBases;
  469. CFusionRegKey ThisCodeBase;
  470. DWORD Win32Error = NO_ERROR;
  471. IFW32FALSE_ORIGINATE_AND_EXIT_UNLESS3(
  472. hkAsmInstallInfo.OpenSubKey(
  473. CodeBases,
  474. CSMD_TOPLEVEL_CODEBASES,
  475. KEY_ALL_ACCESS,
  476. 0),
  477. LIST_3(ERROR_FILE_NOT_FOUND, ERROR_PATH_NOT_FOUND, ERROR_KEY_DELETED),
  478. Win32Error);
  479. if (Win32Error == NO_ERROR)
  480. {
  481. IFW32FALSE_ORIGINATE_AND_EXIT_UNLESS3(
  482. CodeBases.OpenSubKey(
  483. ThisCodeBase,
  484. AssemblyReference->GetGeneratedIdentifier(),
  485. KEY_ALL_ACCESS,
  486. 0),
  487. LIST_3(ERROR_FILE_NOT_FOUND, ERROR_PATH_NOT_FOUND, ERROR_KEY_DELETED),
  488. Win32Error);
  489. }
  490. if (Win32Error == NO_ERROR)
  491. {
  492. IFW32FALSE_ORIGINATE_AND_EXIT_UNLESS3(
  493. ThisCodeBase.DestroyKeyTree(),
  494. LIST_3(ERROR_FILE_NOT_FOUND, ERROR_PATH_NOT_FOUND, ERROR_KEY_DELETED),
  495. Win32Error);
  496. }
  497. if (Win32Error == NO_ERROR)
  498. {
  499. IFW32FALSE_ORIGINATE_AND_EXIT(ThisCodeBase.Win32Close());
  500. IFW32FALSE_ORIGINATE_AND_EXIT_UNLESS3(
  501. CodeBases.DeleteKey(AssemblyReference->GetGeneratedIdentifier()),
  502. LIST_3(ERROR_FILE_NOT_FOUND, ERROR_PATH_NOT_FOUND, ERROR_KEY_DELETED),
  503. Win32Error);
  504. }
  505. //
  506. // If the assembly reference was removed, tell our caller.
  507. //
  508. if (pdwDisposition != NULL)
  509. {
  510. *pdwDisposition |= SXS_UNINSTALL_DISPOSITION_REMOVED_REFERENCE;
  511. }
  512. }
  513. }
  514. //
  515. // Now see if there are any references left at all.
  516. //
  517. IFREGFAILED_ORIGINATE_AND_EXIT_UNLESS2(
  518. ::RegQueryInfoKeyW(
  519. hkReferences,
  520. NULL, NULL, NULL, NULL, NULL, NULL,
  521. &dwReferenceCount,
  522. NULL, NULL, NULL, NULL),
  523. LIST_3(ERROR_FILE_NOT_FOUND, ERROR_PATH_NOT_FOUND, ERROR_KEY_DELETED),
  524. fTempFlag);
  525. //
  526. // If getting the key information succeeded and there were no more references,
  527. // then pow - make it go away.
  528. //
  529. if ((!fTempFlag) && (dwReferenceCount == 0))
  530. fDoRemoveActualBits = TRUE;
  531. }
  532. //
  533. // Now, if the "force delete" flag was set, set the "nuke this data anyhow"
  534. // flag. MSI still gets to veto the uninstall, so make sure that's done last.
  535. //
  536. if ((!fDoRemoveActualBits) && (pcUnInstallData->dwFlags & SXS_UNINSTALL_FLAG_FORCE_DELETE))
  537. fDoRemoveActualBits = TRUE;
  538. //
  539. // One last chance - we're about to remove the assembly from the system. Does Darwin
  540. // know about it?
  541. //
  542. if ( fDoRemoveActualBits )
  543. {
  544. IFW32FALSE_EXIT(
  545. ::SxspDoesMSIStillNeedAssembly(
  546. pcUnInstallData->lpAssemblyIdentity,
  547. fDoRemoveActualBits));
  548. fDoRemoveActualBits = !fDoRemoveActualBits;
  549. }
  550. if ( fDoRemoveActualBits && (hkReferences != CFusionRegKey::GetInvalidValue()))
  551. {
  552. //
  553. // One last check - is the assembly referenced by the OS? They get absolute
  554. // trump over all the other checks.
  555. //
  556. CAssemblyInstallReferenceInformation Ref;
  557. SXS_INSTALL_REFERENCEW Reference = { sizeof(Reference) };
  558. ZeroMemory(&Reference, sizeof(Reference));
  559. Reference.guidScheme = SXS_INSTALL_REFERENCE_SCHEME_OSINSTALL;
  560. IFW32FALSE_EXIT(Ref.Initialize(&Reference));
  561. IFW32FALSE_EXIT(Ref.IsReferencePresentIn(hkReferences, fDoRemoveActualBits));
  562. //
  563. // If it was present, then don't remove!
  564. //
  565. fDoRemoveActualBits = !fDoRemoveActualBits;
  566. }
  567. //
  568. // Now, if we're still supposed to delete the assembly, go yank it out of the
  569. // registry and the filesystem; pCleanupAssemblyData knows how to do that.
  570. //
  571. if (fDoRemoveActualBits)
  572. {
  573. BOOL fWasRemovedProperly;
  574. IFW32FALSE_EXIT(::pCleanUpAssemblyData(AssemblyIdentity, fWasRemovedProperly));
  575. if (fWasRemovedProperly && (pdwDisposition != NULL))
  576. *pdwDisposition |= SXS_UNINSTALL_DISPOSITION_REMOVED_ASSEMBLY;
  577. }
  578. }
  579. fSuccess = TRUE;
  580. Exit:
  581. #if DBG
  582. if (!fSuccess && pcUnInstallData != NULL && pcUnInstallData->lpAssemblyIdentity != NULL)
  583. {
  584. ::FusionpDbgPrintEx(
  585. FUSION_DBG_LEVEL_ERROR,
  586. "SXS.DLL: %s(%ls) failed\n",
  587. __FUNCTION__,
  588. pcUnInstallData->lpAssemblyIdentity
  589. );
  590. }
  591. #endif
  592. return fSuccess;
  593. }