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
1377 lines
36 KiB
//+-------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
//
|
|
// Copyright (C) Microsoft Corporation, 2000
|
|
//
|
|
// File: cputil.cpp
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
#include "shellprv.h"
|
|
|
|
#include "cpviewp.h"
|
|
#include "cputil.h"
|
|
|
|
|
|
//
|
|
// Loads a string based upon the description.
|
|
// Example: shell32,42
|
|
//
|
|
// lpStrDesc - contains the string description
|
|
//
|
|
HRESULT
|
|
CPL::LoadStringFromResource(
|
|
LPCWSTR pszStrDesc,
|
|
LPWSTR *ppszOut
|
|
)
|
|
{
|
|
ASSERT(NULL != pszStrDesc);
|
|
ASSERT(NULL != ppszOut);
|
|
ASSERT(!IsBadWritePtr(ppszOut, sizeof(*ppszOut)));
|
|
|
|
*ppszOut = NULL;
|
|
|
|
WCHAR szFile[MAX_PATH];
|
|
HRESULT hr = StringCchCopyW(szFile, ARRAYSIZE(szFile), pszStrDesc); // the below writes this buffer
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
int iStrID = PathParseIconLocationW(szFile);
|
|
if (iStrID < 0)
|
|
{
|
|
iStrID = -iStrID; // support ",-id" syntax
|
|
}
|
|
|
|
HMODULE hLib = LoadLibraryExW(szFile, NULL, LOAD_LIBRARY_AS_DATAFILE);
|
|
if (hLib)
|
|
{
|
|
WCHAR szTemp[INFOTIPSIZE]; // INFOTIPSIZE is the largest string type we're expected to load
|
|
if (0 < LoadStringW(hLib, (UINT)iStrID, szTemp, ARRAYSIZE(szTemp)))
|
|
{
|
|
hr = SHStrDup(szTemp, ppszOut);
|
|
}
|
|
else
|
|
{
|
|
hr = ResultFromLastError();
|
|
}
|
|
FreeLibrary(hLib);
|
|
}
|
|
else
|
|
{
|
|
hr = ResultFromLastError();
|
|
}
|
|
}
|
|
|
|
return THR(hr);
|
|
}
|
|
|
|
|
|
//
|
|
// Shell icon functions deal in terms of "small" and "large" icons.
|
|
// This function determines which should be used for a
|
|
// given eCPIMGSIZE value.
|
|
//
|
|
bool
|
|
CPL::ShouldUseSmallIconForDesiredSize(
|
|
eCPIMGSIZE eSize
|
|
)
|
|
{
|
|
UINT cx;
|
|
UINT cy;
|
|
ImageDimensionsFromDesiredSize(eSize, &cx, &cy);
|
|
|
|
if (int(cx) <= GetSystemMetrics(SM_CXSMICON))
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
//
|
|
// This function returns a eCPIMGSIZE value to pixel dimensions.
|
|
// This indirection lets us specify image sizes in abstract terms
|
|
// then convert to physical pixel dimensions when required. If
|
|
// you want to change the size of images used for a particular
|
|
// Control Panel UI item type, this is where you change it.
|
|
//
|
|
void
|
|
CPL::ImageDimensionsFromDesiredSize(
|
|
eCPIMGSIZE eSize,
|
|
UINT *pcx,
|
|
UINT *pcy
|
|
)
|
|
{
|
|
ASSERT(NULL != pcx);
|
|
ASSERT(!IsBadWritePtr(pcx, sizeof(*pcx)));
|
|
ASSERT(NULL != pcy);
|
|
ASSERT(!IsBadWritePtr(pcy, sizeof(*pcy)));
|
|
|
|
*pcx = *pcy = 0;
|
|
|
|
//
|
|
// This table converts eCPIMGSIZE values into actual
|
|
// image size values. A couple of things to note:
|
|
//
|
|
// 1. If you want to change the image size associated with
|
|
// an eIMGSIZE value, simply change these numbers.
|
|
//
|
|
// 2. If actual image size is dependent upon some system
|
|
// configuration parameter, do the interpretation of
|
|
// that parameter here making the size a function
|
|
// of that parameter.
|
|
//
|
|
static const SIZE rgSize[] = {
|
|
{ 16, 16 }, // eCPIMGSIZE_WEBVIEW
|
|
{ 16, 16 }, // eCPIMGSIZE_TASK
|
|
{ 48, 48 }, // eCPIMGSIZE_CATEGORY
|
|
{ 32, 32 }, // eCPIMGSIZE_BANNER
|
|
{ 32, 32 } // eCPIMGSIZE_APPLET
|
|
};
|
|
|
|
ASSERT(int(eSize) >= 0 && int(eSize) < ARRAYSIZE(rgSize));
|
|
|
|
*pcx = rgSize[eSize].cx;
|
|
*pcy = rgSize[eSize].cy;
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
CPL::LoadIconFromResourceID(
|
|
LPCWSTR pszModule,
|
|
int idIcon,
|
|
eCPIMGSIZE eSize,
|
|
HICON *phIcon
|
|
)
|
|
{
|
|
ASSERT(NULL != pszModule);
|
|
ASSERT(NULL != phIcon);
|
|
ASSERT(!IsBadWritePtr(phIcon, sizeof(*phIcon)));
|
|
ASSERT(0 < idIcon);
|
|
|
|
HRESULT hr = E_FAIL;
|
|
HICON hIcon = NULL;
|
|
HMODULE hModule = LoadLibraryExW(pszModule, NULL, LOAD_LIBRARY_AS_DATAFILE);
|
|
if (hModule)
|
|
{
|
|
UINT cxIcon;
|
|
UINT cyIcon;
|
|
|
|
ImageDimensionsFromDesiredSize(eSize, &cxIcon, &cyIcon);
|
|
|
|
hIcon = (HICON)LoadImage(hModule,
|
|
MAKEINTRESOURCE(idIcon),
|
|
IMAGE_ICON,
|
|
cxIcon,
|
|
cyIcon,
|
|
0);
|
|
|
|
if (NULL != hIcon)
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hr = ResultFromLastError();
|
|
}
|
|
FreeLibrary(hModule);
|
|
}
|
|
else
|
|
{
|
|
hr = ResultFromLastError();
|
|
}
|
|
|
|
*phIcon = hIcon;
|
|
return THR(hr);
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
CPL::LoadIconFromResourceIndex(
|
|
LPCWSTR pszModule,
|
|
int iIcon,
|
|
eCPIMGSIZE eSize,
|
|
HICON *phIcon
|
|
)
|
|
{
|
|
ASSERT(NULL != pszModule);
|
|
ASSERT(NULL != phIcon);
|
|
ASSERT(!IsBadWritePtr(phIcon, sizeof(*phIcon)));
|
|
|
|
if (-1 == iIcon)
|
|
{
|
|
//
|
|
// Special case. -1 is an invalid icon index/id.
|
|
//
|
|
iIcon = 0;
|
|
}
|
|
|
|
HICON hIcon = NULL;
|
|
HRESULT hr = E_FAIL;
|
|
if (CPL::ShouldUseSmallIconForDesiredSize(eSize))
|
|
{
|
|
if (0 < ExtractIconExW(pszModule, iIcon, NULL, &hIcon, 1))
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
TraceMsg(TF_ERROR, "ExtractIconEx failed for small icon (index %d) in module \"%s\"", iIcon, pszModule);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (0 < ExtractIconExW(pszModule, iIcon, &hIcon, NULL, 1))
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
TraceMsg(TF_ERROR, "ExtractIconEx failed for large icon (index %d) in module \"%s\"", iIcon, pszModule);
|
|
}
|
|
}
|
|
*phIcon = hIcon;
|
|
return THR(hr);
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
CPL::LoadIconFromResource(
|
|
LPCWSTR pszResource,
|
|
eCPIMGSIZE eSize,
|
|
HICON *phIcon
|
|
)
|
|
{
|
|
ASSERT(NULL != pszResource);
|
|
ASSERT(NULL != phIcon);
|
|
ASSERT(!IsBadWritePtr(phIcon, sizeof(*phIcon)));
|
|
|
|
*phIcon = NULL;
|
|
|
|
//
|
|
// PathParseIconLocation modifies it's input string.
|
|
//
|
|
WCHAR szResource[MAX_PATH];
|
|
HRESULT hr = StringCchCopyW(szResource, ARRAYSIZE(szResource), pszResource);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
int idIcon = PathParseIconLocationW(szResource);
|
|
if (-1 == idIcon)
|
|
{
|
|
//
|
|
// Special case. -1 is an invalid icon ID.
|
|
//
|
|
idIcon = 0;
|
|
}
|
|
|
|
if (0 > idIcon)
|
|
{
|
|
hr = CPL::LoadIconFromResourceID(szResource, -idIcon, eSize, phIcon);
|
|
}
|
|
else
|
|
{
|
|
hr = CPL::LoadIconFromResourceIndex(szResource, idIcon, eSize, phIcon);
|
|
}
|
|
}
|
|
return THR(hr);
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
CPL::ExtractIconFromPidl(
|
|
IShellFolder *psf,
|
|
LPCITEMIDLIST pidl,
|
|
eCPIMGSIZE eSize,
|
|
HICON *phIcon
|
|
)
|
|
{
|
|
ASSERT(NULL != psf);
|
|
ASSERT(NULL != pidl);
|
|
ASSERT(NULL != phIcon);
|
|
ASSERT(!IsBadWritePtr(phIcon, sizeof(*phIcon)));
|
|
|
|
*phIcon = NULL;
|
|
|
|
IExtractIcon *pei;
|
|
HRESULT hr = psf->GetUIObjectOf(NULL, 1, &pidl, IID_IExtractIcon, NULL, (void **)&pei);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
TCHAR szFile[MAX_PATH];
|
|
INT iIcon;
|
|
UINT uFlags = 0;
|
|
hr = pei->GetIconLocation(GIL_FORSHELL,
|
|
szFile,
|
|
ARRAYSIZE(szFile),
|
|
&iIcon,
|
|
&uFlags);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (0 == (GIL_NOTFILENAME & uFlags))
|
|
{
|
|
hr = CPL::LoadIconFromResourceIndex(szFile, iIcon, eSize, phIcon);
|
|
}
|
|
else
|
|
{
|
|
HICON hIconLarge = NULL;
|
|
HICON hIconSmall = NULL;
|
|
|
|
const int cxIcon = GetSystemMetrics(SM_CXICON);
|
|
const int cxSmIcon = GetSystemMetrics(SM_CXSMICON);
|
|
|
|
hr = pei->Extract(szFile,
|
|
iIcon,
|
|
&hIconLarge,
|
|
&hIconSmall,
|
|
MAKELONG(cxIcon, cxSmIcon));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (CPL::ShouldUseSmallIconForDesiredSize(eSize))
|
|
{
|
|
*phIcon = hIconSmall;
|
|
hIconSmall = NULL;
|
|
}
|
|
else
|
|
{
|
|
*phIcon = hIconLarge;
|
|
hIconLarge = NULL;
|
|
}
|
|
}
|
|
//
|
|
// Destroy any icons not being returned.
|
|
//
|
|
if (NULL != hIconSmall)
|
|
{
|
|
DestroyIcon(hIconSmall);
|
|
}
|
|
if (NULL != hIconLarge)
|
|
{
|
|
DestroyIcon(hIconLarge);
|
|
}
|
|
}
|
|
}
|
|
pei->Release();
|
|
}
|
|
ASSERT(FAILED(hr) || NULL != *phIcon);
|
|
if (NULL == *phIcon)
|
|
{
|
|
//
|
|
// If by-chance a NULL icon handle is retrieved, we don't
|
|
// want to return a success code.
|
|
//
|
|
hr = E_FAIL;
|
|
}
|
|
return THR(hr);
|
|
}
|
|
|
|
|
|
|
|
|
|
// Checks the given restriction. Returns TRUE (restricted) if the
|
|
// specified key/value exists and is non-zero, false otherwise
|
|
BOOL
|
|
DeskCPL_CheckRestriction(
|
|
HKEY hKey,
|
|
LPCWSTR lpszValueName
|
|
)
|
|
{
|
|
ASSERT(NULL != lpszValueName);
|
|
|
|
DWORD dwData;
|
|
DWORD dwSize = sizeof(dwData);
|
|
|
|
if ((ERROR_SUCCESS == RegQueryValueExW(hKey,
|
|
lpszValueName,
|
|
NULL,
|
|
NULL,
|
|
(BYTE *)&dwData,
|
|
&dwSize))
|
|
&& dwData)
|
|
{
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// Function returns the actual tab index given the default tab index.
|
|
// The actual tab index will be different than the default value if there are
|
|
// various system policies in effect which disable some tabs
|
|
//
|
|
//
|
|
// To add further restrictions, modify the aTabMap to include the default tab
|
|
// index and the corresponding policy. Also, you should keep the eDESKCPLTAB enum
|
|
// in sync with the aTabMap array.
|
|
//
|
|
//
|
|
int
|
|
CPL::DeskCPL_GetTabIndex(
|
|
CPL::eDESKCPLTAB iTab,
|
|
OPTIONAL LPWSTR pszCanonicalName,
|
|
OPTIONAL DWORD cchSize
|
|
)
|
|
{
|
|
HKEY hKey;
|
|
int iTabActual = CPL::CPLTAB_ABSENT;
|
|
|
|
if (iTab >= 0 && iTab < CPL::CPLTAB_DESK_MAX)
|
|
{
|
|
//
|
|
// While adding more tabs, make sure that it is entered in the right position in the
|
|
// the array below. So, for example, if the default tab index of the new tab is 2, it
|
|
// should be the aTabMap[2] entry (Currently CPLTAB_DESK_APPEARANCE is
|
|
// the one with tab index = 2). You will have to modify eDESKCPLTAB accordingly too.
|
|
//
|
|
struct
|
|
{
|
|
int nIndex; // the canonical name of the tab (don't use indexes because they change with policies or revs)
|
|
LPCWSTR pszCanoncialTabName; // the canonical name of the tab (don't use indexes because they change with policies or revs)
|
|
LPCWSTR pszRestriction; // corresponding restriction
|
|
} aTabMap[CPL::CPLTAB_DESK_MAX] = {
|
|
{ 0, SZ_DISPLAYCPL_OPENTO_DESKTOP, REGSTR_VAL_DISPCPL_NOBACKGROUNDPAGE }, // CPLTAB_DESK_BACKGROUND == 0
|
|
{ 1, SZ_DISPLAYCPL_OPENTO_SCREENSAVER, REGSTR_VAL_DISPCPL_NOSCRSAVPAGE }, // CPLTAB_DESK_SCREENSAVER == 1
|
|
{ 2, SZ_DISPLAYCPL_OPENTO_APPEARANCE, REGSTR_VAL_DISPCPL_NOAPPEARANCEPAGE }, // CPLTAB_DESK_APPEARANCE == 2
|
|
{ 3, SZ_DISPLAYCPL_OPENTO_SETTINGS, REGSTR_VAL_DISPCPL_NOSETTINGSPAGE } // CPLTAB_DESK_SETTINGS == 3
|
|
};
|
|
|
|
#ifdef DEBUG
|
|
//
|
|
// Verify proper initialization of the nIndex member of aTabMap[]
|
|
//
|
|
for (int k=0; k < ARRAYSIZE(aTabMap); k++)
|
|
{
|
|
ASSERT(aTabMap[k].nIndex == k);
|
|
}
|
|
#endif
|
|
|
|
iTabActual = aTabMap[iTab].nIndex;
|
|
|
|
//
|
|
// Note, if no policy is configured, the RegOpenKey call below will fail,
|
|
// in that case we return the default tab value, as entered in the
|
|
// map above.
|
|
//
|
|
if ((ERROR_SUCCESS == RegOpenKeyExW(HKEY_CURRENT_USER,
|
|
REGSTR_PATH_POLICIES L"\\" REGSTR_KEY_SYSTEM,
|
|
0,
|
|
KEY_QUERY_VALUE,
|
|
&hKey)))
|
|
{
|
|
//
|
|
// check all tabs to see if there is restriction
|
|
//
|
|
if (DeskCPL_CheckRestriction(hKey, aTabMap[iTab].pszRestriction))
|
|
{
|
|
// this tab does not exist, mark it as such
|
|
iTabActual = CPL::CPLTAB_ABSENT;
|
|
}
|
|
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
if (pszCanonicalName &&
|
|
(iTab >= 0) && (iTab < ARRAYSIZE(aTabMap)))
|
|
{
|
|
StringCchCopyW(pszCanonicalName, cchSize, aTabMap[iTab].pszCanoncialTabName);
|
|
}
|
|
}
|
|
return iTabActual;
|
|
}
|
|
|
|
|
|
bool
|
|
CPL::DeskCPL_IsTabPresent(
|
|
eDESKCPLTAB iTab
|
|
)
|
|
{
|
|
return CPLTAB_ABSENT != DeskCPL_GetTabIndex(iTab, NULL, 0);
|
|
}
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
//
|
|
// Policy checking routines
|
|
//
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
#define REGSTR_POLICIES_RESTRICTCPL REGSTR_PATH_POLICIES TEXT("\\Explorer\\RestrictCpl")
|
|
#define REGSTR_POLICIES_DISALLOWCPL REGSTR_PATH_POLICIES TEXT("\\Explorer\\DisallowCpl")
|
|
|
|
|
|
//
|
|
// Returns true if the specified app is listed under the specified key
|
|
//
|
|
// pszFileName can be a string resource ID in shell32 for things
|
|
// like "Fonts", "Printers and Faxes" etc.
|
|
//
|
|
// i.e. IsNameListedUnderKey(MAKEINTRESOURCE(IDS_MY_APPLET_TITLE), hkey);
|
|
//
|
|
// In this case, if the resource string cannot be loaded, the function
|
|
// returns 'false'.
|
|
//
|
|
bool
|
|
IsNameListedUnderKey(
|
|
LPCWSTR pszFileName,
|
|
LPCWSTR pszKey
|
|
)
|
|
{
|
|
bool bResult = FALSE;
|
|
HKEY hkey;
|
|
TCHAR szName[MAX_PATH];
|
|
|
|
if (IS_INTRESOURCE(pszFileName))
|
|
{
|
|
//
|
|
// The name is localized so we specify it as a string resource ID.
|
|
// Load it from shell32.dll.
|
|
//
|
|
if (0 < LoadString(HINST_THISDLL, PtrToUint(pszFileName), szName, ARRAYSIZE(szName)))
|
|
{
|
|
pszFileName = szName;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// If the load fails spit out a debug squirty and return false.
|
|
//
|
|
TW32(GetLastError());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (ERROR_SUCCESS == RegOpenKeyExW(HKEY_CURRENT_USER,
|
|
pszKey,
|
|
0,
|
|
KEY_QUERY_VALUE,
|
|
&hkey))
|
|
{
|
|
int iValue = 0;
|
|
WCHAR szValue[MAX_PATH];
|
|
WCHAR szData[MAX_PATH];
|
|
DWORD dwType, cbData, cchValue;
|
|
|
|
while (cbData = sizeof(szData),
|
|
cchValue = ARRAYSIZE(szValue),
|
|
ERROR_SUCCESS == RegEnumValue(hkey,
|
|
iValue,
|
|
szValue,
|
|
&cchValue,
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE) szData,
|
|
&cbData))
|
|
{
|
|
if (0 == lstrcmpiW(szData, pszFileName))
|
|
{
|
|
bResult = true;
|
|
break;
|
|
}
|
|
iValue++;
|
|
}
|
|
RegCloseKey(hkey);
|
|
}
|
|
return bResult;
|
|
}
|
|
|
|
|
|
//
|
|
// Method cloned from shell32\ctrlfldr.cpp (DoesCplPolicyAllow)
|
|
//
|
|
// pszName can be a string resource ID in shell32 for things
|
|
// like "Fonts", "Printers and Faxes" etc.
|
|
//
|
|
// i.e. IsAppletEnabled(NULL, MAKEINTRESOURCE(IDS_MY_APPLET_TITLE));
|
|
//
|
|
bool
|
|
CPL::IsAppletEnabled(
|
|
LPCWSTR pszFileName,
|
|
LPCWSTR pszName
|
|
)
|
|
{
|
|
bool bEnabled = true;
|
|
//
|
|
// It's illegal (and meaningless) for both to be NULL.
|
|
// Trap both with an assert and runtime check. I don't want any
|
|
// code to erroneously think an applet is enabled when it's not.
|
|
//
|
|
ASSERT(NULL != pszName || NULL != pszFileName);
|
|
if (NULL == pszName && NULL == pszFileName)
|
|
{
|
|
bEnabled = false;
|
|
}
|
|
else
|
|
{
|
|
if (SHRestricted(REST_RESTRICTCPL) &&
|
|
((NULL == pszName || !IsNameListedUnderKey(pszName, REGSTR_POLICIES_RESTRICTCPL)) &&
|
|
(NULL == pszFileName || !IsNameListedUnderKey(pszFileName, REGSTR_POLICIES_RESTRICTCPL))))
|
|
{
|
|
bEnabled = false;
|
|
}
|
|
if (bEnabled)
|
|
{
|
|
if (SHRestricted(REST_DISALLOWCPL) &&
|
|
((NULL == pszName || IsNameListedUnderKey(pszName, REGSTR_POLICIES_DISALLOWCPL)) ||
|
|
(NULL == pszFileName || IsNameListedUnderKey(pszFileName, REGSTR_POLICIES_DISALLOWCPL))))
|
|
{
|
|
bEnabled = false;
|
|
}
|
|
}
|
|
}
|
|
return bEnabled;
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
CPL::ControlPanelViewFromSite(
|
|
IUnknown *punkSite,
|
|
ICplView **ppview
|
|
)
|
|
{
|
|
ASSERT(NULL != punkSite);
|
|
ASSERT(NULL != ppview);
|
|
ASSERT(!IsBadWritePtr(ppview, sizeof(*ppview)));
|
|
|
|
*ppview = NULL;
|
|
|
|
HRESULT hr = IUnknown_QueryService(punkSite, SID_SControlPanelView, IID_ICplView, (void **)ppview);
|
|
return THR(hr);
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
CPL::ShellBrowserFromSite(
|
|
IUnknown *punkSite,
|
|
IShellBrowser **ppsb
|
|
)
|
|
{
|
|
ASSERT(NULL != punkSite);
|
|
ASSERT(NULL != ppsb);
|
|
ASSERT(!IsBadWritePtr(ppsb, sizeof(*ppsb)));
|
|
|
|
*ppsb = NULL;
|
|
|
|
HRESULT hr = IUnknown_QueryService(punkSite, SID_STopLevelBrowser, IID_PPV_ARG(IShellBrowser, ppsb));
|
|
return THR(hr);
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
CPL::BrowseIDListInPlace(
|
|
LPCITEMIDLIST pidl,
|
|
IShellBrowser *psb
|
|
)
|
|
{
|
|
ASSERT(NULL != pidl);
|
|
ASSERT(NULL != psb);
|
|
|
|
const UINT uFlags = SBSP_SAMEBROWSER | SBSP_OPENMODE | SBSP_ABSOLUTE;
|
|
HRESULT hr = psb->BrowseObject(pidl, uFlags);
|
|
return THR(hr);
|
|
}
|
|
|
|
|
|
//
|
|
// System Restore is allowed only for admins/owners.
|
|
// Also must check policy.
|
|
//
|
|
bool
|
|
CPL::IsSystemRestoreRestricted(
|
|
void
|
|
)
|
|
{
|
|
bool bRestricted = false;
|
|
|
|
//
|
|
// First check policy.
|
|
//
|
|
DWORD dwType;
|
|
DWORD dwValue;
|
|
DWORD cbValue = sizeof(dwValue);
|
|
|
|
DWORD dwResult = SHGetValueW(HKEY_LOCAL_MACHINE,
|
|
L"Software\\Policies\\Microsoft\\Windows NT\\SystemRestore",
|
|
L"DisableSR",
|
|
&dwType,
|
|
&dwValue,
|
|
&cbValue);
|
|
|
|
if (ERROR_SUCCESS == dwResult && REG_DWORD == dwType)
|
|
{
|
|
if (1 == dwValue)
|
|
{
|
|
//
|
|
// Sytem Restore is disabled by policy.
|
|
//
|
|
bRestricted = true;
|
|
}
|
|
}
|
|
|
|
if (!bRestricted)
|
|
{
|
|
//
|
|
// Not restricted by policy. Check for admin/owner.
|
|
//
|
|
if (!CPL::IsUserAdmin())
|
|
{
|
|
//
|
|
// User is not an admin.
|
|
//
|
|
bRestricted = true;
|
|
}
|
|
}
|
|
return bRestricted;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
HRESULT
|
|
ReadTestConfigurationFlag(
|
|
LPCWSTR pszValue,
|
|
BOOL *pbFlag
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DWORD dwValue = 0;
|
|
DWORD cbValue = sizeof(dwValue);
|
|
DWORD dwType;
|
|
|
|
DWORD dwResult = SHGetValueW(HKEY_CURRENT_USER,
|
|
REGSTR_PATH_CONTROLPANEL,
|
|
pszValue,
|
|
&dwType,
|
|
&dwValue,
|
|
&cbValue);
|
|
|
|
if (ERROR_SUCCESS != dwResult)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(dwResult);
|
|
}
|
|
else if (REG_DWORD != dwType)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
}
|
|
if (SUCCEEDED(hr) && NULL != pbFlag)
|
|
{
|
|
if (0 == dwValue)
|
|
{
|
|
*pbFlag = FALSE;
|
|
}
|
|
else
|
|
{
|
|
*pbFlag = TRUE;
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
enum eSKU
|
|
{
|
|
eSKU_SERVER,
|
|
eSKU_PROFESSIONAL,
|
|
eSKU_PERSONAL,
|
|
eSKU_NUMSKUS
|
|
};
|
|
|
|
|
|
HRESULT
|
|
ReadTestConfigurationSku(
|
|
eSKU *peSku
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
WCHAR szValue[MAX_PATH];
|
|
DWORD cbValue = sizeof(szValue);
|
|
DWORD dwType;
|
|
|
|
DWORD dwResult = SHGetValueW(HKEY_CURRENT_USER,
|
|
REGSTR_PATH_CONTROLPANEL,
|
|
L"SKU",
|
|
&dwType,
|
|
szValue,
|
|
&cbValue);
|
|
|
|
if (ERROR_SUCCESS != dwResult)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(dwResult);
|
|
}
|
|
else if (REG_SZ != dwType)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
}
|
|
if (SUCCEEDED(hr) && NULL != peSku)
|
|
{
|
|
static const struct
|
|
{
|
|
LPCWSTR pszValue;
|
|
eSKU sku;
|
|
|
|
} rgMap[] = {
|
|
{ L"personal", eSKU_PERSONAL },
|
|
{ L"professional", eSKU_PROFESSIONAL },
|
|
{ L"pro", eSKU_PROFESSIONAL },
|
|
{ L"server", eSKU_SERVER }
|
|
};
|
|
|
|
hr = E_FAIL;
|
|
for (int i = 0; i < ARRAYSIZE(rgMap); i++)
|
|
{
|
|
if (0 == lstrcmpiW(rgMap[i].pszValue, szValue))
|
|
{
|
|
*peSku = rgMap[i].sku;
|
|
hr = S_OK;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
BOOL
|
|
CPL::IsOsServer(
|
|
void
|
|
)
|
|
{
|
|
BOOL bServer = IsOS(OS_ANYSERVER);
|
|
|
|
#ifdef DEBUG
|
|
eSKU sku;
|
|
if (SUCCEEDED(ReadTestConfigurationSku(&sku)))
|
|
{
|
|
bServer = (eSKU_SERVER == sku);
|
|
}
|
|
#endif
|
|
|
|
return bServer;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
CPL::IsOsPersonal(
|
|
void
|
|
)
|
|
{
|
|
BOOL bPersonal = IsOS(OS_PERSONAL);
|
|
|
|
#ifdef DEBUG
|
|
eSKU sku;
|
|
if (SUCCEEDED(ReadTestConfigurationSku(&sku)))
|
|
{
|
|
bPersonal = (eSKU_PERSONAL == sku);
|
|
}
|
|
#endif
|
|
|
|
return bPersonal;
|
|
}
|
|
|
|
|
|
BOOL
|
|
CPL::IsOsProfessional(
|
|
void
|
|
)
|
|
{
|
|
BOOL bProfessional = IsOS(OS_PROFESSIONAL);
|
|
|
|
#ifdef DEBUG
|
|
eSKU sku;
|
|
if (SUCCEEDED(ReadTestConfigurationSku(&sku)))
|
|
{
|
|
bProfessional = (eSKU_PROFESSIONAL == sku);
|
|
}
|
|
#endif
|
|
|
|
return bProfessional;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
CPL::IsConnectedToDomain(
|
|
void
|
|
)
|
|
{
|
|
BOOL bDomain = IsOS(OS_DOMAINMEMBER);
|
|
|
|
#ifdef DEBUG
|
|
ReadTestConfigurationFlag(L"Domain", &bDomain);
|
|
#endif
|
|
|
|
return bDomain;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
CPL::IsUserAdmin(
|
|
void
|
|
)
|
|
{
|
|
BOOL bAdmin = ::IsUserAnAdmin();
|
|
|
|
#ifdef DEBUG
|
|
ReadTestConfigurationFlag(L"Admin", &bAdmin);
|
|
#endif
|
|
|
|
return bAdmin;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CPL::GetUserAccountType(
|
|
eACCOUNTTYPE *pType
|
|
)
|
|
{
|
|
ASSERT(NULL != pType);
|
|
ASSERT(!IsBadWritePtr(pType, sizeof(*pType)));
|
|
|
|
HRESULT hr = E_FAIL;
|
|
eACCOUNTTYPE acctype = eACCOUNTTYPE_UNKNOWN;
|
|
|
|
static const struct
|
|
{
|
|
DWORD rid; // Account relative ID.
|
|
eACCOUNTTYPE eType; // Type code to return.
|
|
|
|
} rgMap[] = {
|
|
{ DOMAIN_ALIAS_RID_ADMINS, eACCOUNTTYPE_OWNER },
|
|
{ DOMAIN_ALIAS_RID_POWER_USERS, eACCOUNTTYPE_STANDARD },
|
|
{ DOMAIN_ALIAS_RID_USERS, eACCOUNTTYPE_LIMITED },
|
|
{ DOMAIN_ALIAS_RID_GUESTS, eACCOUNTTYPE_GUEST }
|
|
};
|
|
|
|
for (int i = 0; i < ARRAYSIZE(rgMap); i++)
|
|
{
|
|
if (SHTestTokenMembership(NULL, rgMap[i].rid))
|
|
{
|
|
acctype = rgMap[i].eType;
|
|
hr = S_OK;
|
|
break;
|
|
}
|
|
}
|
|
ASSERT(eACCOUNTTYPE_UNKNOWN != acctype);
|
|
*pType = acctype;
|
|
return THR(hr);
|
|
}
|
|
|
|
|
|
//
|
|
// Create a URL to pass to HSS help.
|
|
// The URL created references the Control_Panel help topic.
|
|
//
|
|
HRESULT
|
|
CPL::BuildHssHelpURL(
|
|
LPCWSTR pszSelect, // Optional. NULL == base CP help.
|
|
LPWSTR pszURL,
|
|
UINT cchURL
|
|
)
|
|
{
|
|
ASSERT(NULL != pszURL);
|
|
ASSERT(!IsBadWritePtr(pszURL, cchURL * sizeof(*pszURL)));
|
|
ASSERT(NULL == pszSelect || !IsBadStringPtr(pszSelect, UINT(-1)));
|
|
|
|
//
|
|
// HSS has specific help content for 'limited' users.
|
|
// Default to a non-limited user.
|
|
//
|
|
bool bLimitedUser = false;
|
|
CPL::eACCOUNTTYPE accType;
|
|
if (SUCCEEDED(CPL::GetUserAccountType(&accType)))
|
|
{
|
|
bLimitedUser = (eACCOUNTTYPE_LIMITED == accType);
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
WCHAR szSelect[160];
|
|
szSelect[0] = L'\0';
|
|
if (NULL != pszSelect)
|
|
{
|
|
hr = StringCchPrintfW(szSelect, ARRAYSIZE(szSelect), L"&select=Unmapped/Control_Panel/%s", pszSelect);
|
|
}
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
//
|
|
// The URL can take one of 4 forms depending upon the category and account type.
|
|
//
|
|
// User Account CP View Help Content Displayed
|
|
// ---------------- ---------------- -----------------------
|
|
// Non-limited Category choice General CP help
|
|
// Non-limited Category Category-specific help
|
|
// Limited Category choice General CP help
|
|
// Limited Category Category-specific help
|
|
//
|
|
hr = StringCchPrintfW(pszURL,
|
|
cchURL,
|
|
L"hcp://services/subsite?node=Unmapped/%sControl_Panel&topic=MS-ITS%%3A%%25HELP_LOCATION%%25%%5Chs.chm%%3A%%3A/hs_control_panel.htm%s",
|
|
bLimitedUser ? L"L/" : L"",
|
|
szSelect);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
CPL::GetControlPanelFolder(
|
|
IShellFolder **ppsf
|
|
)
|
|
{
|
|
ASSERT(NULL != ppsf);
|
|
ASSERT(!IsBadWritePtr(ppsf, sizeof(*ppsf)));
|
|
|
|
*ppsf = NULL;
|
|
|
|
LPITEMIDLIST pidlCpanel;
|
|
HRESULT hr = SHGetSpecialFolderLocation(NULL, CSIDL_CONTROLS, &pidlCpanel);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IShellFolder *psfDesktop;
|
|
hr = SHGetDesktopFolder(&psfDesktop);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = psfDesktop->BindToObject(pidlCpanel, NULL, IID_IShellFolder, (void **)ppsf);
|
|
ATOMICRELEASE(psfDesktop);
|
|
}
|
|
ILFree(pidlCpanel);
|
|
}
|
|
return THR(hr);
|
|
}
|
|
|
|
|
|
//
|
|
// On successful return, caller is responsible for freeing
|
|
// returned buffer using LocalFree.
|
|
//
|
|
HRESULT
|
|
CPL::ExpandEnvironmentVars(
|
|
LPCTSTR psz,
|
|
LPTSTR *ppszOut
|
|
)
|
|
{
|
|
ASSERT(NULL != psz);
|
|
ASSERT(NULL != ppszOut);
|
|
ASSERT(!IsBadWritePtr(ppszOut, sizeof(*ppszOut)));
|
|
|
|
HRESULT hr = E_FAIL;
|
|
|
|
*ppszOut = NULL;
|
|
|
|
TCHAR szDummy[1];
|
|
DWORD dwResult = ExpandEnvironmentStrings(psz, szDummy, 0);
|
|
if (0 < dwResult)
|
|
{
|
|
const DWORD cchRequired = dwResult;
|
|
*ppszOut = (LPTSTR)LocalAlloc(LPTR, cchRequired * sizeof(TCHAR));
|
|
if (NULL != *ppszOut)
|
|
{
|
|
dwResult = ExpandEnvironmentStrings(psz, *ppszOut, cchRequired);
|
|
if (0 < dwResult)
|
|
{
|
|
ASSERT(dwResult <= cchRequired);
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
LocalFree(*ppszOut);
|
|
*ppszOut = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
if (0 == dwResult)
|
|
{
|
|
hr = ResultFromLastError();
|
|
}
|
|
return THR(hr);
|
|
}
|
|
|
|
|
|
//// from sdfolder.cpp
|
|
VARIANT_BOOL GetBarricadeStatus(LPCTSTR pszValueName);
|
|
|
|
|
|
#define REGSTR_POLICIES_EXPLORER TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer")
|
|
|
|
bool
|
|
CPL::CategoryViewIsActive(
|
|
bool *pbBarricadeFixedByPolicy
|
|
)
|
|
{
|
|
DBG_ENTER(FTF_CPANEL, "CPL::CategoryViewIsActive");
|
|
|
|
bool bActive = false;
|
|
bool bBarricadeFixedByPolicy = false;
|
|
|
|
//
|
|
// We don't provide category view when running WOW64.
|
|
//
|
|
if (!IsOS(OS_WOW6432))
|
|
{
|
|
SHELLSTATE ss;
|
|
const DWORD dwMask = SSF_WEBVIEW | SSF_WIN95CLASSIC;
|
|
SHGetSetSettings(&ss, dwMask, FALSE);
|
|
|
|
//
|
|
// WebView? Barricade status View type
|
|
// ----------- ---------------- ------------------------------
|
|
// Off On Classic view
|
|
// Off Off Classic view
|
|
// On On Category view (aka 'simple')
|
|
// On Off Classic view
|
|
//
|
|
// Note that these two shellstate settings encompass and are set
|
|
// by the shell restrictions REST_CLASSICSHELL and REST_NOWEBVIEW.
|
|
// Therefore, there is no reason to explicitely check those two restrictions.
|
|
//
|
|
if (ss.fWebView && !ss.fWin95Classic)
|
|
{
|
|
if (VARIANT_TRUE == CPL::GetBarricadeStatus(&bBarricadeFixedByPolicy))
|
|
{
|
|
bActive = true;
|
|
}
|
|
}
|
|
}
|
|
if (NULL != pbBarricadeFixedByPolicy)
|
|
{
|
|
*pbBarricadeFixedByPolicy = bBarricadeFixedByPolicy;
|
|
}
|
|
TraceMsg(TF_CPANEL, "Category view is %s.", bActive ? TEXT("ACTIVE") : TEXT("INACTIVE"));
|
|
DBG_EXIT(FTF_CPANEL, "CPL::CategoryViewIsActive");
|
|
return bActive;
|
|
}
|
|
|
|
|
|
//
|
|
// Control Panel uses the 'barricade status' to determine which view
|
|
// 'classic' or 'category' to display. Yes, this is overloading the
|
|
// meaning of 'barricade' as used in the shell. However, since the
|
|
// Control Panel does not use a barricade in it's usual sense, this
|
|
// is a reasonable application of the feature.
|
|
//
|
|
VARIANT_BOOL
|
|
CPL::GetBarricadeStatus(
|
|
bool *pbFixedByPolicy // Optional. May be NULL.
|
|
)
|
|
{
|
|
DBG_ENTER(FTF_CPANEL, "CPL::GetBarricadeStatus");
|
|
|
|
VARIANT_BOOL vtb;
|
|
DWORD dwType;
|
|
DWORD dwData;
|
|
DWORD cbData = sizeof(dwData);
|
|
bool bFixedByPolicy = false;
|
|
bool bSetBarricade = false;
|
|
|
|
//
|
|
// First handle any OOBE issues.
|
|
//
|
|
if (CPL::IsFirstRunForThisUser())
|
|
{
|
|
TraceMsg(TF_CPANEL, "First time this user has opened Control Panel");
|
|
//
|
|
// Determine the default view to display out-of-box.
|
|
//
|
|
// Server gets 'classic'.
|
|
// Non-servers get 'category'.
|
|
//
|
|
if (IsOS(OS_ANYSERVER))
|
|
{
|
|
//
|
|
// Default is 'classic'.
|
|
//
|
|
vtb = VARIANT_FALSE;
|
|
TraceMsg(TF_CPANEL, "Running on server. Default to 'classic' view Control Panel.");
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Default is 'category'.
|
|
//
|
|
vtb = VARIANT_TRUE;
|
|
TraceMsg(TF_CPANEL, "Running on non-server. Default to 'category' view Control Panel.");
|
|
}
|
|
bSetBarricade = true;
|
|
}
|
|
|
|
//
|
|
// Apply any 'force view type' policy. This will override
|
|
// the default out-of-box setting obtained above.
|
|
//
|
|
if (ERROR_SUCCESS == SHRegGetUSValue(REGSTR_POLICIES_EXPLORER,
|
|
TEXT("ForceClassicControlPanel"),
|
|
&dwType,
|
|
&dwData,
|
|
&cbData,
|
|
FALSE,
|
|
NULL,
|
|
0))
|
|
{
|
|
//
|
|
// policy exists
|
|
//
|
|
bFixedByPolicy = true;
|
|
if (0 == dwData)
|
|
{
|
|
//
|
|
// force the simple (category) view, ie, show barricade
|
|
//
|
|
vtb = VARIANT_TRUE;
|
|
TraceMsg(TF_CPANEL, "Policy forcing use of 'category' view Control Panel.");
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// force the classic (icon) view, ie, no barricade
|
|
//
|
|
vtb = VARIANT_FALSE;
|
|
TraceMsg(TF_CPANEL, "Policy forcing use of 'classic' view Control Panel.");
|
|
}
|
|
bSetBarricade = true;
|
|
}
|
|
|
|
if (bSetBarricade)
|
|
{
|
|
THR(CPL::SetControlPanelBarricadeStatus(vtb));
|
|
}
|
|
|
|
vtb = ::GetBarricadeStatus(TEXT("shell:ControlPanelFolder"));
|
|
if (NULL != pbFixedByPolicy)
|
|
{
|
|
*pbFixedByPolicy = bFixedByPolicy;
|
|
}
|
|
|
|
TraceMsg(TF_CPANEL, "Barricade is %s", VARIANT_TRUE == vtb ? TEXT("ON") : TEXT("OFF"));
|
|
DBG_EXIT(FTF_CPANEL, "CPL::GetBarricadeStatus");
|
|
return vtb;
|
|
}
|
|
|
|
|
|
//
|
|
// Checks for the existance of the "HKCU\Control Panel\Opened" reg value.
|
|
// If this value does not exist or it contains a number less than what
|
|
// is expected, we assume the control panel has not been opened by this
|
|
// user. The 'expected' value is then written at this location in the
|
|
// registry to indicate to subsequent calls that the user has indeed
|
|
// already opened Control Panel. If future versions of the OS need
|
|
// to again trigger this "first run" behavior following upgrades,
|
|
// simply increment this expected value in the code below.
|
|
//
|
|
bool
|
|
CPL::IsFirstRunForThisUser(
|
|
void
|
|
)
|
|
{
|
|
bool bFirstRun = true; // Assume first run.
|
|
HKEY hkey;
|
|
DWORD dwResult = RegOpenKeyEx(HKEY_CURRENT_USER,
|
|
REGSTR_PATH_CONTROLPANEL,
|
|
0,
|
|
KEY_QUERY_VALUE | KEY_SET_VALUE,
|
|
&hkey);
|
|
|
|
if (ERROR_SUCCESS == dwResult)
|
|
{
|
|
DWORD dwType;
|
|
DWORD dwData;
|
|
DWORD cbData = sizeof(dwData);
|
|
|
|
const TCHAR szValueName[] = TEXT("Opened");
|
|
//
|
|
// Increment this value if you want to re-trigger
|
|
// this 'first run' state on future versions.
|
|
//
|
|
const DWORD dwTestValue = 1;
|
|
|
|
dwResult = RegQueryValueEx(hkey,
|
|
szValueName,
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE)&dwData,
|
|
&cbData);
|
|
|
|
if (ERROR_SUCCESS == dwResult)
|
|
{
|
|
if (REG_DWORD == dwType && dwData >= dwTestValue)
|
|
{
|
|
bFirstRun = false;
|
|
}
|
|
}
|
|
else if (ERROR_FILE_NOT_FOUND != dwResult)
|
|
{
|
|
TraceMsg(TF_ERROR, "Error %d reading Control Panel 'first run' value from registry", dwResult);
|
|
}
|
|
|
|
if (bFirstRun)
|
|
{
|
|
//
|
|
// Write our value so we know user has opened
|
|
// Control Panel.
|
|
//
|
|
dwResult = RegSetValueEx(hkey,
|
|
szValueName,
|
|
0,
|
|
REG_DWORD,
|
|
(CONST BYTE *)&dwTestValue,
|
|
sizeof(dwTestValue));
|
|
|
|
if (ERROR_SUCCESS != dwResult)
|
|
{
|
|
TraceMsg(TF_ERROR, "Error %d writing Control Panel 'first run' value to registry", dwResult);
|
|
}
|
|
}
|
|
|
|
RegCloseKey(hkey);
|
|
}
|
|
else
|
|
{
|
|
TraceMsg(TF_ERROR, "Error %d opening 'HKCU\\Control Panel' reg key", dwResult);
|
|
}
|
|
return bFirstRun;
|
|
}
|
|
|
|
|
|
//
|
|
// Use a private version of SetBarricadeStatus so that we don't
|
|
// clear the global barricade status whenever we turn on 'category' view
|
|
// (i.e. enable our barricade).
|
|
//
|
|
#define REGSTR_WEBVIEW_BARRICADEDFOLDERS (REGSTR_PATH_EXPLORER TEXT("\\WebView\\BarricadedFolders"))
|
|
|
|
HRESULT
|
|
CPL::SetControlPanelBarricadeStatus(
|
|
VARIANT_BOOL vtb
|
|
)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
DWORD dwBarricade = (VARIANT_FALSE == vtb) ? 0 : 1;
|
|
|
|
if (SHRegSetUSValue(REGSTR_WEBVIEW_BARRICADEDFOLDERS,
|
|
TEXT("shell:ControlPanelFolder"),
|
|
REG_DWORD,
|
|
(void *)&dwBarricade,
|
|
sizeof(dwBarricade),
|
|
SHREGSET_FORCE_HKCU) == ERROR_SUCCESS)
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
return THR(hr);
|
|
}
|
|
|
|
|