How to write a Windows XP Application that stores user and application data in the correct location by using Visual C++

Article translations Article translations
Article ID: 310294 - View products that this article applies to.
This article was previously published under Q310294
Important This article contains information about how to modify the registry. Make sure that you back up the registry before you modify it. Make sure that you know how to restore the registry if a problem occurs. For more information about how to back up, restore, and modify the registry, click the following article number to view the article in the Microsoft Knowledge Base:
322756 How to back up and restore the registry in Windows
Note Microsoft Visual C++ .NET 2002, Microsoft Visual C++ .NET 2003, Microsoft Visual C++ 2005, and Microsoft Visual C++ 2008 support both the managed code model that is provided by the Microsoft .NET Framework and the unmanaged native Microsoft Windows code model. The information in this article applies only to unmanaged Visual C++ code
Expand all | Collapse all

On This Page

Summary

Applications work with two types of documents: those that the user creates and those that the application creates. Your applications should use the SHGetFolderPath shell function to retrieve valid folder locations to store data that is specific to the user and the application. This is essential for Windows XP applications to support multiple users who are using the same computer and to enable users to switch quickly.

This article describes how to store user data in the correct place in the following steps:
  • Create a Win32 application.
  • Add a Save As option to the File menu.
  • Use the standard File Save dialog box to default to the correct location.
  • Verify the correct file save location.
  • Remember the user's previous selection.
  • Verify the user's previous selection.
In the following steps, this article also describes where you must store application data and how to ensure that it is stored in the appropriate locations:
  • Classify application data.
  • Store application data in the correct location.
  • Use the registry judiciously.

Requirements

The following list outlines the recommended hardware, software, network infrastructure, skills, knowledge, and service packs that you need:
  • Windows XP Home Edition or Windows XP Professional
  • Visual Studio 2008, Visual Sudio 2005, or Visual Studio .NET.
  • Prior knowledge of Win32 application development

Create a Win32 Application

Start Visual Studio, and create a new Win32 application named SavingData.
  • In Visual Studio .NET, click Visual C++ Projects under Project Types, and then click Win32 Project under Templates. Accept the default application settings that the application setup wizard displays.
  • In Visual Studio 2005 or 2008, click Visual C++ under Project Types, and then click Win32 Project under Templates. Accept the default application settings.

Add a Save As Option to the File Menu

  1. Click Resource View, and then double-click IDC_SAVINGDATA.
  2. Add a Save As menu option to the File menu. Use IDM_FILE_SAVEAS as the ID of the menu item.
  3. Locate the application's WndProc window procedure within SavingData.cppm and add a new case statement within the WM_COMMAND section to process the Save As menu option. Call the OnFileSaveAs function, which you will create in the next section. This function takes no parameters.

    Your code should appear as follows:
    case WM_COMMAND:
        wmId    = LOWORD(wParam);
        wmEvent = HIWORD(wParam);
        // Parse the menu selections:
        switch (wmId)
        {
        case IDM_ABOUT:
            DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
            break;
        case IDM_EXIT:
            DestroyWindow(hWnd);
            break;
        case IDM_FILE_SAVEAS:
            OnFileSaveAs(hWnd);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        break;
    

Use the standard File Save dialog box to default to the correct location

When a user displays an application's File Save (or File Open) dialog box for the first time, the dialog box must default to the user's My Documents folder (or a descendant of My Documents, such as My Pictures for image data and My Music for audio files).

NOTE: You must never hard code a path within your application because you can never guarantee its physical location. For example, an Administrator may relocate the My Documents folder to a network location.
  1. At the top of SavingData.cpp, add the following include statements:
    #include <commdlg.h>   // for GetSaveFileName
    #include <shlobj.h>    // for SHGetFolderPath
    					
  2. Add the following prototype for the OnFileSaveAs function:
    void OnFileSaveAs( HWND hWnd );
    					
  3. Create the new OnFileSaveAs function. Within this function, use the SHGetFolderPath function in conjunction with the CSIDL_MYPICTURES CSIDL identifier to retrieve the correct folder location to store picture data. Pass this folder location to the GetSaveFileName function to display the standard File Save dialog box.

    Your code should appear as follows:
    void OnFileSaveAs(HWND hWnd)
    {
       OPENFILENAME openFile;
       TCHAR szPath[MAX_PATH];
       TCHAR szFile[MAX_PATH];
    
        // Initialize OPENFILENAME structure.
        ZeroMemory( &openFile, sizeof(OPENFILENAME) );
        openFile.lStructSize = sizeof(OPENFILENAME);
    
        szFile[0] = '\0';
        openFile.hwndOwner = hWnd;
        openFile.lpstrFile = szFile;
        openFile.nMaxFile = sizeof(szFile)/sizeof(*szFile);
    
        // Default to My Pictures. First, get its path.
        if ( SUCCEEDED( SHGetFolderPath( NULL, CSIDL_MYPICTURES, 
                                              NULL, 0, szPath ) ) )
        {
           // Set lpstrInitialDir to the path that SHGetFolderPath obtains. 
           // This causes GetSaveFileName to point to the My Pictures folder.
              openFile.lpstrInitialDir = szPath;
        }
        // Display the standard File Save dialog box, defaulting to My Pictures.
        if ( GetSaveFileName( &openFile ) == TRUE )
        {
            // User clicks the Save button.
            // Save the file
        }
        else
        {
           // User cancels the File Save dialog box.
        }
    }
    

Verify the Correct File Save Location

  1. Press the F5 key to build the project.
  2. Run the application, and click Save As from the File menu.
  3. Verify that the standard File Save dialog box defaults to the My Pictures folder, as CSIDL_MYPICTURES specifies.
  4. Click Cancel to close the dialog box, and close the application.

Remember the User's Previous Selection

For subsequent use of the File Save (or File Open) dialog box, it is recommended that the dialog box default to the user's previously selected location.

If you do not supply an initial folder location within the OPENFILENAME structure, GetSaveFileName (and GetOpenFileName) display the standard File Save or File Open dialog box, which points to the My Documents folder. In addition, if the user has used one of these dialog boxes previously and has chosen a non-default folder, these functions automatically default to the previously used folder.

To support the recommended best practice of targeting a specific folder location (such as My Pictures) the first time a user saves or loads a file, and to subsequently default to the user's previously selected location, you should use a Boolean variable to track whether this is the first time the user has performed the Save or Open operation.
  1. Create a static BOOL variable named bFirstSave in the OnFileSaveAs function, and initialize it to TRUE.
  2. Modify the code within OnFileSaveAs to call SHGetFolderPath and set the lpstrInitialDir member of the OPENFILENAME structure, only if bFirstSave is TRUE.
  3. If the user clicks Save in the File Save dialog box, set bFirstSave to FALSE.

    Your code should appear as follows:
    void OnFileSaveAs(HWND hWnd)
    {
        OPENFILENAME openFile;
        TCHAR szPath[MAX_PATH];
        TCHAR szFile[MAX_PATH];
        static BOOL bFirstSave = TRUE;
    
        // Initialize OPENFILENAME structure.
        ZeroMemory( &openFile, sizeof(OPENFILENAME) );
        openFile.lStructSize = sizeof(OPENFILENAME);
    
        szFile[0] = '\0';
        openFile.hwndOwner = hWnd;
        openFile.lpstrFile = szFile;
        openFile.nMaxFile = sizeof(szFile)/sizeof(*szFile);
    
        // The first time the user saves a document, default to My Pictures.
        if ( TRUE == bFirstSave )
        {
            if ( SUCCEEDED( SHGetFolderPath( NULL, CSIDL_MYPICTURES, 
                NULL, 0, szPath ) ) )
            {
                // Set lpstrInitialDir to the path that SHGetFolderPath obtains.
                // This causes GetSaveFileName to point to the My Pictures folder.
                openFile.lpstrInitialDir = szPath;
            }
        }
        // Display standard File Save dialog box, defaulting to My Pictures
        // or the user's previously selected location.
        if ( GetSaveFileName( &openFile ) == TRUE )
        {
            // User clicks Save.
            // Save the file.
            bFirstSave = FALSE;
        }
        else
        {
            // User cancels the File Save dialog box.
        }
    }
    

Verify the User's Previous Selection

  1. Build the project, and run the application.
  2. From the File menu, click Save As.
  3. Browse from the My Pictures folder to the My Documents folder, select a file, and click Save.
  4. From the File menu, click Save As again.
  5. Verify that the dialog box defaults to your previous selection (in this case, My Documents).
  6. Click Cancel to dismiss the dialog box, and close the application.
  7. Run the application, and click Save As from the File menu.
  8. Verify that the dialog box defaults back to the My Pictures folder.
  9. Close the dialog box, and quit the application.

Classify Application Data

You should not store application-specific data (such as temporary files, user preferences, application configuration files, and so on) in the My Documents folder. Instead, use either an appropriate location in the Windows Registry (for data that does not exceed 64 kilobytes) or an application-specific file that is located in a valid Application Data folder.

It is important to store application data in the correct location to allow several people to use the same computer without corrupting or overwriting each other's data and settings.

To determine the most appropriate location for your application data, use the following categories to classify your data:
  • For each user (roaming): This category describes application data that is specific to a particular user and should be available to the user as he or she moves between computers within a domain (for example, a custom dictionary). Note that this setting does not apply to applications that are not designed to run in a domain environment.
  • For each user (non-roaming): This category describes application data that is specific to a particular user but applies only to a single computer (for example, a user-specified monitor resolution).
  • For each computer (non-user specific and non-roaming): This category describes application data that applies to all users and to a specific computer (for example, an application dictionary, a log file, or a temporary file).

Store Application Data in the Correct Location

You use the SHGetFolderPath function to retrieve the correct Application Data folder. Do not store application data directly in this folder. Instead, use the PathAppend function to append a subfolder to the path that SHGetFolderPath returns. Make sure that you use the following convention:
Company Name\Product Name\Product Version
For example, the resultant full path may appear as follows:
\Documents and Settings\All Users\Application Data\My Company\My Product\1.0
To locate the correct Application Data folder, pass the appropriate CSIDL value, based on the category of your application data.
  • For each user (roaming) data, use the CSIDL_APPDATA value. This defaults to the following path:
    \Documents and Settings\<User Name>\Application Data
  • For each user (non-roaming) data, use the CSIDL_LOCAL_APPDATA value. This defaults to the following path:
    \Documents and Settings\<User Name>\Local Settings\Application Data
  • For each computer (non-user specific and non-roaming) data, use the CSIDL_COMMON_APPDATA value. This defaults to the following path:
    \Documents and Settings\All Users\Application Data
The following code fragment demonstrates how to open a temporary log file, which is located beneath CSIDL_COMMON_APPDATA:
include <shlwapi.h>
#pragma comment(lib,"shlwapi.lib")
void CreateTemporaryFile()
{
   TCHAR szPath[MAX_PATH];
   // Get path for each computer, non-user specific and non-roaming data.
   if ( SUCCEEDED( SHGetFolderPath( NULL, CSIDL_COMMON_APPDATA, 
                                    NULL, 0, szPath ) ) )
   {
      TCHAR szTempFileName[MAX_PATH];
      // Append product-specific path - this path needs to already exist
      // for GetTempFileName to succeed.
      PathAppend( szPath, _T("\\My Company\\My Product\\1.0\\") );
      // Generate a temporary file name within this folder.
      if (GetTempFileName( szPath, 
                           _T("PRE"),
                           0,
                           szTempFileName ) != 0 )
      {
         HANDLE hFile = NULL;
         // Open the file.
         if (( hFile = CreateFile( szTempFileName, 
                                   GENERIC_WRITE, 
                                   0, 
                                   NULL, 
                                   CREATE_ALWAYS, 
                                   FILE_ATTRIBUTE_NORMAL, 
                                   NULL )) != INVALID_HANDLE_VALUE )
         {
            // Write temporary data (code omitted).
            CloseHandle( hFile );
         }
      }
      else
          DWORD err = GetLastError();
   }
}

Use the Registry Judiciously

Important This section, method, or task contains steps that tell you how to modify the registry. However, serious problems might occur if you modify the registry incorrectly. Therefore, make sure that you follow these steps carefully. For added protection, back up the registry before you modify it. Then, you can restore the registry if a problem occurs. For more information about how to back up and restore the registry, click the following article number to view the article in the Microsoft Knowledge Base:
322756 How to back up and restore the registry in Windows

You can also use the registry to store small amounts of application data. For data that exceeds 64 kilobytes (KB), you must use an Application Data folder. Observe the following guidelines when you use the registry to store application data:
  • For small amounts of user data, use the HKEY_CURRENT_USER (HKCU) registry key.
  • For small amounts of computer data, use the HKEY_LOCAL_MACHINE (HKLM) registry key. Your application should not write to HKLM at run time because, by default, non-administrator users only have read-only access to the HKLM tree.
  • At installation time, your application must not store more than a total of 128 KB across HKCU and HKLM.
  • Component Object Model (COM) components are registered beneath the HKEY_CLASSES_ROOT (HKCR) registry key. The 128 KB maximum does not include HKCR.
  • When you write to HKLM or HKCU, you must create keys for the company name, product name, and product version number, as follows:
    HKLM\Software\Company Name\Product Name\Product Version
    HKCU\Software\Company Name\Product Name\Product Version
  • Use the registry functions (such as RegCreateKeyEx and RegSetValueEx) to read and write registry entries.

Troubleshooting

  • To help ensure that applications run on earlier versions of Windows in addition to Windows XP, always link to the SHGetFolderPath implementation in Shfolder.dll. Although Windows XP includes SHGetFolderPath in Shell32.dll, earlier versions of Windows may not support the function within this dynamic-link library (DLL).
  • Shfolder.dll is a redistributable component and may be distributed with your applications.
  • Do not store fully-qualified paths to the My Documents folder (or other system folders) within an application-specific place such as a file list of most recently used files because a user or Administrator may relocate these folders between successive uses of your application.

References

For other top-hit Visual C++ .NET Microsoft Knowledge Base articles, visit the Visual C++ .NET Support Center at the following Microsoft Web site:
Visual C++ .NET (2002) Support Center
http://support.microsoft.com/default.aspx?xmlid=fh%3BEN-US%3Bvcnet
 For more information about the complete set of folders that SHGetFolderPath can identify, see the following Microsoft Platform Software Development Kit (SDK) documentation:
CSIDL
http://msdn.microsoft.com/en-us/library/bb762494.aspx
For more information about shell programming in general, see the following Microsoft Developer Network (MSDN) Web site:
Shell Programmers Guide
http://msdn2.microsoft.com/en-us/library/bb776778.aspx

Properties

Article ID: 310294 - Last Review: October 16, 2012 - Revision: 4.0
Applies to
  • Microsoft Visual C++ 2008 Express Edition
  • Microsoft Visual C++ 2005 Express Edition
  • Microsoft Visual C++ .NET 2003 Standard Edition
  • Microsoft Visual C++ .NET 2002 Standard Edition
Keywords: 
kbhowtomaster kbnewsgrouplink KB310294

Give Feedback

 

Contact us for more help

Contact us for more help
Connect with Answer Desk for expert help.
Get more support from smallbusiness.support.microsoft.com