Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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