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.

1377 lines
36 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 2000
  6. //
  7. // File: cputil.cpp
  8. //
  9. //--------------------------------------------------------------------------
  10. #include "shellprv.h"
  11. #include "cpviewp.h"
  12. #include "cputil.h"
  13. //
  14. // Loads a string based upon the description.
  15. // Example: shell32,42
  16. //
  17. // lpStrDesc - contains the string description
  18. //
  19. HRESULT
  20. CPL::LoadStringFromResource(
  21. LPCWSTR pszStrDesc,
  22. LPWSTR *ppszOut
  23. )
  24. {
  25. ASSERT(NULL != pszStrDesc);
  26. ASSERT(NULL != ppszOut);
  27. ASSERT(!IsBadWritePtr(ppszOut, sizeof(*ppszOut)));
  28. *ppszOut = NULL;
  29. WCHAR szFile[MAX_PATH];
  30. HRESULT hr = StringCchCopyW(szFile, ARRAYSIZE(szFile), pszStrDesc); // the below writes this buffer
  31. if (SUCCEEDED(hr))
  32. {
  33. int iStrID = PathParseIconLocationW(szFile);
  34. if (iStrID < 0)
  35. {
  36. iStrID = -iStrID; // support ",-id" syntax
  37. }
  38. HMODULE hLib = LoadLibraryExW(szFile, NULL, LOAD_LIBRARY_AS_DATAFILE);
  39. if (hLib)
  40. {
  41. WCHAR szTemp[INFOTIPSIZE]; // INFOTIPSIZE is the largest string type we're expected to load
  42. if (0 < LoadStringW(hLib, (UINT)iStrID, szTemp, ARRAYSIZE(szTemp)))
  43. {
  44. hr = SHStrDup(szTemp, ppszOut);
  45. }
  46. else
  47. {
  48. hr = ResultFromLastError();
  49. }
  50. FreeLibrary(hLib);
  51. }
  52. else
  53. {
  54. hr = ResultFromLastError();
  55. }
  56. }
  57. return THR(hr);
  58. }
  59. //
  60. // Shell icon functions deal in terms of "small" and "large" icons.
  61. // This function determines which should be used for a
  62. // given eCPIMGSIZE value.
  63. //
  64. bool
  65. CPL::ShouldUseSmallIconForDesiredSize(
  66. eCPIMGSIZE eSize
  67. )
  68. {
  69. UINT cx;
  70. UINT cy;
  71. ImageDimensionsFromDesiredSize(eSize, &cx, &cy);
  72. if (int(cx) <= GetSystemMetrics(SM_CXSMICON))
  73. {
  74. return true;
  75. }
  76. return false;
  77. }
  78. //
  79. // This function returns a eCPIMGSIZE value to pixel dimensions.
  80. // This indirection lets us specify image sizes in abstract terms
  81. // then convert to physical pixel dimensions when required. If
  82. // you want to change the size of images used for a particular
  83. // Control Panel UI item type, this is where you change it.
  84. //
  85. void
  86. CPL::ImageDimensionsFromDesiredSize(
  87. eCPIMGSIZE eSize,
  88. UINT *pcx,
  89. UINT *pcy
  90. )
  91. {
  92. ASSERT(NULL != pcx);
  93. ASSERT(!IsBadWritePtr(pcx, sizeof(*pcx)));
  94. ASSERT(NULL != pcy);
  95. ASSERT(!IsBadWritePtr(pcy, sizeof(*pcy)));
  96. *pcx = *pcy = 0;
  97. //
  98. // This table converts eCPIMGSIZE values into actual
  99. // image size values. A couple of things to note:
  100. //
  101. // 1. If you want to change the image size associated with
  102. // an eIMGSIZE value, simply change these numbers.
  103. //
  104. // 2. If actual image size is dependent upon some system
  105. // configuration parameter, do the interpretation of
  106. // that parameter here making the size a function
  107. // of that parameter.
  108. //
  109. static const SIZE rgSize[] = {
  110. { 16, 16 }, // eCPIMGSIZE_WEBVIEW
  111. { 16, 16 }, // eCPIMGSIZE_TASK
  112. { 48, 48 }, // eCPIMGSIZE_CATEGORY
  113. { 32, 32 }, // eCPIMGSIZE_BANNER
  114. { 32, 32 } // eCPIMGSIZE_APPLET
  115. };
  116. ASSERT(int(eSize) >= 0 && int(eSize) < ARRAYSIZE(rgSize));
  117. *pcx = rgSize[eSize].cx;
  118. *pcy = rgSize[eSize].cy;
  119. }
  120. HRESULT
  121. CPL::LoadIconFromResourceID(
  122. LPCWSTR pszModule,
  123. int idIcon,
  124. eCPIMGSIZE eSize,
  125. HICON *phIcon
  126. )
  127. {
  128. ASSERT(NULL != pszModule);
  129. ASSERT(NULL != phIcon);
  130. ASSERT(!IsBadWritePtr(phIcon, sizeof(*phIcon)));
  131. ASSERT(0 < idIcon);
  132. HRESULT hr = E_FAIL;
  133. HICON hIcon = NULL;
  134. HMODULE hModule = LoadLibraryExW(pszModule, NULL, LOAD_LIBRARY_AS_DATAFILE);
  135. if (hModule)
  136. {
  137. UINT cxIcon;
  138. UINT cyIcon;
  139. ImageDimensionsFromDesiredSize(eSize, &cxIcon, &cyIcon);
  140. hIcon = (HICON)LoadImage(hModule,
  141. MAKEINTRESOURCE(idIcon),
  142. IMAGE_ICON,
  143. cxIcon,
  144. cyIcon,
  145. 0);
  146. if (NULL != hIcon)
  147. {
  148. hr = S_OK;
  149. }
  150. else
  151. {
  152. hr = ResultFromLastError();
  153. }
  154. FreeLibrary(hModule);
  155. }
  156. else
  157. {
  158. hr = ResultFromLastError();
  159. }
  160. *phIcon = hIcon;
  161. return THR(hr);
  162. }
  163. HRESULT
  164. CPL::LoadIconFromResourceIndex(
  165. LPCWSTR pszModule,
  166. int iIcon,
  167. eCPIMGSIZE eSize,
  168. HICON *phIcon
  169. )
  170. {
  171. ASSERT(NULL != pszModule);
  172. ASSERT(NULL != phIcon);
  173. ASSERT(!IsBadWritePtr(phIcon, sizeof(*phIcon)));
  174. if (-1 == iIcon)
  175. {
  176. //
  177. // Special case. -1 is an invalid icon index/id.
  178. //
  179. iIcon = 0;
  180. }
  181. HICON hIcon = NULL;
  182. HRESULT hr = E_FAIL;
  183. if (CPL::ShouldUseSmallIconForDesiredSize(eSize))
  184. {
  185. if (0 < ExtractIconExW(pszModule, iIcon, NULL, &hIcon, 1))
  186. {
  187. hr = S_OK;
  188. }
  189. else
  190. {
  191. TraceMsg(TF_ERROR, "ExtractIconEx failed for small icon (index %d) in module \"%s\"", iIcon, pszModule);
  192. }
  193. }
  194. else
  195. {
  196. if (0 < ExtractIconExW(pszModule, iIcon, &hIcon, NULL, 1))
  197. {
  198. hr = S_OK;
  199. }
  200. else
  201. {
  202. TraceMsg(TF_ERROR, "ExtractIconEx failed for large icon (index %d) in module \"%s\"", iIcon, pszModule);
  203. }
  204. }
  205. *phIcon = hIcon;
  206. return THR(hr);
  207. }
  208. HRESULT
  209. CPL::LoadIconFromResource(
  210. LPCWSTR pszResource,
  211. eCPIMGSIZE eSize,
  212. HICON *phIcon
  213. )
  214. {
  215. ASSERT(NULL != pszResource);
  216. ASSERT(NULL != phIcon);
  217. ASSERT(!IsBadWritePtr(phIcon, sizeof(*phIcon)));
  218. *phIcon = NULL;
  219. //
  220. // PathParseIconLocation modifies it's input string.
  221. //
  222. WCHAR szResource[MAX_PATH];
  223. HRESULT hr = StringCchCopyW(szResource, ARRAYSIZE(szResource), pszResource);
  224. if (SUCCEEDED(hr))
  225. {
  226. int idIcon = PathParseIconLocationW(szResource);
  227. if (-1 == idIcon)
  228. {
  229. //
  230. // Special case. -1 is an invalid icon ID.
  231. //
  232. idIcon = 0;
  233. }
  234. if (0 > idIcon)
  235. {
  236. hr = CPL::LoadIconFromResourceID(szResource, -idIcon, eSize, phIcon);
  237. }
  238. else
  239. {
  240. hr = CPL::LoadIconFromResourceIndex(szResource, idIcon, eSize, phIcon);
  241. }
  242. }
  243. return THR(hr);
  244. }
  245. HRESULT
  246. CPL::ExtractIconFromPidl(
  247. IShellFolder *psf,
  248. LPCITEMIDLIST pidl,
  249. eCPIMGSIZE eSize,
  250. HICON *phIcon
  251. )
  252. {
  253. ASSERT(NULL != psf);
  254. ASSERT(NULL != pidl);
  255. ASSERT(NULL != phIcon);
  256. ASSERT(!IsBadWritePtr(phIcon, sizeof(*phIcon)));
  257. *phIcon = NULL;
  258. IExtractIcon *pei;
  259. HRESULT hr = psf->GetUIObjectOf(NULL, 1, &pidl, IID_IExtractIcon, NULL, (void **)&pei);
  260. if (SUCCEEDED(hr))
  261. {
  262. TCHAR szFile[MAX_PATH];
  263. INT iIcon;
  264. UINT uFlags = 0;
  265. hr = pei->GetIconLocation(GIL_FORSHELL,
  266. szFile,
  267. ARRAYSIZE(szFile),
  268. &iIcon,
  269. &uFlags);
  270. if (SUCCEEDED(hr))
  271. {
  272. if (0 == (GIL_NOTFILENAME & uFlags))
  273. {
  274. hr = CPL::LoadIconFromResourceIndex(szFile, iIcon, eSize, phIcon);
  275. }
  276. else
  277. {
  278. HICON hIconLarge = NULL;
  279. HICON hIconSmall = NULL;
  280. const int cxIcon = GetSystemMetrics(SM_CXICON);
  281. const int cxSmIcon = GetSystemMetrics(SM_CXSMICON);
  282. hr = pei->Extract(szFile,
  283. iIcon,
  284. &hIconLarge,
  285. &hIconSmall,
  286. MAKELONG(cxIcon, cxSmIcon));
  287. if (SUCCEEDED(hr))
  288. {
  289. if (CPL::ShouldUseSmallIconForDesiredSize(eSize))
  290. {
  291. *phIcon = hIconSmall;
  292. hIconSmall = NULL;
  293. }
  294. else
  295. {
  296. *phIcon = hIconLarge;
  297. hIconLarge = NULL;
  298. }
  299. }
  300. //
  301. // Destroy any icons not being returned.
  302. //
  303. if (NULL != hIconSmall)
  304. {
  305. DestroyIcon(hIconSmall);
  306. }
  307. if (NULL != hIconLarge)
  308. {
  309. DestroyIcon(hIconLarge);
  310. }
  311. }
  312. }
  313. pei->Release();
  314. }
  315. ASSERT(FAILED(hr) || NULL != *phIcon);
  316. if (NULL == *phIcon)
  317. {
  318. //
  319. // If by-chance a NULL icon handle is retrieved, we don't
  320. // want to return a success code.
  321. //
  322. hr = E_FAIL;
  323. }
  324. return THR(hr);
  325. }
  326. // Checks the given restriction. Returns TRUE (restricted) if the
  327. // specified key/value exists and is non-zero, false otherwise
  328. BOOL
  329. DeskCPL_CheckRestriction(
  330. HKEY hKey,
  331. LPCWSTR lpszValueName
  332. )
  333. {
  334. ASSERT(NULL != lpszValueName);
  335. DWORD dwData;
  336. DWORD dwSize = sizeof(dwData);
  337. if ((ERROR_SUCCESS == RegQueryValueExW(hKey,
  338. lpszValueName,
  339. NULL,
  340. NULL,
  341. (BYTE *)&dwData,
  342. &dwSize))
  343. && dwData)
  344. {
  345. return TRUE;
  346. }
  347. return FALSE;
  348. }
  349. //
  350. // Function returns the actual tab index given the default tab index.
  351. // The actual tab index will be different than the default value if there are
  352. // various system policies in effect which disable some tabs
  353. //
  354. //
  355. // To add further restrictions, modify the aTabMap to include the default tab
  356. // index and the corresponding policy. Also, you should keep the eDESKCPLTAB enum
  357. // in sync with the aTabMap array.
  358. //
  359. //
  360. int
  361. CPL::DeskCPL_GetTabIndex(
  362. CPL::eDESKCPLTAB iTab,
  363. OPTIONAL LPWSTR pszCanonicalName,
  364. OPTIONAL DWORD cchSize
  365. )
  366. {
  367. HKEY hKey;
  368. int iTabActual = CPL::CPLTAB_ABSENT;
  369. if (iTab >= 0 && iTab < CPL::CPLTAB_DESK_MAX)
  370. {
  371. //
  372. // While adding more tabs, make sure that it is entered in the right position in the
  373. // the array below. So, for example, if the default tab index of the new tab is 2, it
  374. // should be the aTabMap[2] entry (Currently CPLTAB_DESK_APPEARANCE is
  375. // the one with tab index = 2). You will have to modify eDESKCPLTAB accordingly too.
  376. //
  377. struct
  378. {
  379. int nIndex; // the canonical name of the tab (don't use indexes because they change with policies or revs)
  380. LPCWSTR pszCanoncialTabName; // the canonical name of the tab (don't use indexes because they change with policies or revs)
  381. LPCWSTR pszRestriction; // corresponding restriction
  382. } aTabMap[CPL::CPLTAB_DESK_MAX] = {
  383. { 0, SZ_DISPLAYCPL_OPENTO_DESKTOP, REGSTR_VAL_DISPCPL_NOBACKGROUNDPAGE }, // CPLTAB_DESK_BACKGROUND == 0
  384. { 1, SZ_DISPLAYCPL_OPENTO_SCREENSAVER, REGSTR_VAL_DISPCPL_NOSCRSAVPAGE }, // CPLTAB_DESK_SCREENSAVER == 1
  385. { 2, SZ_DISPLAYCPL_OPENTO_APPEARANCE, REGSTR_VAL_DISPCPL_NOAPPEARANCEPAGE }, // CPLTAB_DESK_APPEARANCE == 2
  386. { 3, SZ_DISPLAYCPL_OPENTO_SETTINGS, REGSTR_VAL_DISPCPL_NOSETTINGSPAGE } // CPLTAB_DESK_SETTINGS == 3
  387. };
  388. #ifdef DEBUG
  389. //
  390. // Verify proper initialization of the nIndex member of aTabMap[]
  391. //
  392. for (int k=0; k < ARRAYSIZE(aTabMap); k++)
  393. {
  394. ASSERT(aTabMap[k].nIndex == k);
  395. }
  396. #endif
  397. iTabActual = aTabMap[iTab].nIndex;
  398. //
  399. // Note, if no policy is configured, the RegOpenKey call below will fail,
  400. // in that case we return the default tab value, as entered in the
  401. // map above.
  402. //
  403. if ((ERROR_SUCCESS == RegOpenKeyExW(HKEY_CURRENT_USER,
  404. REGSTR_PATH_POLICIES L"\\" REGSTR_KEY_SYSTEM,
  405. 0,
  406. KEY_QUERY_VALUE,
  407. &hKey)))
  408. {
  409. //
  410. // check all tabs to see if there is restriction
  411. //
  412. if (DeskCPL_CheckRestriction(hKey, aTabMap[iTab].pszRestriction))
  413. {
  414. // this tab does not exist, mark it as such
  415. iTabActual = CPL::CPLTAB_ABSENT;
  416. }
  417. RegCloseKey(hKey);
  418. }
  419. if (pszCanonicalName &&
  420. (iTab >= 0) && (iTab < ARRAYSIZE(aTabMap)))
  421. {
  422. StringCchCopyW(pszCanonicalName, cchSize, aTabMap[iTab].pszCanoncialTabName);
  423. }
  424. }
  425. return iTabActual;
  426. }
  427. bool
  428. CPL::DeskCPL_IsTabPresent(
  429. eDESKCPLTAB iTab
  430. )
  431. {
  432. return CPLTAB_ABSENT != DeskCPL_GetTabIndex(iTab, NULL, 0);
  433. }
  434. //////////////////////////////////////////////////////////////
  435. //
  436. // Policy checking routines
  437. //
  438. //////////////////////////////////////////////////////////////
  439. #define REGSTR_POLICIES_RESTRICTCPL REGSTR_PATH_POLICIES TEXT("\\Explorer\\RestrictCpl")
  440. #define REGSTR_POLICIES_DISALLOWCPL REGSTR_PATH_POLICIES TEXT("\\Explorer\\DisallowCpl")
  441. //
  442. // Returns true if the specified app is listed under the specified key
  443. //
  444. // pszFileName can be a string resource ID in shell32 for things
  445. // like "Fonts", "Printers and Faxes" etc.
  446. //
  447. // i.e. IsNameListedUnderKey(MAKEINTRESOURCE(IDS_MY_APPLET_TITLE), hkey);
  448. //
  449. // In this case, if the resource string cannot be loaded, the function
  450. // returns 'false'.
  451. //
  452. bool
  453. IsNameListedUnderKey(
  454. LPCWSTR pszFileName,
  455. LPCWSTR pszKey
  456. )
  457. {
  458. bool bResult = FALSE;
  459. HKEY hkey;
  460. TCHAR szName[MAX_PATH];
  461. if (IS_INTRESOURCE(pszFileName))
  462. {
  463. //
  464. // The name is localized so we specify it as a string resource ID.
  465. // Load it from shell32.dll.
  466. //
  467. if (0 < LoadString(HINST_THISDLL, PtrToUint(pszFileName), szName, ARRAYSIZE(szName)))
  468. {
  469. pszFileName = szName;
  470. }
  471. else
  472. {
  473. //
  474. // If the load fails spit out a debug squirty and return false.
  475. //
  476. TW32(GetLastError());
  477. return false;
  478. }
  479. }
  480. if (ERROR_SUCCESS == RegOpenKeyExW(HKEY_CURRENT_USER,
  481. pszKey,
  482. 0,
  483. KEY_QUERY_VALUE,
  484. &hkey))
  485. {
  486. int iValue = 0;
  487. WCHAR szValue[MAX_PATH];
  488. WCHAR szData[MAX_PATH];
  489. DWORD dwType, cbData, cchValue;
  490. while (cbData = sizeof(szData),
  491. cchValue = ARRAYSIZE(szValue),
  492. ERROR_SUCCESS == RegEnumValue(hkey,
  493. iValue,
  494. szValue,
  495. &cchValue,
  496. NULL,
  497. &dwType,
  498. (LPBYTE) szData,
  499. &cbData))
  500. {
  501. if (0 == lstrcmpiW(szData, pszFileName))
  502. {
  503. bResult = true;
  504. break;
  505. }
  506. iValue++;
  507. }
  508. RegCloseKey(hkey);
  509. }
  510. return bResult;
  511. }
  512. //
  513. // Method cloned from shell32\ctrlfldr.cpp (DoesCplPolicyAllow)
  514. //
  515. // pszName can be a string resource ID in shell32 for things
  516. // like "Fonts", "Printers and Faxes" etc.
  517. //
  518. // i.e. IsAppletEnabled(NULL, MAKEINTRESOURCE(IDS_MY_APPLET_TITLE));
  519. //
  520. bool
  521. CPL::IsAppletEnabled(
  522. LPCWSTR pszFileName,
  523. LPCWSTR pszName
  524. )
  525. {
  526. bool bEnabled = true;
  527. //
  528. // It's illegal (and meaningless) for both to be NULL.
  529. // Trap both with an assert and runtime check. I don't want any
  530. // code to erroneously think an applet is enabled when it's not.
  531. //
  532. ASSERT(NULL != pszName || NULL != pszFileName);
  533. if (NULL == pszName && NULL == pszFileName)
  534. {
  535. bEnabled = false;
  536. }
  537. else
  538. {
  539. if (SHRestricted(REST_RESTRICTCPL) &&
  540. ((NULL == pszName || !IsNameListedUnderKey(pszName, REGSTR_POLICIES_RESTRICTCPL)) &&
  541. (NULL == pszFileName || !IsNameListedUnderKey(pszFileName, REGSTR_POLICIES_RESTRICTCPL))))
  542. {
  543. bEnabled = false;
  544. }
  545. if (bEnabled)
  546. {
  547. if (SHRestricted(REST_DISALLOWCPL) &&
  548. ((NULL == pszName || IsNameListedUnderKey(pszName, REGSTR_POLICIES_DISALLOWCPL)) ||
  549. (NULL == pszFileName || IsNameListedUnderKey(pszFileName, REGSTR_POLICIES_DISALLOWCPL))))
  550. {
  551. bEnabled = false;
  552. }
  553. }
  554. }
  555. return bEnabled;
  556. }
  557. HRESULT
  558. CPL::ControlPanelViewFromSite(
  559. IUnknown *punkSite,
  560. ICplView **ppview
  561. )
  562. {
  563. ASSERT(NULL != punkSite);
  564. ASSERT(NULL != ppview);
  565. ASSERT(!IsBadWritePtr(ppview, sizeof(*ppview)));
  566. *ppview = NULL;
  567. HRESULT hr = IUnknown_QueryService(punkSite, SID_SControlPanelView, IID_ICplView, (void **)ppview);
  568. return THR(hr);
  569. }
  570. HRESULT
  571. CPL::ShellBrowserFromSite(
  572. IUnknown *punkSite,
  573. IShellBrowser **ppsb
  574. )
  575. {
  576. ASSERT(NULL != punkSite);
  577. ASSERT(NULL != ppsb);
  578. ASSERT(!IsBadWritePtr(ppsb, sizeof(*ppsb)));
  579. *ppsb = NULL;
  580. HRESULT hr = IUnknown_QueryService(punkSite, SID_STopLevelBrowser, IID_PPV_ARG(IShellBrowser, ppsb));
  581. return THR(hr);
  582. }
  583. HRESULT
  584. CPL::BrowseIDListInPlace(
  585. LPCITEMIDLIST pidl,
  586. IShellBrowser *psb
  587. )
  588. {
  589. ASSERT(NULL != pidl);
  590. ASSERT(NULL != psb);
  591. const UINT uFlags = SBSP_SAMEBROWSER | SBSP_OPENMODE | SBSP_ABSOLUTE;
  592. HRESULT hr = psb->BrowseObject(pidl, uFlags);
  593. return THR(hr);
  594. }
  595. //
  596. // System Restore is allowed only for admins/owners.
  597. // Also must check policy.
  598. //
  599. bool
  600. CPL::IsSystemRestoreRestricted(
  601. void
  602. )
  603. {
  604. bool bRestricted = false;
  605. //
  606. // First check policy.
  607. //
  608. DWORD dwType;
  609. DWORD dwValue;
  610. DWORD cbValue = sizeof(dwValue);
  611. DWORD dwResult = SHGetValueW(HKEY_LOCAL_MACHINE,
  612. L"Software\\Policies\\Microsoft\\Windows NT\\SystemRestore",
  613. L"DisableSR",
  614. &dwType,
  615. &dwValue,
  616. &cbValue);
  617. if (ERROR_SUCCESS == dwResult && REG_DWORD == dwType)
  618. {
  619. if (1 == dwValue)
  620. {
  621. //
  622. // Sytem Restore is disabled by policy.
  623. //
  624. bRestricted = true;
  625. }
  626. }
  627. if (!bRestricted)
  628. {
  629. //
  630. // Not restricted by policy. Check for admin/owner.
  631. //
  632. if (!CPL::IsUserAdmin())
  633. {
  634. //
  635. // User is not an admin.
  636. //
  637. bRestricted = true;
  638. }
  639. }
  640. return bRestricted;
  641. }
  642. #ifdef DEBUG
  643. HRESULT
  644. ReadTestConfigurationFlag(
  645. LPCWSTR pszValue,
  646. BOOL *pbFlag
  647. )
  648. {
  649. HRESULT hr = S_OK;
  650. DWORD dwValue = 0;
  651. DWORD cbValue = sizeof(dwValue);
  652. DWORD dwType;
  653. DWORD dwResult = SHGetValueW(HKEY_CURRENT_USER,
  654. REGSTR_PATH_CONTROLPANEL,
  655. pszValue,
  656. &dwType,
  657. &dwValue,
  658. &cbValue);
  659. if (ERROR_SUCCESS != dwResult)
  660. {
  661. hr = HRESULT_FROM_WIN32(dwResult);
  662. }
  663. else if (REG_DWORD != dwType)
  664. {
  665. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  666. }
  667. if (SUCCEEDED(hr) && NULL != pbFlag)
  668. {
  669. if (0 == dwValue)
  670. {
  671. *pbFlag = FALSE;
  672. }
  673. else
  674. {
  675. *pbFlag = TRUE;
  676. }
  677. }
  678. return hr;
  679. }
  680. enum eSKU
  681. {
  682. eSKU_SERVER,
  683. eSKU_PROFESSIONAL,
  684. eSKU_PERSONAL,
  685. eSKU_NUMSKUS
  686. };
  687. HRESULT
  688. ReadTestConfigurationSku(
  689. eSKU *peSku
  690. )
  691. {
  692. HRESULT hr = S_OK;
  693. WCHAR szValue[MAX_PATH];
  694. DWORD cbValue = sizeof(szValue);
  695. DWORD dwType;
  696. DWORD dwResult = SHGetValueW(HKEY_CURRENT_USER,
  697. REGSTR_PATH_CONTROLPANEL,
  698. L"SKU",
  699. &dwType,
  700. szValue,
  701. &cbValue);
  702. if (ERROR_SUCCESS != dwResult)
  703. {
  704. hr = HRESULT_FROM_WIN32(dwResult);
  705. }
  706. else if (REG_SZ != dwType)
  707. {
  708. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  709. }
  710. if (SUCCEEDED(hr) && NULL != peSku)
  711. {
  712. static const struct
  713. {
  714. LPCWSTR pszValue;
  715. eSKU sku;
  716. } rgMap[] = {
  717. { L"personal", eSKU_PERSONAL },
  718. { L"professional", eSKU_PROFESSIONAL },
  719. { L"pro", eSKU_PROFESSIONAL },
  720. { L"server", eSKU_SERVER }
  721. };
  722. hr = E_FAIL;
  723. for (int i = 0; i < ARRAYSIZE(rgMap); i++)
  724. {
  725. if (0 == lstrcmpiW(rgMap[i].pszValue, szValue))
  726. {
  727. *peSku = rgMap[i].sku;
  728. hr = S_OK;
  729. break;
  730. }
  731. }
  732. }
  733. return hr;
  734. }
  735. #endif
  736. BOOL
  737. CPL::IsOsServer(
  738. void
  739. )
  740. {
  741. BOOL bServer = IsOS(OS_ANYSERVER);
  742. #ifdef DEBUG
  743. eSKU sku;
  744. if (SUCCEEDED(ReadTestConfigurationSku(&sku)))
  745. {
  746. bServer = (eSKU_SERVER == sku);
  747. }
  748. #endif
  749. return bServer;
  750. }
  751. BOOL
  752. CPL::IsOsPersonal(
  753. void
  754. )
  755. {
  756. BOOL bPersonal = IsOS(OS_PERSONAL);
  757. #ifdef DEBUG
  758. eSKU sku;
  759. if (SUCCEEDED(ReadTestConfigurationSku(&sku)))
  760. {
  761. bPersonal = (eSKU_PERSONAL == sku);
  762. }
  763. #endif
  764. return bPersonal;
  765. }
  766. BOOL
  767. CPL::IsOsProfessional(
  768. void
  769. )
  770. {
  771. BOOL bProfessional = IsOS(OS_PROFESSIONAL);
  772. #ifdef DEBUG
  773. eSKU sku;
  774. if (SUCCEEDED(ReadTestConfigurationSku(&sku)))
  775. {
  776. bProfessional = (eSKU_PROFESSIONAL == sku);
  777. }
  778. #endif
  779. return bProfessional;
  780. }
  781. BOOL
  782. CPL::IsConnectedToDomain(
  783. void
  784. )
  785. {
  786. BOOL bDomain = IsOS(OS_DOMAINMEMBER);
  787. #ifdef DEBUG
  788. ReadTestConfigurationFlag(L"Domain", &bDomain);
  789. #endif
  790. return bDomain;
  791. }
  792. BOOL
  793. CPL::IsUserAdmin(
  794. void
  795. )
  796. {
  797. BOOL bAdmin = ::IsUserAnAdmin();
  798. #ifdef DEBUG
  799. ReadTestConfigurationFlag(L"Admin", &bAdmin);
  800. #endif
  801. return bAdmin;
  802. }
  803. HRESULT
  804. CPL::GetUserAccountType(
  805. eACCOUNTTYPE *pType
  806. )
  807. {
  808. ASSERT(NULL != pType);
  809. ASSERT(!IsBadWritePtr(pType, sizeof(*pType)));
  810. HRESULT hr = E_FAIL;
  811. eACCOUNTTYPE acctype = eACCOUNTTYPE_UNKNOWN;
  812. static const struct
  813. {
  814. DWORD rid; // Account relative ID.
  815. eACCOUNTTYPE eType; // Type code to return.
  816. } rgMap[] = {
  817. { DOMAIN_ALIAS_RID_ADMINS, eACCOUNTTYPE_OWNER },
  818. { DOMAIN_ALIAS_RID_POWER_USERS, eACCOUNTTYPE_STANDARD },
  819. { DOMAIN_ALIAS_RID_USERS, eACCOUNTTYPE_LIMITED },
  820. { DOMAIN_ALIAS_RID_GUESTS, eACCOUNTTYPE_GUEST }
  821. };
  822. for (int i = 0; i < ARRAYSIZE(rgMap); i++)
  823. {
  824. if (SHTestTokenMembership(NULL, rgMap[i].rid))
  825. {
  826. acctype = rgMap[i].eType;
  827. hr = S_OK;
  828. break;
  829. }
  830. }
  831. ASSERT(eACCOUNTTYPE_UNKNOWN != acctype);
  832. *pType = acctype;
  833. return THR(hr);
  834. }
  835. //
  836. // Create a URL to pass to HSS help.
  837. // The URL created references the Control_Panel help topic.
  838. //
  839. HRESULT
  840. CPL::BuildHssHelpURL(
  841. LPCWSTR pszSelect, // Optional. NULL == base CP help.
  842. LPWSTR pszURL,
  843. UINT cchURL
  844. )
  845. {
  846. ASSERT(NULL != pszURL);
  847. ASSERT(!IsBadWritePtr(pszURL, cchURL * sizeof(*pszURL)));
  848. ASSERT(NULL == pszSelect || !IsBadStringPtr(pszSelect, UINT(-1)));
  849. //
  850. // HSS has specific help content for 'limited' users.
  851. // Default to a non-limited user.
  852. //
  853. bool bLimitedUser = false;
  854. CPL::eACCOUNTTYPE accType;
  855. if (SUCCEEDED(CPL::GetUserAccountType(&accType)))
  856. {
  857. bLimitedUser = (eACCOUNTTYPE_LIMITED == accType);
  858. }
  859. HRESULT hr = S_OK;
  860. WCHAR szSelect[160];
  861. szSelect[0] = L'\0';
  862. if (NULL != pszSelect)
  863. {
  864. hr = StringCchPrintfW(szSelect, ARRAYSIZE(szSelect), L"&select=Unmapped/Control_Panel/%s", pszSelect);
  865. }
  866. if (SUCCEEDED(hr))
  867. {
  868. //
  869. // The URL can take one of 4 forms depending upon the category and account type.
  870. //
  871. // User Account CP View Help Content Displayed
  872. // ---------------- ---------------- -----------------------
  873. // Non-limited Category choice General CP help
  874. // Non-limited Category Category-specific help
  875. // Limited Category choice General CP help
  876. // Limited Category Category-specific help
  877. //
  878. hr = StringCchPrintfW(pszURL,
  879. cchURL,
  880. L"hcp://services/subsite?node=Unmapped/%sControl_Panel&topic=MS-ITS%%3A%%25HELP_LOCATION%%25%%5Chs.chm%%3A%%3A/hs_control_panel.htm%s",
  881. bLimitedUser ? L"L/" : L"",
  882. szSelect);
  883. }
  884. return hr;
  885. }
  886. HRESULT
  887. CPL::GetControlPanelFolder(
  888. IShellFolder **ppsf
  889. )
  890. {
  891. ASSERT(NULL != ppsf);
  892. ASSERT(!IsBadWritePtr(ppsf, sizeof(*ppsf)));
  893. *ppsf = NULL;
  894. LPITEMIDLIST pidlCpanel;
  895. HRESULT hr = SHGetSpecialFolderLocation(NULL, CSIDL_CONTROLS, &pidlCpanel);
  896. if (SUCCEEDED(hr))
  897. {
  898. IShellFolder *psfDesktop;
  899. hr = SHGetDesktopFolder(&psfDesktop);
  900. if (SUCCEEDED(hr))
  901. {
  902. hr = psfDesktop->BindToObject(pidlCpanel, NULL, IID_IShellFolder, (void **)ppsf);
  903. ATOMICRELEASE(psfDesktop);
  904. }
  905. ILFree(pidlCpanel);
  906. }
  907. return THR(hr);
  908. }
  909. //
  910. // On successful return, caller is responsible for freeing
  911. // returned buffer using LocalFree.
  912. //
  913. HRESULT
  914. CPL::ExpandEnvironmentVars(
  915. LPCTSTR psz,
  916. LPTSTR *ppszOut
  917. )
  918. {
  919. ASSERT(NULL != psz);
  920. ASSERT(NULL != ppszOut);
  921. ASSERT(!IsBadWritePtr(ppszOut, sizeof(*ppszOut)));
  922. HRESULT hr = E_FAIL;
  923. *ppszOut = NULL;
  924. TCHAR szDummy[1];
  925. DWORD dwResult = ExpandEnvironmentStrings(psz, szDummy, 0);
  926. if (0 < dwResult)
  927. {
  928. const DWORD cchRequired = dwResult;
  929. *ppszOut = (LPTSTR)LocalAlloc(LPTR, cchRequired * sizeof(TCHAR));
  930. if (NULL != *ppszOut)
  931. {
  932. dwResult = ExpandEnvironmentStrings(psz, *ppszOut, cchRequired);
  933. if (0 < dwResult)
  934. {
  935. ASSERT(dwResult <= cchRequired);
  936. hr = S_OK;
  937. }
  938. else
  939. {
  940. LocalFree(*ppszOut);
  941. *ppszOut = NULL;
  942. }
  943. }
  944. else
  945. {
  946. hr = E_OUTOFMEMORY;
  947. }
  948. }
  949. if (0 == dwResult)
  950. {
  951. hr = ResultFromLastError();
  952. }
  953. return THR(hr);
  954. }
  955. //// from sdfolder.cpp
  956. VARIANT_BOOL GetBarricadeStatus(LPCTSTR pszValueName);
  957. #define REGSTR_POLICIES_EXPLORER TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer")
  958. bool
  959. CPL::CategoryViewIsActive(
  960. bool *pbBarricadeFixedByPolicy
  961. )
  962. {
  963. DBG_ENTER(FTF_CPANEL, "CPL::CategoryViewIsActive");
  964. bool bActive = false;
  965. bool bBarricadeFixedByPolicy = false;
  966. //
  967. // We don't provide category view when running WOW64.
  968. //
  969. if (!IsOS(OS_WOW6432))
  970. {
  971. SHELLSTATE ss;
  972. const DWORD dwMask = SSF_WEBVIEW | SSF_WIN95CLASSIC;
  973. SHGetSetSettings(&ss, dwMask, FALSE);
  974. //
  975. // WebView? Barricade status View type
  976. // ----------- ---------------- ------------------------------
  977. // Off On Classic view
  978. // Off Off Classic view
  979. // On On Category view (aka 'simple')
  980. // On Off Classic view
  981. //
  982. // Note that these two shellstate settings encompass and are set
  983. // by the shell restrictions REST_CLASSICSHELL and REST_NOWEBVIEW.
  984. // Therefore, there is no reason to explicitely check those two restrictions.
  985. //
  986. if (ss.fWebView && !ss.fWin95Classic)
  987. {
  988. if (VARIANT_TRUE == CPL::GetBarricadeStatus(&bBarricadeFixedByPolicy))
  989. {
  990. bActive = true;
  991. }
  992. }
  993. }
  994. if (NULL != pbBarricadeFixedByPolicy)
  995. {
  996. *pbBarricadeFixedByPolicy = bBarricadeFixedByPolicy;
  997. }
  998. TraceMsg(TF_CPANEL, "Category view is %s.", bActive ? TEXT("ACTIVE") : TEXT("INACTIVE"));
  999. DBG_EXIT(FTF_CPANEL, "CPL::CategoryViewIsActive");
  1000. return bActive;
  1001. }
  1002. //
  1003. // Control Panel uses the 'barricade status' to determine which view
  1004. // 'classic' or 'category' to display. Yes, this is overloading the
  1005. // meaning of 'barricade' as used in the shell. However, since the
  1006. // Control Panel does not use a barricade in it's usual sense, this
  1007. // is a reasonable application of the feature.
  1008. //
  1009. VARIANT_BOOL
  1010. CPL::GetBarricadeStatus(
  1011. bool *pbFixedByPolicy // Optional. May be NULL.
  1012. )
  1013. {
  1014. DBG_ENTER(FTF_CPANEL, "CPL::GetBarricadeStatus");
  1015. VARIANT_BOOL vtb;
  1016. DWORD dwType;
  1017. DWORD dwData;
  1018. DWORD cbData = sizeof(dwData);
  1019. bool bFixedByPolicy = false;
  1020. bool bSetBarricade = false;
  1021. //
  1022. // First handle any OOBE issues.
  1023. //
  1024. if (CPL::IsFirstRunForThisUser())
  1025. {
  1026. TraceMsg(TF_CPANEL, "First time this user has opened Control Panel");
  1027. //
  1028. // Determine the default view to display out-of-box.
  1029. //
  1030. // Server gets 'classic'.
  1031. // Non-servers get 'category'.
  1032. //
  1033. if (IsOS(OS_ANYSERVER))
  1034. {
  1035. //
  1036. // Default is 'classic'.
  1037. //
  1038. vtb = VARIANT_FALSE;
  1039. TraceMsg(TF_CPANEL, "Running on server. Default to 'classic' view Control Panel.");
  1040. }
  1041. else
  1042. {
  1043. //
  1044. // Default is 'category'.
  1045. //
  1046. vtb = VARIANT_TRUE;
  1047. TraceMsg(TF_CPANEL, "Running on non-server. Default to 'category' view Control Panel.");
  1048. }
  1049. bSetBarricade = true;
  1050. }
  1051. //
  1052. // Apply any 'force view type' policy. This will override
  1053. // the default out-of-box setting obtained above.
  1054. //
  1055. if (ERROR_SUCCESS == SHRegGetUSValue(REGSTR_POLICIES_EXPLORER,
  1056. TEXT("ForceClassicControlPanel"),
  1057. &dwType,
  1058. &dwData,
  1059. &cbData,
  1060. FALSE,
  1061. NULL,
  1062. 0))
  1063. {
  1064. //
  1065. // policy exists
  1066. //
  1067. bFixedByPolicy = true;
  1068. if (0 == dwData)
  1069. {
  1070. //
  1071. // force the simple (category) view, ie, show barricade
  1072. //
  1073. vtb = VARIANT_TRUE;
  1074. TraceMsg(TF_CPANEL, "Policy forcing use of 'category' view Control Panel.");
  1075. }
  1076. else
  1077. {
  1078. //
  1079. // force the classic (icon) view, ie, no barricade
  1080. //
  1081. vtb = VARIANT_FALSE;
  1082. TraceMsg(TF_CPANEL, "Policy forcing use of 'classic' view Control Panel.");
  1083. }
  1084. bSetBarricade = true;
  1085. }
  1086. if (bSetBarricade)
  1087. {
  1088. THR(CPL::SetControlPanelBarricadeStatus(vtb));
  1089. }
  1090. vtb = ::GetBarricadeStatus(TEXT("shell:ControlPanelFolder"));
  1091. if (NULL != pbFixedByPolicy)
  1092. {
  1093. *pbFixedByPolicy = bFixedByPolicy;
  1094. }
  1095. TraceMsg(TF_CPANEL, "Barricade is %s", VARIANT_TRUE == vtb ? TEXT("ON") : TEXT("OFF"));
  1096. DBG_EXIT(FTF_CPANEL, "CPL::GetBarricadeStatus");
  1097. return vtb;
  1098. }
  1099. //
  1100. // Checks for the existance of the "HKCU\Control Panel\Opened" reg value.
  1101. // If this value does not exist or it contains a number less than what
  1102. // is expected, we assume the control panel has not been opened by this
  1103. // user. The 'expected' value is then written at this location in the
  1104. // registry to indicate to subsequent calls that the user has indeed
  1105. // already opened Control Panel. If future versions of the OS need
  1106. // to again trigger this "first run" behavior following upgrades,
  1107. // simply increment this expected value in the code below.
  1108. //
  1109. bool
  1110. CPL::IsFirstRunForThisUser(
  1111. void
  1112. )
  1113. {
  1114. bool bFirstRun = true; // Assume first run.
  1115. HKEY hkey;
  1116. DWORD dwResult = RegOpenKeyEx(HKEY_CURRENT_USER,
  1117. REGSTR_PATH_CONTROLPANEL,
  1118. 0,
  1119. KEY_QUERY_VALUE | KEY_SET_VALUE,
  1120. &hkey);
  1121. if (ERROR_SUCCESS == dwResult)
  1122. {
  1123. DWORD dwType;
  1124. DWORD dwData;
  1125. DWORD cbData = sizeof(dwData);
  1126. const TCHAR szValueName[] = TEXT("Opened");
  1127. //
  1128. // Increment this value if you want to re-trigger
  1129. // this 'first run' state on future versions.
  1130. //
  1131. const DWORD dwTestValue = 1;
  1132. dwResult = RegQueryValueEx(hkey,
  1133. szValueName,
  1134. NULL,
  1135. &dwType,
  1136. (LPBYTE)&dwData,
  1137. &cbData);
  1138. if (ERROR_SUCCESS == dwResult)
  1139. {
  1140. if (REG_DWORD == dwType && dwData >= dwTestValue)
  1141. {
  1142. bFirstRun = false;
  1143. }
  1144. }
  1145. else if (ERROR_FILE_NOT_FOUND != dwResult)
  1146. {
  1147. TraceMsg(TF_ERROR, "Error %d reading Control Panel 'first run' value from registry", dwResult);
  1148. }
  1149. if (bFirstRun)
  1150. {
  1151. //
  1152. // Write our value so we know user has opened
  1153. // Control Panel.
  1154. //
  1155. dwResult = RegSetValueEx(hkey,
  1156. szValueName,
  1157. 0,
  1158. REG_DWORD,
  1159. (CONST BYTE *)&dwTestValue,
  1160. sizeof(dwTestValue));
  1161. if (ERROR_SUCCESS != dwResult)
  1162. {
  1163. TraceMsg(TF_ERROR, "Error %d writing Control Panel 'first run' value to registry", dwResult);
  1164. }
  1165. }
  1166. RegCloseKey(hkey);
  1167. }
  1168. else
  1169. {
  1170. TraceMsg(TF_ERROR, "Error %d opening 'HKCU\\Control Panel' reg key", dwResult);
  1171. }
  1172. return bFirstRun;
  1173. }
  1174. //
  1175. // Use a private version of SetBarricadeStatus so that we don't
  1176. // clear the global barricade status whenever we turn on 'category' view
  1177. // (i.e. enable our barricade).
  1178. //
  1179. #define REGSTR_WEBVIEW_BARRICADEDFOLDERS (REGSTR_PATH_EXPLORER TEXT("\\WebView\\BarricadedFolders"))
  1180. HRESULT
  1181. CPL::SetControlPanelBarricadeStatus(
  1182. VARIANT_BOOL vtb
  1183. )
  1184. {
  1185. HRESULT hr = E_FAIL;
  1186. DWORD dwBarricade = (VARIANT_FALSE == vtb) ? 0 : 1;
  1187. if (SHRegSetUSValue(REGSTR_WEBVIEW_BARRICADEDFOLDERS,
  1188. TEXT("shell:ControlPanelFolder"),
  1189. REG_DWORD,
  1190. (void *)&dwBarricade,
  1191. sizeof(dwBarricade),
  1192. SHREGSET_FORCE_HKCU) == ERROR_SUCCESS)
  1193. {
  1194. hr = S_OK;
  1195. }
  1196. return THR(hr);
  1197. }