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.

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