windows-nt/Source/XPSP1/NT/shell/shell32/cputil.cpp
2020-09-26 16:20:57 +08:00

1472 lines
37 KiB
C++

//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 2000
//
// File: cputil.cpp
//
//--------------------------------------------------------------------------
#include "shellprv.h"
#include "cpviewp.h"
#include "cputil.h"
HRESULT
CPL::ResultFromLastError(
void
)
{
const DWORD dwError = GetLastError();
return HRESULT_FROM_WIN32(dwError);
}
//
// 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];
lstrcpynW(szFile, pszStrDesc, ARRAYSIZE(szFile)); // the below writes this buffer
int iStrID = PathParseIconLocationW(szFile);
if (iStrID < 0)
{
iStrID = -iStrID; // support ",-id" syntax
}
HRESULT hr;
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 = CPL::ResultFromLastError();
}
FreeLibrary(hLib);
}
else
{
hr = CPL::ResultFromLastError();
}
return THR(hr);
}
//
// Loads a bitmap based upon the description.
// Example: shell32,-42
//
// lpBitmapDesc - contains the bitmap description
// hInstTheme - instance handle of theme dll
//
HRESULT
CPL::LoadBitmapFromResource(
LPCWSTR pszBitmapDesc,
HINSTANCE hInstTheme,
UINT uiLoadFlags,
HBITMAP *phBitmapOut
)
{
ASSERT(NULL != pszBitmapDesc);
ASSERT(NULL != phBitmapOut);
ASSERT(!IsBadWritePtr(phBitmapOut, sizeof(*phBitmapOut)));
HRESULT hr = E_FAIL;
HBITMAP hBitmap = NULL;
WCHAR szFile[MAX_PATH];
lstrcpynW(szFile, pszBitmapDesc, ARRAYSIZE(szFile)); // the below writes this buffer
int iBitmapID = PathParseIconLocationW(szFile);
if (iBitmapID < 0)
{
iBitmapID = -iBitmapID; // support ",-id" syntax
}
// Load the module to get the bitmap from
HMODULE hLib = LoadLibraryExW(szFile, NULL, LOAD_LIBRARY_AS_DATAFILE);
if (hLib)
{
hBitmap = (HBITMAP)LoadImage(hLib, MAKEINTRESOURCE(iBitmapID), IMAGE_BITMAP, 0, 0, uiLoadFlags);
if (NULL != hBitmap)
{
hr = S_OK;
}
else
{
hr = CPL::ResultFromLastError();
}
FreeLibrary(hLib);
}
else
{
// if loadlibrary failed to find the dll, try loading the bitmap from the
// theme dll
hBitmap = (HBITMAP)LoadImage(hInstTheme, MAKEINTRESOURCE(iBitmapID), IMAGE_BITMAP, 0, 0, uiLoadFlags);
if (NULL != hBitmap)
{
hr = S_OK;
}
else
{
hr = CPL::ResultFromLastError();
}
}
*phBitmapOut = hBitmap;
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 = CPL::ResultFromLastError();
}
FreeLibrary(hModule);
}
else
{
hr = CPL::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];
lstrcpynW(szResource, pszResource, ARRAYSIZE(szResource));
HRESULT hr = E_FAIL;
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)))
{
StrCpyN(pszCanonicalName, aTabMap[iTab].pszCanoncialTabName, cchSize);
}
}
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);
}
HRESULT
CPL::BrowsePathInPlace(
LPCWSTR pszPath,
IShellBrowser *psb
)
{
ASSERT(NULL != pszPath);
ASSERT(NULL != psb);
HRESULT hr = E_FAIL;
LPITEMIDLIST pidl = ILCreateFromPath(pszPath);
if (NULL != pidl)
{
hr = CPL::BrowseIDListInPlace(pidl, psb);
ILFree(pidl);
}
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);
}
WCHAR szSelect[160];
szSelect[0] = L'\0';
if (NULL != pszSelect)
{
wnsprintf(szSelect, ARRAYSIZE(szSelect), L"&select=Unmapped/Control_Panel/%s", pszSelect);
}
//
// 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
//
wnsprintf(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 S_OK;
}
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 = CPL::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);
}