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.

721 lines
20 KiB

  1. /*++
  2. Copyright (c) 2001 Microsoft Corporation
  3. Module Name:
  4. Duuninst.cpp
  5. Abstract:
  6. Contains the main uninstallation code
  7. used by the app.
  8. Notes:
  9. Unicode only.
  10. History:
  11. 03/02/2001 rparsons Created
  12. --*/
  13. #include "precomp.h"
  14. extern SETUP_INFO g_si;
  15. /*++
  16. Routine Description:
  17. Removes the specified registry key
  18. Assumes HKEY_LOCAL_MACHINE
  19. Arguments:
  20. lpwKey - Path of the key to open
  21. lpwSubKey - Path of the subkey to delete
  22. Return Value:
  23. None
  24. --*/
  25. void
  26. UninstallDeleteSubKey(
  27. IN LPCWSTR lpwKey,
  28. IN LPCWSTR lpwSubKey
  29. )
  30. {
  31. CRegistry creg;
  32. //
  33. // Remove the key from the registry
  34. //
  35. creg.DeleteRegistryKey(HKEY_LOCAL_MACHINE,
  36. lpwKey,
  37. lpwSubKey,
  38. TRUE,
  39. TRUE);
  40. return;
  41. }
  42. /*++
  43. Routine Description:
  44. Retrieves the section names from the uninstall
  45. INF file. This dictates what operations will be
  46. performed during uninstall
  47. Arguments:
  48. None
  49. Return Value:
  50. TRUE on success, FALSE otherwise
  51. --*/
  52. BOOL
  53. UninstallGetSectionsFromINF()
  54. {
  55. BOOL fReturn = FALSE;
  56. DWORD dwType = 0;
  57. char szSectionName[MAX_QUEUE_SIZE] = "";
  58. char *pSectionName;
  59. WCHAR wszDirective[MAX_PATH] = L"";
  60. INFCONTEXT InfContext;
  61. //
  62. // Loop through all the lines in the Sections section(s),
  63. //
  64. fReturn = SetupFindFirstLineA(g_si.hInf, INF_MASTER_SECTIONS, NULL,
  65. &InfContext) &&
  66. SetupGetLineTextA(&InfContext,
  67. NULL, NULL, NULL,
  68. szSectionName, MAX_QUEUE_SIZE, NULL);
  69. while (fReturn) {
  70. //
  71. // Determine which section we're working with
  72. //
  73. if (strstr(szSectionName, INF_RESTORE_FILES)) {
  74. dwType = dwRestoreFiles;
  75. } else if (strstr(szSectionName, INF_DELETE_REGISTRY)) {
  76. dwType = dwDeleteRegistry;
  77. } else if (strstr(szSectionName, INF_RESTORE_REGISTRY)) {
  78. dwType = dwRestoreRegistry;
  79. } else if (strstr(szSectionName, INF_UNREGISTRATIONS)) {
  80. dwType = dwUnRegistrations;
  81. } else if (strstr(szSectionName, INF_EXCLUDE)) {
  82. dwType = dwExclusionsUninstall;
  83. } else {
  84. Print(ERROR,
  85. L"[UninstallGetSectionsFromINF] Illegal section name passed %s\n",
  86. szSectionName);
  87. return FALSE; // illegal section name
  88. }
  89. pSectionName = strtok(szSectionName, ",");
  90. do {
  91. pAnsiToUnicode(pSectionName, wszDirective, MAX_PATH);
  92. //
  93. // Loop through each section name and add it to the
  94. // appropriate queue
  95. //
  96. switch (dwType) {
  97. case dwRestoreFiles:
  98. g_si.RestoreFileQueue.Enqueue(wszDirective);
  99. break;
  100. case dwDeleteRegistry:
  101. g_si.DeleteRegistryQueue.Enqueue(wszDirective);
  102. break;
  103. case dwRestoreRegistry:
  104. g_si.RestoreRegistryQueue.Enqueue(wszDirective);
  105. break;
  106. case dwUnRegistrations:
  107. g_si.RegistrationQueue.Enqueue(wszDirective);
  108. break;
  109. case dwExclusionsUninstall:
  110. g_si.ExclusionQueue.Enqueue(wszDirective);
  111. break;
  112. default:
  113. return FALSE; // illegal section name
  114. }
  115. pSectionName = strtok(NULL, ",");
  116. } while (NULL != pSectionName);
  117. fReturn = SetupFindNextLine(&InfContext, &InfContext) &&
  118. SetupGetLineTextA(&InfContext,
  119. NULL, NULL, NULL,
  120. szSectionName, MAX_QUEUE_SIZE, NULL);
  121. }
  122. return TRUE;
  123. }
  124. /*++
  125. Routine Description:
  126. Restores registry keys that were saved
  127. during the install
  128. Arguments:
  129. None
  130. Return Value:
  131. TRUE on success, FALSE otherwise
  132. --*/
  133. BOOL
  134. UninstallRestoreRegistryKeys()
  135. {
  136. BOOL fReturn = FALSE, fResult = FALSE;
  137. HKEY hKeyRoot = NULL;
  138. char szEntry[MAX_QUEUE_SIZE] = "";
  139. WCHAR wszEntry[MAX_QUEUE_SIZE] = L"";
  140. char szKeyPath[MAX_PATH*3] = "";
  141. WCHAR wszKeyPath[MAX_PATH*3] = L"";
  142. LPWSTR lpwSubKey = NULL, lpwFilePath = NULL, lpwKeyRoot = NULL;
  143. UINT uCount = 0;
  144. CRegistry creg;
  145. INFCONTEXT InfContext;
  146. //
  147. // The entry in the uninstall INF will have the following format:
  148. // HKxx,SubKeyPath,PathToBackupFile
  149. //
  150. //
  151. // Step through each entry in the queue
  152. //
  153. while (g_si.RestoreRegistryQueue.GetSize()) {
  154. g_si.RestoreRegistryQueue.Dequeue(wszEntry, MAX_PATH - 1, FALSE);
  155. pUnicodeToAnsi(wszEntry, szEntry, MAX_PATH);
  156. //
  157. // Loop through all the lines in the restore registry section(s),
  158. // and perform the restore
  159. //
  160. fReturn = SetupFindFirstLineA(g_si.hInf, szEntry, NULL,
  161. &InfContext) &&
  162. SetupGetLineTextA(&InfContext,
  163. NULL, NULL, NULL,
  164. szKeyPath, MAX_PATH*3, NULL);
  165. while (fReturn) {
  166. pAnsiToUnicode(szKeyPath, wszKeyPath, MAX_PATH*3);
  167. //
  168. // Split the key path into three separate parts
  169. //
  170. lpwKeyRoot = GetNextToken(wszKeyPath, L",");
  171. if (NULL == lpwKeyRoot) {
  172. break;
  173. }
  174. Print(TRACE, L"[UninstallRestoreRegistryKeys] Key root: %s\n", lpwKeyRoot);
  175. if (!_wcsicmp(lpwKeyRoot, L"HKLM")) {
  176. hKeyRoot = HKEY_LOCAL_MACHINE;
  177. } else if (!_wcsicmp(lpwKeyRoot, L"HKCR")) {
  178. hKeyRoot = HKEY_CLASSES_ROOT;
  179. } else if (!_wcsicmp(lpwKeyRoot, L"HKCU")) {
  180. hKeyRoot = HKEY_CURRENT_USER;
  181. } else if (!_wcsicmp(lpwKeyRoot, L"HKU")) {
  182. hKeyRoot = HKEY_USERS;
  183. } else {
  184. break;
  185. }
  186. //
  187. // Get the subkey path
  188. //
  189. lpwSubKey = GetNextToken(NULL, L",");
  190. if (NULL == lpwSubKey) {
  191. break;
  192. }
  193. Print(TRACE, L"[UninstallRestoreRegistryKeys] Subkey path: %s\n", lpwSubKey);
  194. //
  195. // Get the file name with full path
  196. //
  197. lpwFilePath = GetNextToken(NULL, L",");
  198. if (NULL == lpwFilePath) {
  199. break;
  200. }
  201. Print(TRACE, L"[UninstallRestoreRegistryKeys] File path: %s\n", lpwFilePath);
  202. //
  203. // Restore the key
  204. //
  205. fResult = creg.RestoreKey(hKeyRoot, lpwSubKey, lpwFilePath, TRUE);
  206. if (!fResult) {
  207. Print(ERROR,
  208. L"[UninstallRestoreRegistryKeys] Failed to restore key\n");
  209. return FALSE;
  210. }
  211. fReturn = SetupFindNextLine(&InfContext, &InfContext) &&
  212. SetupGetLineTextA(&InfContext,
  213. NULL, NULL, NULL,
  214. szKeyPath, MAX_PATH*3, NULL);
  215. }
  216. }
  217. return TRUE;
  218. }
  219. /*++
  220. Routine Description:
  221. Removes files from the installation directory
  222. Arguments:
  223. None
  224. Return Value:
  225. None
  226. --*/
  227. BOOL
  228. UninstallRemoveFiles()
  229. {
  230. CEnumDir ced;
  231. //
  232. // Remove files from the installation directory,
  233. // but leave subdirectories alone
  234. //
  235. ced.EnumerateDirectoryTree(g_si.lpwInstallDirectory,
  236. DeleteOneFile,
  237. FALSE,
  238. (PVOID) TRUE);
  239. return TRUE;
  240. }
  241. /*++
  242. Routine Description:
  243. Restores files that were backed up during the install
  244. Arguments:
  245. None
  246. Return Value:
  247. TRUE on success, FALSE otherwise
  248. --*/
  249. BOOL
  250. UninstallRestoreFiles()
  251. {
  252. WCHAR wszBackupDir[MAX_PATH] = L"";
  253. WCHAR wszDestFileName[MAX_PATH] = L"";
  254. WCHAR wszSourceFileName[MAX_PATH] = L"";
  255. char szEntry[MAX_PATH] = "";
  256. WCHAR wszEntry[MAX_PATH] = L"";
  257. char szFileName[MAX_PATH] = "";
  258. WCHAR wszFileName[MAX_PATH] = L"";
  259. BOOL fReturn = FALSE, fResult = FALSE;
  260. LPWSTR lpwDestDir = NULL;
  261. DWORDLONG dwlSourceVersion = 0, dwlDestVersion = 0;
  262. INFCONTEXT InfContext;
  263. //
  264. // Step through each entry in the queue
  265. //
  266. while (g_si.RestoreFileQueue.GetSize()) {
  267. g_si.RestoreFileQueue.Dequeue(wszEntry, MAX_PATH - 1, FALSE);
  268. pUnicodeToAnsi(wszEntry, szEntry, MAX_PATH);
  269. //
  270. // Get the destination directory
  271. //
  272. GetNextToken(wszEntry, L".");
  273. GetNextToken(NULL, L".");
  274. lpwDestDir = GetNextToken(NULL, L".");
  275. if (NULL == lpwDestDir) {
  276. break;
  277. }
  278. //
  279. // Loop through all the lines in the restore files section(s),
  280. // and perform the copy
  281. //
  282. fReturn = SetupFindFirstLineA(g_si.hInf, szEntry, NULL,
  283. &InfContext) &&
  284. SetupGetLineTextA(&InfContext,
  285. NULL, NULL, NULL,
  286. szFileName, MAX_PATH, NULL);
  287. while (fReturn) {
  288. pAnsiToUnicode(szFileName, wszFileName, MAX_PATH);
  289. //
  290. // Build the path to the destination file
  291. //
  292. wsprintf(wszDestFileName,
  293. L"%s\\%s\\%s",
  294. g_si.lpwWindowsDirectory,
  295. lpwDestDir,
  296. wszFileName);
  297. //
  298. // Build the path to the source file
  299. //
  300. wsprintf(wszSourceFileName,
  301. L"%s\\%s\\%s",
  302. g_si.lpwExtractPath,
  303. g_si.lpwUninstallDirectory,
  304. wszFileName);
  305. //
  306. // Ensure that this file is not under WFP
  307. //
  308. fResult = IsFileProtected(wszDestFileName);
  309. //
  310. // Copy the file - be sensitive to WFP
  311. //
  312. if (fResult) {
  313. Print(TRACE,
  314. L"[UninstallRestoreFiles] Preparing to restore WFP file from %s to %s\n",
  315. wszSourceFileName, wszDestFileName);
  316. fResult = CommonEnableProtectedRenames();
  317. if (!fResult) {
  318. Print(ERROR,
  319. L"[UninstallRestoreFiles] Failed to enable protected renames\n");
  320. return FALSE;
  321. }
  322. Print(TRACE,
  323. L"[UninstallRestoreFiles] Preparing to install WFP file from %s to %s\n",
  324. wszSourceFileName, wszDestFileName);
  325. fResult = UninstallWFPFile(wszSourceFileName,
  326. wszFileName,
  327. wszDestFileName,
  328. g_si.fUpdateDllCache);
  329. if (!fResult) {
  330. Print(ERROR,
  331. L"[UninstallRestoreFiles] Failed to install WFP file %s\n",
  332. wszFileName);
  333. return FALSE;
  334. }
  335. } else {
  336. Print(TRACE,
  337. L"[UninstallRestoreFiles] Preparing to restore file from %s to %s\n",
  338. wszSourceFileName, wszDestFileName);
  339. fResult = ForceMove(wszSourceFileName, wszDestFileName);
  340. if (!fResult) {
  341. Print(ERROR,
  342. L"[UninstallRestoreFiles] Failed to restore file from %s to %s. GLE = %d\n",
  343. wszSourceFileName, wszDestFileName, GetLastError());
  344. return FALSE;
  345. }
  346. }
  347. fReturn = SetupFindNextLine(&InfContext, &InfContext) &&
  348. SetupGetLineTextA(&InfContext,
  349. NULL, NULL, NULL,
  350. szFileName, MAX_PATH, NULL);
  351. }
  352. }
  353. return TRUE;
  354. }
  355. /*++
  356. Routine Description:
  357. Restores a file that is protected
  358. by WFP
  359. Arguments:
  360. lpwSourceFileName - Path to the source file
  361. lpwDestFileName - Name of the destination file
  362. lpwDestFileNamePath Name & path to the destination file
  363. fUpdateDllCache - A flag to indicate if the file
  364. should be placed in the DllCache directory
  365. Return Value:
  366. TRUE on success, FALSE otherwise
  367. --*/
  368. BOOL
  369. UninstallWFPFile(
  370. IN LPCWSTR lpwSourceFileName,
  371. IN LPCWSTR lpwDestFileName,
  372. IN LPCWSTR lpwDestFileNamePath,
  373. IN BOOL fUpdateDllCache
  374. )
  375. {
  376. LPWSTR lpwCachePath = NULL;
  377. LPWSTR lpwExpandedCachePath = NULL;
  378. LPWSTR lpwTempFileName = NULL;
  379. DWORD cbSize = 0;
  380. WCHAR wszDllCachePath[MAX_PATH] = L"";
  381. CRegistry creg;
  382. if (g_si.fUpdateDllCache) {
  383. Print(TRACE,
  384. L"[UninstallWFPFile] Restoring %s to DllCache dir\n",
  385. lpwSourceFileName);
  386. //
  387. // Try to get the dllcache directory path from
  388. // the registry
  389. //
  390. lpwCachePath = creg.GetString(HKEY_LOCAL_MACHINE,
  391. REG_WINFP_PATH,
  392. L"SfcDllCacheDir",
  393. TRUE);
  394. if (lpwCachePath) {
  395. if (cbSize = ExpandEnvironmentStrings(lpwCachePath,
  396. lpwExpandedCachePath,
  397. 0)) {
  398. lpwExpandedCachePath = (LPWSTR) MALLOC(cbSize*sizeof(WCHAR));
  399. if (lpwExpandedCachePath) {
  400. if (ExpandEnvironmentStrings(lpwCachePath,
  401. lpwExpandedCachePath,
  402. cbSize)) {
  403. //
  404. // Build a full path to \%windir%\system32\dllcache\filename.xxx
  405. //
  406. wsprintf(wszDllCachePath,
  407. L"%s\\%s",
  408. lpwExpandedCachePath,
  409. lpwDestFileName);
  410. }
  411. }
  412. }
  413. }
  414. //
  415. // If we couldn't get it from that key, try another
  416. //
  417. if (NULL == lpwExpandedCachePath) {
  418. lpwCachePath = creg.GetString(HKEY_LOCAL_MACHINE,
  419. REG_WINLOGON_PATH,
  420. L"SfcDllCacheDir",
  421. TRUE);
  422. if (lpwCachePath) {
  423. if (cbSize = ExpandEnvironmentStrings(lpwCachePath,
  424. lpwExpandedCachePath,
  425. 0)) {
  426. lpwExpandedCachePath = (LPWSTR) MALLOC(cbSize*sizeof(WCHAR));
  427. if (lpwExpandedCachePath) {
  428. if (ExpandEnvironmentStrings(lpwCachePath,
  429. lpwExpandedCachePath,
  430. cbSize)) {
  431. //
  432. // Build a full path to \%windir%\system32\dllcache\filename.xxx
  433. //
  434. wsprintf(wszDllCachePath,
  435. L"%s\\%s",
  436. lpwExpandedCachePath,
  437. lpwDestFileName);
  438. }
  439. }
  440. }
  441. }
  442. }
  443. //
  444. // If neither key worked, build the path manually
  445. //
  446. if (NULL == lpwExpandedCachePath) {
  447. wsprintf(wszDllCachePath,
  448. L"%s\\DllCache\\%s",
  449. g_si.lpwSystem32Directory,
  450. lpwDestFileName);
  451. }
  452. //
  453. // Replace the file in the DllCache directory
  454. //
  455. if (!CopyFile(lpwSourceFileName, wszDllCachePath, FALSE)) {
  456. Print(ERROR,
  457. L"[UninstallWFPFile] Failed to copy %s to %s\n",
  458. lpwSourceFileName, wszDllCachePath);
  459. goto cleanup;
  460. return FALSE;
  461. }
  462. }
  463. //
  464. // This is semi-custom, but because some of the
  465. // WFPs will be in use, we'll delay replace them
  466. // and let the Session Manager rename them
  467. //
  468. lpwTempFileName = CopyTempFile(lpwSourceFileName,
  469. g_si.lpwSystem32Directory);
  470. if (NULL == lpwTempFileName) {
  471. Print(ERROR, L"[UninstallWFPFile] Failed to get temp file\n");
  472. goto cleanup;
  473. return FALSE;
  474. }
  475. if (!MoveFileEx(lpwTempFileName,
  476. lpwDestFileNamePath,
  477. MOVEFILE_REPLACE_EXISTING | MOVEFILE_DELAY_UNTIL_REBOOT)) {
  478. Print(ERROR, L"[UninstallWFPFile] Failed to delay replace from %s to %s\n",
  479. lpwTempFileName, lpwDestFileNamePath);
  480. goto cleanup;
  481. return FALSE;
  482. }
  483. cleanup:
  484. if (lpwTempFileName) {
  485. FREE(lpwTempFileName);
  486. }
  487. if (lpwExpandedCachePath) {
  488. FREE(lpwExpandedCachePath);
  489. }
  490. return TRUE;
  491. }
  492. /*++
  493. Routine Description:
  494. Custom worker routine for uninstall
  495. Arguments:
  496. None
  497. Return Value:
  498. None
  499. --*/
  500. void
  501. UninstallCustomWorker()
  502. {
  503. //
  504. // This is where determine if we should remove
  505. // any regsvr32s that we did during install.
  506. // We also remove the $Sources$ directory,
  507. // if necessary.
  508. // Our method of detection is to see if our
  509. // Guid is present under the Active Setup
  510. // key. If not, it's safe to assume that
  511. // no package is currently installed
  512. //
  513. BOOL fReturn = FALSE;
  514. CRegistry creg;
  515. char szActiveSetupKey[MAX_PATH] = "";
  516. WCHAR wszActiveSetupKey[MAX_PATH] = L"";
  517. WCHAR wszSourcesDir[MAX_PATH] = L"";
  518. fReturn = SetupGetLineTextA(NULL,
  519. g_si.hInf,
  520. "Strings",
  521. "ActiveSetupKey",
  522. szActiveSetupKey,
  523. sizeof(szActiveSetupKey),
  524. NULL);
  525. if (!fReturn) {
  526. return;
  527. }
  528. pAnsiToUnicode(szActiveSetupKey, wszActiveSetupKey, MAX_PATH);
  529. fReturn = creg.IsRegistryKeyPresent(HKEY_LOCAL_MACHINE,
  530. wszActiveSetupKey);
  531. if (!fReturn) {
  532. wsprintf(wszSourcesDir, L"%s\\$Sources$", g_si.lpwInstallDirectory);
  533. CommonRemoveDirectoryAndFiles(wszSourcesDir, (PVOID) FALSE, TRUE, FALSE);
  534. CommonRegisterServers(FALSE);
  535. }
  536. return;
  537. }