Handle access-denied errors and degrade gracefully |
“Out of memory or disk space” messages are misleading and frustrating to users that know they have lots of both. Many times the application mistakenly assumed it had sufficient access to the registry or file system without actually checking beforehand. Other times the level of error handling is simply deficient to report and handle the event that actually occurred.
While this isn't a new situation, stricter default security settings on new user accounts could make this situation more prevalent in a Windows 2000 environment. More than ever before, applications will need to detect and gracefully handle access denied errors and continue to run, even if in a diminished capacity.
Often this is caused by applications requesting a higher level of access than is actually needed to get the job done. More often than not, write access isn't required and these types of problems can be avoided by opening the key or file with the minimum rights needed. This allows your program to open the key or file successfully to view information even if it doesn't have write access.
If your application does need write access, then it must handle issues that arise from security settings. If your app only writes to the areas suggested by the guidelines, such as CSIDL_PERSONAL, the path returned from GetTempPath and HKEY_CURRENT_USER, you should be safe, but don’t ever assume that this will always be the case. The same caveat applies to read access. Don't assume that you'll always have read access to the registry and file system. Code defensively, anticipate write failures and handle them gracefully.
An access-denied error need not always be catastrophic. If you can’t save the file the user is working on, then warn them as soon as possible.
In this code sample we will handle the Preferences… menu option for a program. The program first checks to see if it can write the settings. If we can’t open the settings registry key with write access, we make the settings dialog read-only, and try to at least show the user the settings (degrading nicely). If we can’t read the user’s settings, we show the application defaults (handling the open error). If we can’t open the key for writing, we tell the user they can’t change the settings.
// Preference dialog
#define USER_SETTINGS_KEY _T("Software\\Microsoft\\STGraph\\Settings")
void CMainFrame::OnPreferences()
{
CDlgPref dlgPref; // Preferences dialog
HKEY hKey;
LONG lResult = ERROR_SUCCESS;
lResult = RegOpenKeyEx(HKEY_CURRENT_USER,
USER_SETTINGS_KEY, NULL,
KEY_SET_VALUE, &hKey)
if (lResult != ERROR_SUCCESS)
{
// Unable to open key with write access.
// Since we can't write the preferences,
// make the dialog Read-Only.
// (Degrade nicely)
dlgPref.m_fReadOnly = TRUE;
// We should also tell the user they
// can't change any of the settings
CString strUserWarning;
try
{
// Load the string from resources (localization)
strUserWarning.LoadString(IDS_SETTINGS_READONLY);
MessageBox(strUserWarning);
}
catch(CMemoryException *pex)
{
// Handle the out of memory error
}
// Open the key with read access
lResult = RegOpenKeyEx(HKEY_CURRENT_USER,
USER_SETTINGS_KEY, NULL
KEY_QUERY_VALUE, &hKey)
if (lResult != ERROR_SUCCESS)
{
// We can't even read the settings, oh well.
// Instead of showing them the user settings,
// we'll just show the application defaults
// since that's what we're using anyway.
// (Handle Error)
hKey = NULL;
}
}
if (hKey)
{
// Read the settings
}
else
{
// Use application provided defaults
}
// initilize and DoModal the dialog, etc
Handle write failures gracefully. If you can’t save preferences tell the user, then carry on. It’s not a good idea to stop the whole program for non-critical preferences that are inherently arbitrary. Use your own hardcoded default and continue running.
CSIDLs, File Functions, File I/O Overview, GetTempPath, SHGetFolderPath