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.

1680 lines
54 KiB

  1. //---------------------------------------------------------------------------
  2. //
  3. // Copyright (c) Microsoft Corporation
  4. //
  5. // File: instapp.cpp
  6. //
  7. // Installed applications
  8. //
  9. // History:
  10. // 1-18-97 by dli
  11. //------------------------------------------------------------------------
  12. #include "priv.h"
  13. #include "instapp.h"
  14. #include "sccls.h"
  15. #include "util.h"
  16. #ifndef DOWNLEVEL_PLATFORM
  17. #include "findapp.h"
  18. #endif //DOWNLEVEL_PLATFORM
  19. #include "tasks.h"
  20. #include "slowfind.h"
  21. #include "appsize.h"
  22. #include "appwizid.h"
  23. #include "resource.h"
  24. #include "uemapp.h"
  25. const TCHAR c_szInstall[] = TEXT("Software\\Installer\\Products\\%s");
  26. const TCHAR c_szTSInstallMode[] = TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Terminal Server\\Install\\Change User Option");
  27. const TCHAR c_szUpdateInfo[] = TEXT("URLUpdateInfo");
  28. const TCHAR c_szSlowInfoCache[] = TEXT("SlowInfoCache");
  29. const TCHAR c_szRegstrARPCache[] = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\App Management\\ARPCache");
  30. #ifdef WX86
  31. EXTERN_C BOOL bWx86Enabled;
  32. EXTERN_C BOOL bForceX86Env;
  33. #endif
  34. #ifdef WINNT
  35. #ifndef DOWNLEVEL_PLATFORM
  36. #include <tsappcmp.h> // for TermsrvAppInstallMode
  37. #include "scripts.h"
  38. #endif // DOWNLEVEL_PLATFORM
  39. #endif // WINNT
  40. #include <winsta.h> // WinStation* APIs
  41. #include <allproc.h> // TS_COUNTER
  42. #include <msginaexports.h> // ShellIsMultipleUsersEnabled, ShellSwitchUser
  43. #define APPACTION_STANDARD (APPACTION_UNINSTALL | APPACTION_MODIFY | APPACTION_REPAIR)
  44. // overloaded constructor (for legacy apps)
  45. CInstalledApp::CInstalledApp(HKEY hkeySub, LPCTSTR pszKeyName, LPCTSTR pszProduct, LPCTSTR pszUninstall, DWORD dwCIA) : _cRef(1), _dwSource(IA_LEGACY), _dwCIA(dwCIA), _guid(GUID_NULL)
  46. {
  47. DWORD dwType;
  48. ULONG cbModify;
  49. LONG lRet;
  50. ASSERT(IS_VALID_HANDLE(hkeySub, KEY));
  51. ASSERT(_bTriedToFindFolder == FALSE);
  52. TraceAddRef(CInstalledApp, _cRef);
  53. DllAddRef();
  54. TraceMsg(TF_INSTAPP, "(CInstalledApp) Legacy App Created key name = %s, product name = %s, uninstall string = %s",
  55. pszKeyName, pszProduct, pszUninstall);
  56. lstrcpy(_szKeyName, pszKeyName);
  57. InsertSpaceBeforeVersion(_szKeyName, _szCleanedKeyName);
  58. lstrcpy(_szProduct, pszProduct);
  59. #ifdef FULL_DEBUG
  60. if (_dwCIA & CIA_ALT)
  61. {
  62. StrCatBuff(_szProduct, TEXT(" (32-bit)"), ARRAYSIZE(_szProduct));
  63. }
  64. #endif
  65. lstrcpy(_szUninstall, pszUninstall);
  66. DWORD dwActionBlocked = _QueryBlockedActions(hkeySub);
  67. if (dwActionBlocked != 0)
  68. {
  69. // NoRemove, NoModify, or NoRepair has been specified
  70. _dwAction |= APPACTION_STANDARD & (~dwActionBlocked);
  71. }
  72. else
  73. {
  74. // Start with the basics. For legacy apps, we assume they don't distinguish between
  75. // modify and remove functions.
  76. _dwAction |= APPACTION_MODIFYREMOVE;
  77. }
  78. // If there is no "uninstall" key, we could try to find other hints as where
  79. // this app lives, if we could find that hint, as the uninstall process, we could
  80. // just delete that directory and the registry entry.
  81. // What if we find no hints at all? Should we just delete this thing from the
  82. // registry?
  83. if (!(dwActionBlocked & APPACTION_UNINSTALL) && _szUninstall[0])
  84. _dwAction |= APPACTION_UNINSTALL;
  85. // Does this app have an explicit modify path?
  86. cbModify = SIZEOF(_szModifyPath);
  87. lRet = SHQueryValueEx(hkeySub, TEXT("ModifyPath"), 0, &dwType, (PBYTE)_szModifyPath, &cbModify);
  88. if ((ERROR_SUCCESS == lRet) && (TEXT('\0') != _szModifyPath[0]))
  89. {
  90. // Yes; remove the legacy modify/remove combination.
  91. _dwAction &= ~APPACTION_MODIFYREMOVE;
  92. // Does policy prevent this?
  93. if (!(dwActionBlocked & APPACTION_MODIFY))
  94. _dwAction |= APPACTION_MODIFY; // No
  95. }
  96. _GetInstallLocationFromRegistry(hkeySub);
  97. _GetUpdateUrl();
  98. RegCloseKey(hkeySub);
  99. }
  100. // overloaded constructor (for darwin apps)
  101. CInstalledApp::CInstalledApp(LPTSTR pszProductID) : _cRef(1), _dwSource(IA_DARWIN), _guid(GUID_NULL)
  102. {
  103. ASSERT(_bTriedToFindFolder == FALSE);
  104. TraceAddRef(CInstalledApp, _cRef);
  105. DllAddRef();
  106. TraceMsg(TF_INSTAPP, "(CInstalledApp) Darwin app created product name = %s", pszProductID);
  107. lstrcpy(_szProductID, pszProductID);
  108. // Get the information from the ProductId
  109. ULONG cchProduct = ARRAYSIZE(_szProduct);
  110. MsiGetProductInfo(pszProductID, INSTALLPROPERTY_PRODUCTNAME, _szProduct, &cchProduct);
  111. BOOL bMachineAssigned = FALSE;
  112. // For Machine Assigned Darwin Apps, only admins should be allowed
  113. // to modify the app
  114. if (!IsUserAnAdmin())
  115. {
  116. TCHAR szAT[5];
  117. DWORD cchAT = ARRAYSIZE(szAT);
  118. // NOTE: according to chetanp, the first character of szAT should be "0" or "1"
  119. // '0' means it's user assigned, '1' means it's machine assigned
  120. if ((ERROR_SUCCESS == MsiGetProductInfo(pszProductID, INSTALLPROPERTY_ASSIGNMENTTYPE,
  121. szAT, &cchAT))
  122. && (szAT[0] == TEXT('1')))
  123. bMachineAssigned = TRUE;
  124. }
  125. // Query the install state and separate the cases where this app is
  126. // installed on the machine or assigned...
  127. // In the assigned case we allow only Uninstall operation.
  128. if (INSTALLSTATE_ADVERTISED == MsiQueryProductState(pszProductID))
  129. {
  130. _dwAction |= APPACTION_UNINSTALL;
  131. }
  132. else
  133. {
  134. DWORD dwActionBlocked = 0;
  135. HKEY hkeySub = _OpenUninstallRegKey(KEY_READ);
  136. if (hkeySub)
  137. {
  138. dwActionBlocked = _QueryBlockedActions(hkeySub);
  139. _GetInstallLocationFromRegistry(hkeySub);
  140. RegCloseKey(hkeySub);
  141. if (bMachineAssigned)
  142. _dwAction |= APPACTION_REPAIR & (~dwActionBlocked);
  143. else
  144. {
  145. _dwAction |= APPACTION_STANDARD & (~dwActionBlocked);
  146. _GetUpdateUrl();
  147. }
  148. }
  149. }
  150. }
  151. // destructor
  152. CInstalledApp::~CInstalledApp()
  153. {
  154. if (_pszUpdateUrl)
  155. {
  156. ASSERT(_dwSource & IA_DARWIN);
  157. LocalFree(_pszUpdateUrl);
  158. }
  159. DllRelease();
  160. }
  161. // The UpdateUrl info is optional for both Darwin and Legacy apps.
  162. void CInstalledApp::_GetUpdateUrl()
  163. {
  164. TCHAR szInstall[MAX_PATH];
  165. HKEY hkeyInstall;
  166. wnsprintf(szInstall, ARRAYSIZE(szInstall), c_szInstall, _szProductID);
  167. if (RegOpenKeyEx(_MyHkeyRoot(), szInstall, 0, KEY_READ, &hkeyInstall) == ERROR_SUCCESS)
  168. {
  169. ULONG cbUrl;
  170. if (SHQueryValueEx(hkeyInstall, c_szUpdateInfo, NULL, NULL, NULL, &cbUrl) == ERROR_SUCCESS)
  171. {
  172. _pszUpdateUrl = (LPTSTR) LocalAlloc(LPTR, cbUrl);
  173. if (ERROR_SUCCESS != SHQueryValueEx(hkeyInstall, TEXT(""), NULL, NULL, (PBYTE)_pszUpdateUrl, &cbUrl))
  174. {
  175. LocalFree(_pszUpdateUrl);
  176. _pszUpdateUrl = NULL;
  177. }
  178. else
  179. _dwAction |= APPACTION_UPGRADE;
  180. }
  181. RegCloseKey(hkeyInstall);
  182. }
  183. }
  184. // Queries policy restrictions on the action info
  185. DWORD CInstalledApp::_QueryActionBlockInfo(HKEY hkey)
  186. {
  187. DWORD dwRet = 0;
  188. DWORD dwType = 0;
  189. DWORD dwData = 0;
  190. ULONG cbData = SIZEOF(dwData);
  191. if ((ERROR_SUCCESS == SHQueryValueEx(hkey, TEXT("NoRemove"), 0, &dwType, (PBYTE)&dwData, &cbData))
  192. && (dwType == REG_DWORD) && (dwData == 1))
  193. dwRet |= APPACTION_UNINSTALL;
  194. if ((ERROR_SUCCESS == SHQueryValueEx(hkey, TEXT("NoModify"), 0, &dwType, (PBYTE)&dwData, &cbData))
  195. && (dwType == REG_DWORD) && (dwData == 1))
  196. dwRet |= APPACTION_MODIFY;
  197. if ((ERROR_SUCCESS == SHQueryValueEx(hkey, TEXT("NoRepair"), 0, &dwType, (PBYTE)&dwData, &cbData))
  198. && (dwType == REG_DWORD) && (dwData == 1))
  199. dwRet |= APPACTION_REPAIR;
  200. return dwRet;
  201. }
  202. DWORD CInstalledApp::_QueryBlockedActions(HKEY hkey)
  203. {
  204. DWORD dwRet = _QueryActionBlockInfo(hkey);
  205. if (dwRet != APPACTION_STANDARD)
  206. {
  207. HKEY hkeyPolicy = _OpenRelatedRegKey(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer"), KEY_READ, FALSE);
  208. if (hkeyPolicy)
  209. {
  210. dwRet |= _QueryActionBlockInfo(hkeyPolicy);
  211. RegCloseKey(hkeyPolicy);
  212. }
  213. }
  214. return dwRet;
  215. }
  216. void CInstalledApp::_GetInstallLocationFromRegistry(HKEY hkeySub)
  217. {
  218. DWORD dwType;
  219. ULONG cbInstallLocation = SIZEOF(_szInstallLocation);
  220. LONG lRet = SHQueryValueEx(hkeySub, TEXT("InstallLocation"), 0, &dwType, (PBYTE)_szInstallLocation, &cbInstallLocation);
  221. PathUnquoteSpaces(_szInstallLocation);
  222. if (lRet == ERROR_SUCCESS)
  223. {
  224. ASSERT(IS_VALID_STRING_PTR(_szInstallLocation, -1));
  225. _dwAction |= APPACTION_CANGETSIZE;
  226. }
  227. }
  228. HKEY CInstalledApp::_OpenRelatedRegKey(HKEY hkey, LPCTSTR pszRegLoc, REGSAM samDesired, BOOL bCreate)
  229. {
  230. HKEY hkeySub = NULL;
  231. LONG lRet;
  232. TCHAR szRegKey[MAX_PATH];
  233. RIP (pszRegLoc);
  234. // For Darwin apps, use the ProductID as the key name
  235. LPTSTR pszKeyName = (_dwSource & IA_DARWIN) ? _szProductID : _szKeyName;
  236. wnsprintf(szRegKey, ARRAYSIZE(szRegKey), TEXT("%s\\%s"), pszRegLoc, pszKeyName, ARRAYSIZE(szRegKey));
  237. // Open this key in the registry
  238. lRet = RegOpenKeyEx(hkey, szRegKey, 0, samDesired, &hkeySub);
  239. if (bCreate && (lRet == ERROR_FILE_NOT_FOUND))
  240. {
  241. lRet = RegCreateKeyEx(hkey, szRegKey, 0, NULL, REG_OPTION_NON_VOLATILE, samDesired,
  242. NULL, &hkeySub, NULL);
  243. }
  244. if (lRet != ERROR_SUCCESS)
  245. hkeySub = NULL;
  246. return hkeySub;
  247. }
  248. HKEY CInstalledApp::_OpenUninstallRegKey(REGSAM samDesired)
  249. {
  250. LPCTSTR pszSubkey = (_dwCIA & CIA_ALT) ? REGSTR_PATH_ALTUNINSTALL : REGSTR_PATH_UNINSTALL;
  251. return _OpenRelatedRegKey(_MyHkeyRoot(), pszSubkey, samDesired, FALSE);
  252. }
  253. // Helper function to query the registry for legacy app info strings
  254. LPWSTR CInstalledApp::_GetLegacyInfoString(HKEY hkeySub, LPTSTR pszInfoName)
  255. {
  256. DWORD cbSize;
  257. DWORD dwType;
  258. LPWSTR pwszInfo = NULL;
  259. if (SHQueryValueEx(hkeySub, pszInfoName, 0, &dwType, NULL, &cbSize) == ERROR_SUCCESS)
  260. {
  261. LPTSTR pszInfoT = (LPTSTR)LocalAlloc(LPTR, cbSize);
  262. if (pszInfoT && (SHQueryValueEx(hkeySub, pszInfoName, 0, &dwType, (PBYTE)pszInfoT, &cbSize) == ERROR_SUCCESS))
  263. {
  264. if ((dwType == REG_SZ) || (dwType == REG_EXPAND_SZ))
  265. {
  266. if (FAILED(SHStrDup(pszInfoT, &pwszInfo)))
  267. {
  268. ASSERT(pwszInfo == NULL);
  269. }
  270. // For the "DisplayIcon" case, we need to make sure the path of
  271. // the icon actually exists.
  272. if (pwszInfo && !lstrcmp(pszInfoName, TEXT("DisplayIcon")))
  273. {
  274. PathParseIconLocation(pszInfoT);
  275. if (!PathFileExists(pszInfoT))
  276. {
  277. SHFree(pwszInfo);
  278. pwszInfo = NULL;
  279. }
  280. }
  281. }
  282. LocalFree(pszInfoT);
  283. }
  284. }
  285. return pwszInfo;
  286. }
  287. // IShellApps::GetAppInfo
  288. STDMETHODIMP CInstalledApp::GetAppInfo(PAPPINFODATA pai)
  289. {
  290. ASSERT(pai);
  291. if (pai->cbSize != SIZEOF(APPINFODATA))
  292. return E_FAIL;
  293. DWORD dwInfoFlags = pai->dwMask;
  294. pai->dwMask = 0;
  295. // We cache the product name in all cases(Legacy, Darwin, SMS).
  296. if (dwInfoFlags & AIM_DISPLAYNAME)
  297. {
  298. if (SUCCEEDED(SHStrDup(_szProduct, &pai->pszDisplayName)))
  299. pai->dwMask |= AIM_DISPLAYNAME;
  300. }
  301. if (dwInfoFlags & ~AIM_DISPLAYNAME)
  302. {
  303. HKEY hkeySub = _OpenUninstallRegKey(KEY_READ);
  304. if (hkeySub != NULL)
  305. {
  306. const static struct {
  307. DWORD dwBit;
  308. LPTSTR szRegText;
  309. DWORD ibOffset;
  310. } s_rgInitAppInfo[] = {
  311. //
  312. // WARNING: If you add a new field that is not an LPWSTR type,
  313. // revisit the loop below. It only knows about LPWSTR.
  314. //
  315. {AIM_VERSION, TEXT("DisplayVersion"), FIELD_OFFSET(APPINFODATA, pszVersion) },
  316. {AIM_PUBLISHER, TEXT("Publisher"), FIELD_OFFSET(APPINFODATA, pszPublisher) },
  317. {AIM_PRODUCTID, TEXT("ProductID"), FIELD_OFFSET(APPINFODATA, pszProductID) },
  318. {AIM_REGISTEREDOWNER, TEXT("RegOwner"), FIELD_OFFSET(APPINFODATA, pszRegisteredOwner) },
  319. {AIM_REGISTEREDCOMPANY, TEXT("RegCompany"), FIELD_OFFSET(APPINFODATA, pszRegisteredCompany) },
  320. {AIM_SUPPORTURL, TEXT("UrlInfoAbout"), FIELD_OFFSET(APPINFODATA, pszSupportUrl) },
  321. {AIM_SUPPORTTELEPHONE,TEXT("HelpTelephone"), FIELD_OFFSET(APPINFODATA, pszSupportTelephone) },
  322. {AIM_HELPLINK, TEXT("HelpLink"), FIELD_OFFSET(APPINFODATA, pszHelpLink) },
  323. {AIM_INSTALLLOCATION, TEXT("InstallLocation"), FIELD_OFFSET(APPINFODATA, pszInstallLocation) },
  324. {AIM_INSTALLSOURCE, TEXT("InstallSource"), FIELD_OFFSET(APPINFODATA, pszInstallSource) },
  325. {AIM_INSTALLDATE, TEXT("InstallDate"), FIELD_OFFSET(APPINFODATA, pszInstallDate) },
  326. {AIM_CONTACT, TEXT("Contact"), FIELD_OFFSET(APPINFODATA, pszContact) },
  327. {AIM_COMMENTS, TEXT("Comments"), FIELD_OFFSET(APPINFODATA, pszComments) },
  328. {AIM_IMAGE, TEXT("DisplayIcon"), FIELD_OFFSET(APPINFODATA, pszImage) },
  329. {AIM_READMEURL, TEXT("Readme"), FIELD_OFFSET(APPINFODATA, pszReadmeUrl) },
  330. {AIM_UPDATEINFOURL, TEXT("UrlUpdateInfo"), FIELD_OFFSET(APPINFODATA, pszUpdateInfoUrl) },
  331. };
  332. ASSERT(IS_VALID_HANDLE(hkeySub, KEY));
  333. int i;
  334. for (i = 0; i < ARRAYSIZE(s_rgInitAppInfo); i++)
  335. {
  336. if (dwInfoFlags & s_rgInitAppInfo[i].dwBit)
  337. {
  338. LPWSTR pszInfo = _GetLegacyInfoString(hkeySub, s_rgInitAppInfo[i].szRegText);
  339. if (pszInfo)
  340. {
  341. // We are assuming each field is a LPWSTR.
  342. LPBYTE pbField = (LPBYTE)pai + s_rgInitAppInfo[i].ibOffset;
  343. pai->dwMask |= s_rgInitAppInfo[i].dwBit;
  344. *(LPWSTR *)pbField = pszInfo;
  345. }
  346. }
  347. }
  348. // If we want a image path but did not get it, and we are a darwin app
  349. if ((dwInfoFlags & AIM_IMAGE) && !(pai->dwMask & AIM_IMAGE) && (_dwSource & IA_DARWIN))
  350. {
  351. TCHAR szProductIcon[MAX_PATH*2];
  352. DWORD cchProductIcon = ARRAYSIZE(szProductIcon);
  353. // Okay, call Darwin to get the image
  354. if ((ERROR_SUCCESS == MsiGetProductInfo(_szProductID, INSTALLPROPERTY_PRODUCTICON, szProductIcon, &cchProductIcon))
  355. && szProductIcon[0])
  356. {
  357. // Expand any embedded environment strings while copying
  358. // to return buffer.
  359. TCHAR szTemp[1];
  360. int cchExp = ExpandEnvironmentStrings(szProductIcon, szTemp, ARRAYSIZE(szTemp));
  361. pai->pszImage = (TCHAR *)CoTaskMemAlloc(cchExp * sizeof(TCHAR));
  362. if (NULL != pai->pszImage)
  363. {
  364. ExpandEnvironmentStrings(szProductIcon, pai->pszImage, cchExp);
  365. pai->dwMask |= AIM_IMAGE;
  366. }
  367. }
  368. }
  369. RegCloseKey(hkeySub);
  370. }
  371. }
  372. #ifndef DOWNLEVEL_PLATFORM
  373. // Software installation policy settings can override the default display name
  374. // and help link url which are authored into a windows installer package.
  375. if ( (_dwSource & IA_DARWIN) && (dwInfoFlags & (AIM_DISPLAYNAME | AIM_HELPLINK)) )
  376. {
  377. LPWSTR pwszDisplayName = 0;
  378. LPWSTR pwszSupportUrl = 0;
  379. GetLocalManagedApplicationData( _szProductID, &pwszDisplayName, &pwszSupportUrl );
  380. if ( pwszDisplayName && (dwInfoFlags & AIM_DISPLAYNAME) )
  381. {
  382. LPWSTR pwszNewDisplayName;
  383. if ( SUCCEEDED(SHStrDup(pwszDisplayName, &pwszNewDisplayName)) )
  384. {
  385. if ( pai->dwMask & AIM_DISPLAYNAME )
  386. SHFree( pai->pszDisplayName );
  387. pai->pszDisplayName = pwszNewDisplayName;
  388. pai->dwMask |= AIM_DISPLAYNAME;
  389. }
  390. }
  391. if ( pwszSupportUrl && (dwInfoFlags & AIM_HELPLINK) )
  392. {
  393. LPWSTR pwszNewHelpLink;
  394. if ( SUCCEEDED(SHStrDup(pwszSupportUrl, &pwszNewHelpLink)) )
  395. {
  396. if ( pai->dwMask & AIM_HELPLINK )
  397. SHFree( pai->pszHelpLink );
  398. pai->pszHelpLink = pwszNewHelpLink;
  399. pai->dwMask |= AIM_HELPLINK;
  400. }
  401. }
  402. LocalFree( pwszDisplayName );
  403. LocalFree( pwszSupportUrl );
  404. }
  405. #endif // DOWNLEVEL_PLATFORM
  406. TraceMsg(TF_INSTAPP, "(CInstalledApp) GetAppInfo with %x but got %x", dwInfoFlags, pai->dwMask);
  407. return S_OK;
  408. }
  409. // IShellApps::GetPossibleActions
  410. STDMETHODIMP CInstalledApp::GetPossibleActions(DWORD * pdwActions)
  411. {
  412. ASSERT(IS_VALID_WRITE_PTR(pdwActions, DWORD));
  413. *pdwActions = _dwAction;
  414. return S_OK;
  415. }
  416. #ifndef DOWNLEVEL_PLATFORM
  417. /*-------------------------------------------------------------------------
  418. Purpose: This method finds the application folder for this app. If a
  419. possible folder is found, it is stored in the _szInstallLocation
  420. member variable.
  421. Returns TRUE if a possible path is found.
  422. */
  423. BOOL CInstalledApp::_FindAppFolderFromStrings()
  424. {
  425. TraceMsg(TF_INSTAPP, "(CInstalledApp) FindAppFolderFromStrings ---- %s %s %s %s",
  426. _szProduct, _szCleanedKeyName, _szUninstall, _szModifyPath);
  427. // Try to determine from the "installlocation", "uninstall", or "modify"
  428. // regvalues.
  429. // Say we have tried
  430. _bTriedToFindFolder = TRUE;
  431. // First try out the location string, this is most likely to give us some thing
  432. // and probably is the correct location for logo 5 apps.
  433. if (_dwAction & APPACTION_CANGETSIZE)
  434. {
  435. if (!IsValidAppFolderLocation(_szInstallLocation))
  436. {
  437. // We got bad location string from the registry, set it to empty string
  438. _dwAction &= ~APPACTION_CANGETSIZE;
  439. _szInstallLocation[0] = 0;
  440. }
  441. else
  442. // The string from the registry is fine
  443. return TRUE;
  444. }
  445. // We didn't have a location string or failed to get anything from it.
  446. // logo 3 apps are typically this case...
  447. LPTSTR pszShortName = (_dwSource & IA_LEGACY) ? _szCleanedKeyName : NULL;
  448. TCHAR szFolder[MAX_PATH];
  449. // Let's take a look at the uninstall string, 2nd most likely to give hints
  450. if ((_dwAction & APPACTION_UNINSTALL) &&
  451. (ParseInfoString(_szUninstall, _szProduct, pszShortName, szFolder)))
  452. {
  453. // remember this string and set the Action bit to get size
  454. lstrcpy(_szInstallLocation, szFolder);
  455. _dwAction |= APPACTION_CANGETSIZE;
  456. return TRUE;
  457. }
  458. // Now try the modify string
  459. if ((_dwAction & APPACTION_MODIFY) &&
  460. (ParseInfoString(_szModifyPath, _szProduct, pszShortName, szFolder)))
  461. {
  462. // remember this string and set the Action bit to get size
  463. lstrcpy(_szInstallLocation, szFolder);
  464. _dwAction |= APPACTION_CANGETSIZE;
  465. return TRUE;
  466. }
  467. return FALSE;
  468. }
  469. /*-------------------------------------------------------------------------
  470. Purpose: Persists the slow app info under the "uninstall" key in the registry
  471. EX: HKLM\\...\\Uninstall\\Word\\ARPCache
  472. Returns S_OK if successfully saved it to the registry
  473. E_FAIL if failed.
  474. */
  475. HRESULT CInstalledApp::_PersistSlowAppInfo(PSLOWAPPINFO psai)
  476. {
  477. HRESULT hres = E_FAIL;
  478. ASSERT(psai);
  479. HKEY hkeyARPCache = _OpenRelatedRegKey(_MyHkeyRoot(), c_szRegstrARPCache, KEY_SET_VALUE, TRUE);
  480. if (hkeyARPCache)
  481. {
  482. PERSISTSLOWINFO psi = {0};
  483. DWORD dwType = 0;
  484. DWORD cbSize = SIZEOF(psi);
  485. // Read in the old cached info, and try to preserve the DisplayIcon path
  486. // Note if the PERSISTSLOWINFO structure is not what we are looking for, we
  487. // ignore the old icon path.
  488. if ((ERROR_SUCCESS != RegQueryValueEx(hkeyARPCache, c_szSlowInfoCache, 0, &dwType, (LPBYTE)&psi, &cbSize))
  489. || (psi.dwSize != SIZEOF(psi)))
  490. ZeroMemory(&psi, SIZEOF(psi));
  491. psi.dwSize = SIZEOF(psi);
  492. psi.ullSize = psai->ullSize;
  493. psi.ftLastUsed = psai->ftLastUsed;
  494. psi.iTimesUsed = psai->iTimesUsed;
  495. if (!(psi.dwMasks & PERSISTSLOWINFO_IMAGE) && psai->pszImage && psai->pszImage[0])
  496. {
  497. psi.dwMasks |= PERSISTSLOWINFO_IMAGE;
  498. StrCpy(psi.szImage, psai->pszImage);
  499. }
  500. if (RegSetValueEx(hkeyARPCache, c_szSlowInfoCache, 0, REG_BINARY, (LPBYTE)&psi, sizeof(psi)) == ERROR_SUCCESS)
  501. hres = S_OK;
  502. _SetSlowAppInfoChanged(hkeyARPCache, 0);
  503. RegCloseKey(hkeyARPCache);
  504. }
  505. return hres;
  506. }
  507. #endif //DOWNLEVEL_PLATFORM
  508. /*-------------------------------------------------------------------------
  509. Purpose: _SetSlowAppInfoChanged
  510. Set in the registry that this app has been changed.
  511. */
  512. HRESULT CInstalledApp::_SetSlowAppInfoChanged(HKEY hkeyARPCache, DWORD dwValue)
  513. {
  514. HRESULT hres = E_FAIL;
  515. BOOL bNewKey = FALSE;
  516. if (!hkeyARPCache)
  517. {
  518. hkeyARPCache = _OpenRelatedRegKey(_MyHkeyRoot(), c_szRegstrARPCache, KEY_READ, FALSE);
  519. if (hkeyARPCache)
  520. bNewKey = TRUE;
  521. }
  522. if (hkeyARPCache)
  523. {
  524. if (ERROR_SUCCESS == RegSetValueEx(hkeyARPCache, TEXT("Changed"), 0, REG_DWORD, (LPBYTE)&dwValue, sizeof(dwValue)))
  525. hres = S_OK;
  526. if (bNewKey)
  527. RegCloseKey(hkeyARPCache);
  528. }
  529. return hres;
  530. }
  531. // IShellApps::GetSlowAppInfo
  532. /*-------------------------------------------------------------------------
  533. Purpose: IShellApps::_IsSlowAppInfoChanged
  534. Retrieve whether the slow app info has been changed from the registry
  535. */
  536. HRESULT CInstalledApp::_IsSlowAppInfoChanged()
  537. {
  538. HRESULT hres = S_FALSE;
  539. HKEY hkeyARPCache = _OpenRelatedRegKey(_MyHkeyRoot(), c_szRegstrARPCache, KEY_READ, FALSE);
  540. if (hkeyARPCache)
  541. {
  542. DWORD dwValue;
  543. DWORD dwType;
  544. DWORD cbSize = SIZEOF(dwValue);
  545. if (ERROR_SUCCESS == SHQueryValueEx(hkeyARPCache, TEXT("Changed"), 0, &dwType, &dwValue, &cbSize)
  546. && (dwType == REG_DWORD) && (dwValue == 1))
  547. hres = S_OK;
  548. RegCloseKey(hkeyARPCache);
  549. }
  550. else
  551. hres = S_OK;
  552. return hres;
  553. }
  554. BOOL CInstalledApp::_GetDarwinAppSize(ULONGLONG * pullTotal)
  555. {
  556. BOOL bRet = FALSE;
  557. HKEY hkeySub = _OpenUninstallRegKey(KEY_READ);
  558. RIP(pullTotal);
  559. *pullTotal = 0;
  560. if (hkeySub)
  561. {
  562. DWORD dwSize = 0;
  563. DWORD dwType = 0;
  564. DWORD cbSize = SIZEOF(dwSize);
  565. if (ERROR_SUCCESS == SHQueryValueEx(hkeySub, TEXT("EstimatedSize"), 0, &dwType, &dwSize, &cbSize)
  566. && (dwType == REG_DWORD))
  567. {
  568. // NOTE: EstimatedSize is in "kb"
  569. *pullTotal = dwSize * 1024;
  570. bRet = TRUE;
  571. }
  572. RegCloseKey(hkeySub);
  573. }
  574. return bRet;
  575. }
  576. // IShellApps::GetSlowAppInfo
  577. /*-------------------------------------------------------------------------
  578. Purpose: IShellApps::GetSlowAppInfo
  579. Gets the appinfo that may take awhile. This includes the amount
  580. of diskspace that the app might take up, etc.
  581. Returns S_OK if some valid info was obtained. S_FALSE is returned
  582. if nothing useful was found. Errors may be returned as well.
  583. */
  584. STDMETHODIMP CInstalledApp::GetSlowAppInfo(PSLOWAPPINFO psai)
  585. {
  586. #ifndef DOWNLEVEL_PLATFORM
  587. HRESULT hres = E_INVALIDARG;
  588. if (psai)
  589. {
  590. // Is this an app that we know we can't get info for?
  591. // In this case this is a darwin app that has not changed
  592. BOOL bFoundFolder = FALSE;
  593. LPCTSTR pszShortName = NULL;
  594. BOOL bSlowAppInfoChanged = (S_OK == _IsSlowAppInfoChanged());
  595. // Nothing should have changed except for the usage info, so get the cached one first
  596. if (FAILED(GetCachedSlowAppInfo(psai)))
  597. {
  598. ZeroMemory(psai, sizeof(*psai));
  599. psai->iTimesUsed = -1;
  600. psai->ullSize = (ULONGLONG) -1;
  601. }
  602. // No; have we tried to determine this app's installation location?
  603. switch (_dwSource) {
  604. case IA_LEGACY:
  605. {
  606. if (!_bTriedToFindFolder)
  607. {
  608. // No; try to find out now
  609. BOOL bRet = _FindAppFolderFromStrings();
  610. if (bRet)
  611. TraceMsg(TF_ALWAYS, "(CInstalledApp) App Folder Found %s --- %s", _szProduct, _szInstallLocation);
  612. else
  613. {
  614. ASSERT(!(_dwAction & APPACTION_CANGETSIZE));
  615. ASSERT(_szInstallLocation[0] == 0);
  616. }
  617. }
  618. pszShortName = _szCleanedKeyName;
  619. bFoundFolder = _dwAction & APPACTION_CANGETSIZE;
  620. if (!bFoundFolder)
  621. bFoundFolder = SlowFindAppFolder(_szProduct, pszShortName, _szInstallLocation);
  622. }
  623. break;
  624. case IA_DARWIN:
  625. {
  626. if (bSlowAppInfoChanged)
  627. {
  628. // Can we get the Darwin app size?
  629. if (!_GetDarwinAppSize(&psai->ullSize))
  630. // No, let's set it back to the default value
  631. psai->ullSize = (ULONGLONG) -1;
  632. }
  633. // Get the "times used" info from UEM
  634. UEMINFO uei = {0};
  635. uei.cbSize = SIZEOF(uei);
  636. uei.dwMask = UEIM_HIT | UEIM_FILETIME;
  637. if(SUCCEEDED(UEMQueryEvent(&UEMIID_SHELL, UEME_RUNPATH, (WPARAM)-1, (LPARAM)_szProductID, &uei)))
  638. {
  639. // Is there a change to the times used?
  640. if (uei.cHit > psai->iTimesUsed)
  641. {
  642. // Yes, then overwrite the times used field
  643. psai->iTimesUsed = uei.cHit;
  644. }
  645. if (CompareFileTime(&(uei.ftExecute), &psai->ftLastUsed) > 0)
  646. psai->ftLastUsed = uei.ftExecute;
  647. }
  648. }
  649. break;
  650. default:
  651. break;
  652. }
  653. LPCTSTR pszInstallLocation = bFoundFolder ? _szInstallLocation : NULL;
  654. hres = FindAppInfo(pszInstallLocation, _szProduct, pszShortName, psai, bSlowAppInfoChanged);
  655. _PersistSlowAppInfo(psai);
  656. }
  657. #else
  658. HRESULT hres = E_NOTIMPL;
  659. #endif //DOWNLEVEL_PLATFORM
  660. return hres;
  661. }
  662. // IShellApps::GetCachedSlowAppInfo
  663. /*-------------------------------------------------------------------------
  664. Purpose: IShellApps::GetCachedSlowAppInfo
  665. Gets the cached appinfo, to get the real info might take a while
  666. Returns S_OK if some valid info was obtained.
  667. Returns E_FAIL if can't find the cached info.
  668. */
  669. STDMETHODIMP CInstalledApp::GetCachedSlowAppInfo(PSLOWAPPINFO psai)
  670. {
  671. #ifndef DOWNLEVEL_PLATFORM
  672. HRESULT hres = E_FAIL;
  673. if (psai)
  674. {
  675. ZeroMemory(psai, sizeof(*psai));
  676. HKEY hkeyARPCache = _OpenRelatedRegKey(_MyHkeyRoot(), c_szRegstrARPCache, KEY_READ, FALSE);
  677. if (hkeyARPCache)
  678. {
  679. PERSISTSLOWINFO psi = {0};
  680. DWORD dwType;
  681. DWORD cbSize = SIZEOF(psi);
  682. if ((RegQueryValueEx(hkeyARPCache, c_szSlowInfoCache, 0, &dwType, (LPBYTE)&psi, &cbSize) == ERROR_SUCCESS)
  683. && (psi.dwSize == SIZEOF(psi)))
  684. {
  685. psai->ullSize = psi.ullSize;
  686. psai->ftLastUsed = psi.ftLastUsed;
  687. psai->iTimesUsed = psi.iTimesUsed;
  688. if (psi.dwMasks & PERSISTSLOWINFO_IMAGE)
  689. SHStrDupW(psi.szImage, &psai->pszImage);
  690. hres = S_OK;
  691. }
  692. RegCloseKey(hkeyARPCache);
  693. }
  694. }
  695. #else
  696. HRESULT hres = E_NOTIMPL;
  697. #endif //DOWNLEVEL_PLATFORM
  698. return hres;
  699. }
  700. // IShellApp::IsInstalled
  701. STDMETHODIMP CInstalledApp::IsInstalled()
  702. {
  703. HRESULT hres = S_FALSE;
  704. switch (_dwSource)
  705. {
  706. case IA_LEGACY:
  707. {
  708. // First Let's see if the reg key is still there
  709. HKEY hkey = _OpenUninstallRegKey(KEY_READ);
  710. if (hkey)
  711. {
  712. // Second we check the "DisplayName" and the "UninstallString"
  713. LPWSTR pszName = _GetLegacyInfoString(hkey, REGSTR_VAL_UNINSTALLER_DISPLAYNAME);
  714. if (pszName)
  715. {
  716. if (pszName[0])
  717. {
  718. LPWSTR pszUninstall = _GetLegacyInfoString(hkey, REGSTR_VAL_UNINSTALLER_COMMANDLINE);
  719. if (pszUninstall)
  720. {
  721. if (pszUninstall[0])
  722. hres = S_OK;
  723. SHFree(pszUninstall);
  724. }
  725. }
  726. SHFree(pszName);
  727. }
  728. RegCloseKey(hkey);
  729. }
  730. }
  731. break;
  732. case IA_DARWIN:
  733. if (MsiQueryProductState(_szProductID) == INSTALLSTATE_DEFAULT)
  734. hres = S_OK;
  735. break;
  736. case IA_SMS:
  737. break;
  738. default:
  739. break;
  740. }
  741. return hres;
  742. }
  743. #ifdef DOWNLEVEL_PLATFORM
  744. STDAPI_(BOOL) Old_CreateAndWaitForProcess(LPTSTR pszExeName)
  745. {
  746. PROCESS_INFORMATION pi = {0};
  747. STARTUPINFO si = {0};
  748. BOOL fWorked = FALSE;
  749. #ifdef WX86
  750. DWORD cchArch;
  751. WCHAR szArchValue[32];
  752. #endif
  753. DWORD dwCreationFlags = 0;
  754. // Create the install process
  755. si.cb = sizeof(si);
  756. #ifdef WX86
  757. if (bWx86Enabled && bForceX86Env) {
  758. cchArch = GetEnvironmentVariableW(ProcArchName,
  759. szArchValue,
  760. sizeof(szArchValue)
  761. );
  762. if (!cchArch || cchArch >= sizeof(szArchValue)) {
  763. szArchValue[0]=L'\0';
  764. }
  765. SetEnvironmentVariableW(ProcArchName, L"x86");
  766. }
  767. #endif
  768. // Create the process
  769. fWorked = CreateProcess(NULL, pszExeName, NULL, NULL, FALSE, dwCreationFlags, NULL, NULL,
  770. &si, &pi);
  771. if (fWorked)
  772. {
  773. //
  774. // Wait for the install to finish.
  775. //
  776. HANDLE rghWait[1];
  777. rghWait[0] = pi.hProcess;
  778. DWORD dwWaitRet;
  779. #ifdef WX86
  780. if (ForceWx86) {
  781. SetEnvironmentVariableW(ProcArchName, ProcArchValue);
  782. }
  783. #endif
  784. do {
  785. dwWaitRet = MsgWaitForMultipleObjects(1, rghWait, FALSE, INFINITE, QS_ALLINPUT);
  786. if (dwWaitRet == WAIT_OBJECT_0 + 1) {
  787. // block-local variable
  788. MSG msg ;
  789. // read all of the messages in this next loop
  790. // removing each message as we read it
  791. while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
  792. TranslateMessage(&msg);
  793. DispatchMessage(&msg);
  794. } // end of PeekMessage while loop
  795. }
  796. } while ((dwWaitRet != WAIT_OBJECT_0) && (dwWaitRet != WAIT_ABANDONED_0));
  797. CloseHandle(pi.hProcess);
  798. CloseHandle(pi.hThread);
  799. }
  800. return fWorked;
  801. }
  802. #endif // DOWNLEVEL_PLATFORM
  803. /*-------------------------------------------------------------------------
  804. Purpose: Creates a process and waits for it to finish
  805. */
  806. STDAPI_(BOOL) CreateAndWaitForProcess(LPTSTR pszExeName)
  807. {
  808. #if defined(WINNT) && !defined(DOWNLEVEL_PLATFORM)
  809. return NT5_CreateAndWaitForProcess(pszExeName);
  810. #else
  811. return Old_CreateAndWaitForProcess(pszExeName);
  812. #endif
  813. }
  814. // Returns FALSE if "pszPath" contains a network app that can not be accessed
  815. // TRUE for all other pathes
  816. BOOL PathIsNetAndCreatable(LPCTSTR pszPath, LPTSTR pszErrExe, UINT cchErrExe)
  817. {
  818. ASSERT(IS_VALID_STRING_PTR(pszPath, -1));
  819. BOOL bRet = TRUE;
  820. TCHAR szExe[MAX_PATH];
  821. lstrcpyn(szExe, pszPath, ARRAYSIZE(szExe));
  822. LPTSTR pSpace = PathGetArgs(szExe);
  823. if (pSpace)
  824. *pSpace = 0;
  825. if (!PathIsLocalAndFixed(szExe))
  826. bRet = PathFileExists(szExe);
  827. if (!bRet)
  828. lstrcpyn(pszErrExe, szExe, cchErrExe);
  829. return bRet;
  830. }
  831. EXTERN_C BOOL BrowseForExe(HWND hwnd, LPTSTR pszName, DWORD cchName,
  832. LPCTSTR pszInitDir);
  833. /*--------------------------------------------------------------------------*
  834. *--------------------------------------------------------------------------*/
  835. BOOL_PTR CALLBACK NewUninstallProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)
  836. {
  837. LPTSTR pszExe = (LPTSTR) GetWindowLongPtr(hDlg, DWLP_USER);
  838. switch (msg)
  839. {
  840. case WM_INITDIALOG:
  841. RIP (lp);
  842. if (lp != NULL)
  843. {
  844. pszExe = (LPTSTR)lp;
  845. SetWindowText(GetDlgItem(hDlg, IDC_TEXT), pszExe);
  846. pszExe[0] = 0;
  847. SetWindowLongPtr(hDlg, DWLP_USER, lp);
  848. }
  849. else
  850. EndDialog(hDlg, -1);
  851. break;
  852. case WM_COMMAND:
  853. ASSERT(pszExe);
  854. RIP (lp);
  855. switch (GET_WM_COMMAND_ID(wp, lp))
  856. {
  857. case IDC_BROWSE:
  858. if (BrowseForExe(hDlg, pszExe, MAX_PATH, NULL))
  859. Edit_SetText(GetDlgItem(hDlg, IDC_COMMAND), pszExe);
  860. break;
  861. case IDOK:
  862. // NOTE: we are assuming the size of the buffer is at least MAX_PATH
  863. GetDlgItemText(hDlg, IDC_COMMAND, pszExe, MAX_PATH);
  864. case IDCANCEL:
  865. EndDialog(hDlg, (GET_WM_COMMAND_ID(wp, lp) == IDOK));
  866. break;
  867. default:
  868. return FALSE;
  869. }
  870. break;
  871. default:
  872. return FALSE;
  873. }
  874. return TRUE;
  875. }
  876. // Assumes pszExePath is of size MAX_PATH
  877. int GetNewUninstallProgram(HWND hwndParent, LPTSTR pszExePath, DWORD cchExePath)
  878. {
  879. int iRet = 0;
  880. RIP(pszExePath);
  881. if (cchExePath >= MAX_PATH)
  882. {
  883. iRet = (int)DialogBoxParam(g_hinst, MAKEINTRESOURCE(DLG_UNCUNINSTALLBROWSE),
  884. hwndParent, NewUninstallProc, (LPARAM)(int *)pszExePath);
  885. }
  886. return iRet;
  887. }
  888. // CreateProcess the app modification of uninstall process
  889. BOOL CInstalledApp::_CreateAppModifyProcessNative(HWND hwndParent, LPTSTR pszExePath)
  890. {
  891. BOOL bRet = FALSE;
  892. TCHAR szModifiedExePath[MAX_PATH + MAX_INFO_STRING];
  893. #ifndef DOWNLEVEL_PLATFORM
  894. // PPCF_LONGESTPOSSIBLE does not exist on down level platforms
  895. if (0 >= PathProcessCommand(pszExePath, szModifiedExePath,
  896. ARRAYSIZE(szModifiedExePath), PPCF_ADDQUOTES | PPCF_NODIRECTORIES | PPCF_LONGESTPOSSIBLE))
  897. #endif
  898. lstrcpy(szModifiedExePath,pszExePath);
  899. TCHAR szErrExe[MAX_PATH];
  900. if (!PathIsNetAndCreatable(szModifiedExePath, szErrExe, ARRAYSIZE(szErrExe)))
  901. {
  902. TCHAR szExplain[MAX_PATH];
  903. LoadString(g_hinst, IDS_UNINSTALL_UNCUNACCESSIBLE, szExplain, ARRAYSIZE(szExplain));
  904. wnsprintf(szModifiedExePath, ARRAYSIZE(szModifiedExePath), szExplain, _szProduct, szErrExe, ARRAYSIZE(szModifiedExePath));
  905. if (!GetNewUninstallProgram(hwndParent, szModifiedExePath, ARRAYSIZE(szModifiedExePath)))
  906. return FALSE;
  907. }
  908. bRet = CreateAndWaitForProcess(szModifiedExePath);
  909. if (!bRet)
  910. {
  911. if (ShellMessageBox( HINST_THISDLL, hwndParent, MAKEINTRESOURCE( IDS_UNINSTALL_FAILED ),
  912. MAKEINTRESOURCE( IDS_UNINSTALL_ERROR ),
  913. MB_YESNO | MB_ICONEXCLAMATION, _szProduct, _szProduct) == IDYES)
  914. {
  915. // If we are unable to uninstall the app, give the user the option of removing
  916. // it from the Add/Remove programs list. Note that we only know an uninstall
  917. // has failed if we are unable to execute its command line in the registry. This
  918. // won't cover all possible failed uninstalls. InstallShield, for instance, passes
  919. // an uninstall path to a generic C:\WINDOWS\UNINST.EXE application. If an
  920. // InstallShield app has been blown away, UNINST will still launch sucessfully, but
  921. // will bomb out when it can't find the path, and we have no way of knowing it failed
  922. // because it always returns an exit code of zero.
  923. // A future work item (which I doubt will ever be done) would be to investigate
  924. // various installer apps and see if any of them do return error codes that we could
  925. // use to be better at detecting failure cases.
  926. HKEY hkUninstall;
  927. if (RegOpenKey(_MyHkeyRoot(), REGSTR_PATH_UNINSTALL, &hkUninstall) == ERROR_SUCCESS)
  928. {
  929. if (ERROR_SUCCESS == SHDeleteKey(hkUninstall, _szKeyName))
  930. bRet = TRUE;
  931. else
  932. {
  933. ShellMessageBox( HINST_THISDLL, hwndParent, MAKEINTRESOURCE( IDS_CANT_REMOVE_FROM_REGISTRY ),
  934. MAKEINTRESOURCE( IDS_UNINSTALL_ERROR ),
  935. MB_OK | MB_ICONEXCLAMATION, _szProduct);
  936. }
  937. RegCloseKey(hkUninstall);
  938. }
  939. }
  940. }
  941. return bRet;
  942. }
  943. // CreateProcess the app modification of uninstall process
  944. BOOL CInstalledApp::_CreateAppModifyProcess(HWND hwndParent, DWORD dwCAMP)
  945. {
  946. if (_dwCIA & CIA_ALT)
  947. {
  948. return _CreateAppModifyProcessWow64(hwndParent, dwCAMP);
  949. }
  950. else
  951. {
  952. switch (dwCAMP)
  953. {
  954. case CAMP_UNINSTALL:
  955. return _CreateAppModifyProcessNative(hwndParent, _szUninstall);
  956. case CAMP_MODIFY:
  957. return _CreateAppModifyProcessNative(hwndParent, _szModifyPath);
  958. }
  959. return FALSE;
  960. }
  961. }
  962. //
  963. // Command line to the rundll32 is
  964. //
  965. // %SystemRoot%\SysWOW64\rundll32.exe %SystemRoot%\SysWOW64\appwiz.cpl,
  966. // WOW64Uninstall_RunDLL,<hwnd>,<CIA>,<CAMP>,<KeyName>
  967. //
  968. // The KeyName must come last because it might contain a comma.
  969. //
  970. //
  971. BOOL CInstalledApp::_CreateAppModifyProcessWow64(HWND hwndParent, DWORD dwCAMP)
  972. {
  973. TCHAR szSysWow64[MAX_PATH];
  974. TCHAR szRundll32[MAX_PATH];
  975. TCHAR szCmdLine[MAX_PATH * 2];
  976. BOOL fSuccess = FALSE;
  977. if (GetWindowsDirectory(szSysWow64, ARRAYSIZE(szSysWow64)) &&
  978. PathAppend(szSysWow64, TEXT("SysWOW64")))
  979. {
  980. wnsprintf(szRundll32, ARRAYSIZE(szRundll32), TEXT("%s\\rundll32.exe"), szSysWow64);
  981. wnsprintf(szCmdLine, ARRAYSIZE(szCmdLine),
  982. TEXT("\"%s\" \"%s\\appwiz.cpl\",WOW64Uninstall_RunDLL %d,%d,%d,%s"),
  983. szRundll32, szSysWow64, hwndParent, _dwCIA, dwCAMP, _szKeyName);
  984. STARTUPINFO si = { 0 };
  985. si.cb = sizeof(si);
  986. PROCESS_INFORMATION pi;
  987. if (CreateProcess(szRundll32, szCmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
  988. {
  989. SHProcessMessagesUntilEvent(NULL, pi.hProcess, INFINITE);
  990. DWORD dwExitCode;
  991. if (GetExitCodeProcess(pi.hProcess, &dwExitCode) && dwExitCode == 0)
  992. {
  993. fSuccess = TRUE;
  994. }
  995. CloseHandle(pi.hProcess);
  996. CloseHandle(pi.hThread);
  997. }
  998. }
  999. return fSuccess;
  1000. }
  1001. // Helper function for command line parsing...
  1002. int _ParseCmdLineIntegerAndComma(LPWSTR *ppwsz)
  1003. {
  1004. LPWSTR psz = *ppwsz;
  1005. if (!psz)
  1006. {
  1007. return -1;
  1008. }
  1009. int i = StrToInt(psz);
  1010. psz = StrChr(psz, TEXT(','));
  1011. if (!psz)
  1012. {
  1013. *ppwsz = NULL;
  1014. return -1;
  1015. }
  1016. *ppwsz = psz + 1;
  1017. return i;
  1018. }
  1019. //
  1020. // Special export that the 64-bit version of appwiz uses to force an app
  1021. // uninstaller to run in 32-bit mode.
  1022. //
  1023. // Command line arguments are as described above.
  1024. STDAPI_(void) WOW64Uninstall_RunDLLW(HWND hwnd, HINSTANCE hAppInstance, LPWSTR lpszCmdLine, int nCmdShow)
  1025. {
  1026. BOOL fSuccess = FALSE;
  1027. HWND hwndParent = (HWND)IntToPtr(_ParseCmdLineIntegerAndComma(&lpszCmdLine));
  1028. int dwCIA = _ParseCmdLineIntegerAndComma(&lpszCmdLine);
  1029. int dwCAMP = _ParseCmdLineIntegerAndComma(&lpszCmdLine);
  1030. if (lpszCmdLine && *lpszCmdLine)
  1031. {
  1032. dwCIA &= ~CIA_ALT; // We *are* the alternate platform
  1033. HKEY hkRoot = (dwCIA & CIA_CU) ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
  1034. HKEY hkSub;
  1035. TCHAR szBuf[MAX_PATH];
  1036. TCHAR szName[MAX_PATH];
  1037. // Note: This is running on the 32-bit side so we don't use ALT
  1038. wnsprintf(szBuf, ARRAYSIZE(szBuf), TEXT("%s\\%s"), REGSTR_PATH_UNINSTALL, lpszCmdLine);
  1039. if (ERROR_SUCCESS == RegOpenKeyEx(hkRoot, szBuf, 0, KEY_READ, &hkSub))
  1040. {
  1041. DWORD cb;
  1042. szBuf[0] = 0;
  1043. cb = SIZEOF(szBuf);
  1044. SHQueryValueEx(hkSub, REGSTR_VAL_UNINSTALLER_COMMANDLINE, 0, NULL, (PBYTE)szBuf, &cb);
  1045. szName[0] = 0;
  1046. cb = SIZEOF(szName);
  1047. SHQueryValueEx(hkSub, REGSTR_VAL_UNINSTALLER_DISPLAYNAME, 0, NULL, (PBYTE)szName, &cb);
  1048. CInstalledApp * pia = new CInstalledApp(hkSub, lpszCmdLine, szName, szBuf, dwCIA);
  1049. if (pia)
  1050. {
  1051. fSuccess = pia->_CreateAppModifyProcess(hwndParent, dwCAMP);
  1052. pia->Release();
  1053. }
  1054. RegCloseKey(hkSub);
  1055. }
  1056. }
  1057. // Let my parent regain foreground activation now that I'm finished
  1058. DWORD dwPid;
  1059. if (GetWindowThreadProcessId(hwndParent, &dwPid))
  1060. {
  1061. AllowSetForegroundWindow(dwPid);
  1062. }
  1063. // Return 0 on success, 1 on failure (exit codes are like that)
  1064. ExitProcess(!fSuccess);
  1065. }
  1066. // Uinstalls legacy apps
  1067. BOOL CInstalledApp::_LegacyUninstall(HWND hwndParent)
  1068. {
  1069. LPVOID pAppScripts = ScriptManagerInitScripts();
  1070. BOOL bRet = FALSE;
  1071. if (_dwAction & APPACTION_UNINSTALL)
  1072. bRet = _CreateAppModifyProcess(hwndParent, CAMP_UNINSTALL);
  1073. if(pAppScripts)
  1074. {
  1075. ScriptManagerRunScripts(&pAppScripts);
  1076. }
  1077. return bRet;
  1078. }
  1079. #ifdef WINNT
  1080. #ifndef DOWNLEVEL_PLATFORM
  1081. DWORD _QueryTSInstallMode(LPTSTR pszKeyName)
  1082. {
  1083. // NOTE: Terminal Server guys confirmed this, when this value is 0, it means
  1084. // we were installed in install mode. 1 means not installed in "Install Mode"
  1085. // Set default to "install mode"
  1086. DWORD dwVal = 0;
  1087. DWORD dwValSize = SIZEOF(dwVal);
  1088. if (ERROR_SUCCESS != SHGetValue(HKEY_LOCAL_MACHINE, c_szTSInstallMode, pszKeyName,
  1089. NULL, &dwVal, &dwValSize))
  1090. {
  1091. dwVal = 0;
  1092. }
  1093. return dwVal;
  1094. }
  1095. #endif // DOWNLEVEL_PLATFORM
  1096. #endif // WINNT
  1097. BOOL_PTR CALLBACK _MultiUserWarningProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)
  1098. {
  1099. switch (msg)
  1100. {
  1101. case WM_INITDIALOG:
  1102. SendDlgItemMessage(hDlg, IDC_ICON_WARNING, STM_SETICON, (WPARAM)LoadIcon(NULL, IDI_WARNING), 0);
  1103. return TRUE;
  1104. case WM_COMMAND:
  1105. switch (GET_WM_COMMAND_ID(wp, lp))
  1106. {
  1107. case IDC_SWITCHUSER:
  1108. ShellSwitchUser(FALSE);
  1109. EndDialog(hDlg, IDCANCEL);
  1110. break;
  1111. case IDOK:
  1112. case IDCANCEL:
  1113. EndDialog(hDlg, GET_WM_COMMAND_ID(wp, lp));
  1114. break;
  1115. }
  1116. break;
  1117. }
  1118. return FALSE;
  1119. }
  1120. int _GetLoggedOnUserCount(void)
  1121. {
  1122. int iCount = 0;
  1123. HANDLE hServer;
  1124. // Open a connection to terminal services and get the number of sessions.
  1125. hServer = WinStationOpenServerW(reinterpret_cast<WCHAR*>(SERVERNAME_CURRENT));
  1126. if (hServer != NULL)
  1127. {
  1128. TS_COUNTER tsCounters[2] = {0};
  1129. tsCounters[0].counterHead.dwCounterID = TERMSRV_CURRENT_DISC_SESSIONS;
  1130. tsCounters[1].counterHead.dwCounterID = TERMSRV_CURRENT_ACTIVE_SESSIONS;
  1131. if (WinStationGetTermSrvCountersValue(hServer, ARRAYSIZE(tsCounters), tsCounters))
  1132. {
  1133. int i;
  1134. for (i = 0; i < ARRAYSIZE(tsCounters); i++)
  1135. {
  1136. if (tsCounters[i].counterHead.bResult)
  1137. {
  1138. iCount += tsCounters[i].dwValue;
  1139. }
  1140. }
  1141. }
  1142. WinStationCloseServer(hServer);
  1143. }
  1144. return iCount;
  1145. }
  1146. int _ShowMultiUserWarning(HWND hwndParent)
  1147. {
  1148. int iRet = IDOK;
  1149. if (ShellIsMultipleUsersEnabled() && _GetLoggedOnUserCount() > 1)
  1150. {
  1151. iRet = (int)DialogBoxParam(g_hinst, MAKEINTRESOURCE(DLG_MULTIUSERWARNING),
  1152. hwndParent, _MultiUserWarningProc, 0);
  1153. }
  1154. return iRet;
  1155. }
  1156. // IInstalledApps::Uninstall
  1157. STDMETHODIMP CInstalledApp::Uninstall(HWND hwndParent)
  1158. {
  1159. HRESULT hres = E_FAIL;
  1160. if (!_IsAppFastUserSwitchingCompliant() && (IDOK != _ShowMultiUserWarning(hwndParent)))
  1161. return hres;
  1162. #ifdef WINNT
  1163. #ifndef DOWNLEVEL_PLATFORM
  1164. // Default to turn install mode off (1 is off)
  1165. DWORD dwTSInstallMode = 1;
  1166. BOOL bPrevMode = FALSE;
  1167. if (IsTerminalServicesRunning())
  1168. {
  1169. // On NT, let Terminal Services know that we are about to uninstall an application.
  1170. dwTSInstallMode = _QueryTSInstallMode((_dwSource & IA_DARWIN) ? _szProductID : _szKeyName);
  1171. if (dwTSInstallMode == 0)
  1172. {
  1173. bPrevMode = TermsrvAppInstallMode();
  1174. SetTermsrvAppInstallMode(TRUE);
  1175. }
  1176. }
  1177. #endif // DOWNLEVEL_PLATFORM
  1178. #endif // WINNT
  1179. switch (_dwSource)
  1180. {
  1181. case IA_LEGACY:
  1182. if (_LegacyUninstall(hwndParent))
  1183. hres = S_OK;
  1184. break;
  1185. case IA_DARWIN:
  1186. {
  1187. TCHAR szFinal[512], szPrompt[256];
  1188. LoadString(g_hinst, IDS_CONFIRM_REMOVE, szPrompt, ARRAYSIZE(szPrompt));
  1189. wnsprintf(szFinal, ARRAYSIZE(szFinal), szPrompt, _szProduct);
  1190. if (ShellMessageBox(g_hinst, hwndParent, szFinal, MAKEINTRESOURCE(IDS_NAME),
  1191. MB_YESNO | MB_ICONQUESTION, _szProduct, _szProduct) == IDYES)
  1192. {
  1193. LONG lRet;
  1194. INSTALLUILEVEL OldUI = MsiSetInternalUI(INSTALLUILEVEL_BASIC, NULL);
  1195. lRet = MsiConfigureProduct(_szProductID, INSTALLLEVEL_DEFAULT, INSTALLSTATE_ABSENT);
  1196. MsiSetInternalUI(OldUI, NULL);
  1197. hres = HRESULT_FROM_WIN32(lRet);
  1198. // Is this an ophaned assigned app? If so, say we succeeded and call
  1199. // Class Store to remove it.
  1200. // REARCHITECT: This is too Class Store specific, what if the app is from
  1201. // SMS?
  1202. if ((lRet == ERROR_INSTALL_SOURCE_ABSENT) &&
  1203. (INSTALLSTATE_ADVERTISED == MsiQueryProductState(_szProductID)))
  1204. {
  1205. hres = S_OK;
  1206. lRet = ERROR_SUCCESS;
  1207. }
  1208. // Tell the software installation service we are uninstalling a Darwin app
  1209. // NOTE: We call this function for every Darwin app, which is not right because
  1210. // some darwin apps could be from a different source, such as SMS, we need a better
  1211. // way to do this.
  1212. // We call this regardless of failure or success -- this is needed so that
  1213. // RSoP can record both success and failure status for this uninstall
  1214. WCHAR wszProductID[GUIDSTR_MAX];
  1215. #ifdef UNICODE
  1216. StrCpy(wszProductID, _szProductID);
  1217. #else
  1218. SHTCharToUnicode(_szProductID, wszProductID, ARRAYSIZE(wszProductID));
  1219. #endif
  1220. UninstallApplication(
  1221. wszProductID,
  1222. lRet);
  1223. if (FAILED(hres))
  1224. {
  1225. _ARPErrorMessageBox(lRet);
  1226. }
  1227. }
  1228. else
  1229. {
  1230. hres = E_ABORT; // works for user cancelled
  1231. }
  1232. break;
  1233. }
  1234. case IA_SMS:
  1235. break;
  1236. default:
  1237. break;
  1238. }
  1239. // Get rid of the ARP Cache for this app.
  1240. if (SUCCEEDED(hres))
  1241. {
  1242. HKEY hkeyARPCache;
  1243. if (ERROR_SUCCESS == RegOpenKey(_MyHkeyRoot(), c_szRegstrARPCache, &hkeyARPCache))
  1244. {
  1245. LPTSTR pszKeyName = (_dwSource & IA_DARWIN) ? _szProductID : _szKeyName;
  1246. SHDeleteKey(hkeyARPCache, pszKeyName);
  1247. RegCloseKey(hkeyARPCache);
  1248. }
  1249. }
  1250. #ifdef WINNT
  1251. #ifndef DOWNLEVEL_PLATFORM
  1252. if (dwTSInstallMode == 0)
  1253. SetTermsrvAppInstallMode(bPrevMode);
  1254. #endif // DOWNLEVEL_PLATFORM
  1255. #endif // WINNT
  1256. return hres;
  1257. }
  1258. BOOL CInstalledApp::_LegacyModify(HWND hwndParent)
  1259. {
  1260. ASSERT(_dwAction & APPACTION_MODIFY);
  1261. ASSERT(_dwSource & (IA_LEGACY | IA_DARWIN));
  1262. // ASSERT(IS_VALID_STRING_PTR(_szProductID, 39));
  1263. return _CreateAppModifyProcess(hwndParent, CAMP_MODIFY);
  1264. }
  1265. // IInstalledApps::Modify
  1266. STDMETHODIMP CInstalledApp::Modify(HWND hwndParent)
  1267. {
  1268. HRESULT hres = E_FAIL;
  1269. if (!_IsAppFastUserSwitchingCompliant() && (IDOK != _ShowMultiUserWarning(hwndParent)))
  1270. return hres;
  1271. #ifdef WINNT
  1272. #ifndef DOWNLEVEL_PLATFORM
  1273. // On NT, let Terminal Services know that we are about to modify an application.
  1274. DWORD dwTSInstallMode = _QueryTSInstallMode((_dwSource & IA_DARWIN) ? _szProductID : _szKeyName);
  1275. BOOL bPrevMode = FALSE;
  1276. if (dwTSInstallMode == 0)
  1277. {
  1278. bPrevMode = TermsrvAppInstallMode();
  1279. SetTermsrvAppInstallMode(TRUE);
  1280. }
  1281. #endif // DOWNLEVEL_PLATFORM
  1282. #endif // WINNT
  1283. if (_dwAction & APPACTION_MODIFY)
  1284. {
  1285. if ((_dwSource & IA_LEGACY) && _LegacyModify(hwndParent))
  1286. hres = S_OK;
  1287. else if (_dwSource & IA_DARWIN)
  1288. {
  1289. // For modify operations we need to use the FULL UI level to give user
  1290. // more choices
  1291. // NOTE: we are currently not setting this back to the original after the
  1292. // modify operation. This seems to be okay with the Darwin guys
  1293. INSTALLUILEVEL OldUI = MsiSetInternalUI(INSTALLUILEVEL_FULL, NULL);
  1294. LONG lRet = MsiConfigureProduct(_szProductID, INSTALLLEVEL_DEFAULT,
  1295. INSTALLSTATE_DEFAULT);
  1296. MsiSetInternalUI(OldUI, NULL);
  1297. hres = HRESULT_FROM_WIN32(lRet);
  1298. if (FAILED(hres))
  1299. _ARPErrorMessageBox(lRet);
  1300. else
  1301. _SetSlowAppInfoChanged(NULL, 1);
  1302. }
  1303. }
  1304. #ifdef WINNT
  1305. #ifndef DOWNLEVEL_PLATFORM
  1306. if (dwTSInstallMode == 0)
  1307. SetTermsrvAppInstallMode(bPrevMode);
  1308. #endif // DOWNLEVEL_PLATFORM
  1309. #endif // WINNT
  1310. return hres;
  1311. }
  1312. // Repair Darwin apps.
  1313. LONG CInstalledApp::_DarRepair(BOOL bReinstall)
  1314. {
  1315. DWORD dwReinstall;
  1316. dwReinstall = REINSTALLMODE_USERDATA | REINSTALLMODE_MACHINEDATA |
  1317. REINSTALLMODE_SHORTCUT | REINSTALLMODE_FILEOLDERVERSION |
  1318. REINSTALLMODE_FILEVERIFY | REINSTALLMODE_PACKAGE;
  1319. return MsiReinstallProduct(_szProductID, dwReinstall);
  1320. }
  1321. // IInstalledApps::Repair
  1322. STDMETHODIMP CInstalledApp::Repair(BOOL bReinstall)
  1323. {
  1324. HRESULT hres = E_FAIL;
  1325. if (_dwSource & IA_DARWIN)
  1326. {
  1327. LONG lRet = _DarRepair(bReinstall);
  1328. hres = HRESULT_FROM_WIN32(lRet);
  1329. if (FAILED(hres))
  1330. _ARPErrorMessageBox(lRet);
  1331. else
  1332. _SetSlowAppInfoChanged(NULL, 1);
  1333. }
  1334. // don't know how to do SMS stuff
  1335. return hres;
  1336. }
  1337. // IInstalledApp::Upgrade
  1338. STDMETHODIMP CInstalledApp::Upgrade()
  1339. {
  1340. HRESULT hres = E_FAIL;
  1341. if ((_dwAction & APPACTION_UPGRADE) && (_dwSource & IA_DARWIN))
  1342. {
  1343. ShellExecute(NULL, NULL, _pszUpdateUrl, NULL, NULL, SW_SHOWDEFAULT);
  1344. hres = S_OK;
  1345. _SetSlowAppInfoChanged(NULL, 1);
  1346. }
  1347. return hres;
  1348. }
  1349. // IInstalledApp::QueryInterface
  1350. HRESULT CInstalledApp::QueryInterface(REFIID riid, LPVOID * ppvOut)
  1351. {
  1352. static const QITAB qit[] = {
  1353. QITABENT(CInstalledApp, IInstalledApp), // IID_IInstalledApp
  1354. QITABENTMULTI(CInstalledApp, IShellApp, IInstalledApp), // IID_IShellApp
  1355. { 0 },
  1356. };
  1357. return QISearch(this, qit, riid, ppvOut);
  1358. }
  1359. // IInstalledApp::AddRef
  1360. ULONG CInstalledApp::AddRef()
  1361. {
  1362. InterlockedIncrement(&_cRef);
  1363. TraceAddRef(CInstalledApp, _cRef);
  1364. return _cRef;
  1365. }
  1366. // IInstalledApp::Release
  1367. ULONG CInstalledApp::Release()
  1368. {
  1369. InterlockedDecrement(&_cRef);
  1370. TraceRelease(CInstalledApp, _cRef);
  1371. if (_cRef > 0)
  1372. return _cRef;
  1373. delete this;
  1374. return 0;
  1375. }
  1376. //
  1377. // As of this first release of Windows XP, most applications are
  1378. // not going to be aware of Fast User Switching in the sense that if
  1379. // User 'A' is running the application and User 'B' tries to
  1380. // uninstall that application, the application may be damaged.
  1381. // To protect against this, we display a warning message informing
  1382. // the user of this potential problem. See function _ShowMultiUserWarning().
  1383. // If an application is aware of Fast User Switching, they make that
  1384. // indication by setting a registry value in their "Uninstall" key.
  1385. // This function queries for that value and returns TRUE/FALSE to indicate
  1386. // it's presence. So that we err on the conservative side, any
  1387. // failure to read this value is equivalent to it's absence.
  1388. //
  1389. BOOL
  1390. CInstalledApp::_IsAppFastUserSwitchingCompliant(
  1391. void
  1392. )
  1393. {
  1394. BOOL bCompliant = FALSE;
  1395. HKEY hkey = _OpenUninstallRegKey(KEY_QUERY_VALUE);
  1396. if (NULL != hkey)
  1397. {
  1398. DWORD dwType;
  1399. DWORD dwValue;
  1400. DWORD cbData = sizeof(dwValue);
  1401. DWORD dwResult = RegQueryValueEx(hkey,
  1402. TEXT("FastUserSwitchingCompliant"),
  1403. NULL,
  1404. &dwType,
  1405. (LPBYTE)&dwValue,
  1406. &cbData);
  1407. if (ERROR_SUCCESS == dwResult)
  1408. {
  1409. if (REG_DWORD == dwType)
  1410. {
  1411. if (1 == dwValue)
  1412. {
  1413. bCompliant = TRUE;
  1414. }
  1415. }
  1416. else
  1417. {
  1418. TraceMsg(TF_ERROR, "FastUserSwitchingCompliant reg value is invalid type (%d). Expected REG_DWORD.", dwType);
  1419. }
  1420. }
  1421. else if (ERROR_FILE_NOT_FOUND != dwResult)
  1422. {
  1423. TraceMsg(TF_ERROR, "Error %d reading FastUserSwitchingCompliant reg value.", dwResult);
  1424. }
  1425. RegCloseKey(hkey);
  1426. }
  1427. else
  1428. {
  1429. TraceMsg(TF_ERROR, "Error opening application Uninstall reg key");
  1430. }
  1431. return bCompliant;
  1432. }