Handle access-denied errors and degrade gracefully

Handle access-denied errors and degrade gracefully

Benefits

Description

“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.

Code Sample

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

Considerations

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.

See Also

CSIDLs, File Functions, File I/O Overview, GetTempPath, SHGetFolderPath