There are many folders that you could store files in, but if you pick one that is not valid for some reason, it will give you problems in some circumstances - particularly if the user running your program is not logged in to Windows as an administrator (from Vista onwards, they usually aren't).

Where you should be storing the files depends on what those files are used for.


Files for your program to just read from, and not write to
If files are read-only and are only for use by your program (such as sound or pictures that your program loads while running), they belong in the same folder as the program itself, which you can detect using App.Path, eg:
Code:
strSoundFile = App.Path & "\MySound.wav"
'strSoundFile now contains something like: "C:\Program Files\My Program\MySound.wav"
Whatever tool you are using to create an installation package, it should provide options for placing files in this folder.


All other files
If the files can be written to (such as .ini files for program settings), or you want them to also be available for the user to open in other programs, you should not be using the application folder - even if you have gotten away with doing that in the past.

What is wrong with using the application folder?
The reasons you should not use the application folder include:
  • User Permissions (Windows 2000 and later)
    If the user who is running your program has limited permissions (ie: is not logged in as an Admin user), they may not have permission to work with files in that folder, in which case your program will get an error.

    Many programmers do not notice this, as they (and many users on Windows 2000/XP) log in as an Admin user - but some users do not have that option.

  • Virtualisation (Windows Vista [and possibly later versions too])
    On Vista if a user does not have permission to work with a file, Virtualisation comes in to effect.

    This is a process to deal with programs that seemed to work properly on Windows XP/2000, but only because they were not tested properly under limited user accounts - if they had been, the issues described in 'User Permissions' section above would have been noticed.

    What Virtualisation does is let the program think that it has got permission to work with those files, by creating copies of the files in a special location, and automatically re-directing the programs requests for those files.

    While this allows badly written programs to run on Vista, it is not a perfect solution - one example of this is that if one user runs the program and it makes changes to one of the files, it will not be seen by other users who run the program (because the virtualised files are per-user).

  • User inconvenience (PC backup/transfer) (Windows 2000 and later)
    When the user backs up their computer (or upgrades to a new computer, etc), their documents and program settings should be relatively easily to back-up/transfer, by simply copying the folders designed for that purpose (in Windows 2000/XP, that is "\Documents and Settings" and its sub-folders)

    This process is made easier in XP and later, which includes a Transfer Wizard program to do it (including options for which files to transfer).

    However, as your files are not in that folder, they will not be included - so the user will need to spend extra time to deal with your program. For a backup (rather than a transfer), they may not be aware of it - in which case by the time they realise it will be too late, and the files will be gone.

  • User inconvenience (System Restore) (Windows XP and later)
    The System Restore feature of XP and later allows the user to automatically reset the programs on their computer to an earlier point in time - but keeps their documents as they were.

    As your files are in a location designated as a program folder, they might be affected by System Restore - in which case the user would lose their settings/documents.
Many people claim that these issues are somehow the fault of Windows Vista, because they only noticed when they (or one of their users) upgraded to Vista. This is not the case however, as Microsoft have been telling developers for many years where files should (and should not) be stored, and some of the issues have been around since Windows 2000 (but went unnoticed by people who didn't test with lesser permissions).


Which folder(s) should be used?
Microsoft provides Developer Guidelines to explain to programmers (of all languages, not just VB6) how to interact with Windows - things such as where to place files, and how to install your program without affecting other programs that are already installed.

A good explanation of where your program should store files (and Registry settings) can be found in section 4.2 of the Application Specification for Microsoft Windows 2000 for Desktop Applications, which was provided with some editions of VB6.

In summary, these three special folders are the valid locations:
  • AppData - for the current Windows user, on any computer on the network
  • Local_AppData - for the current Windows user, on this computer only
  • CommonAppData - for all Windows users on this computer
You should create your own sub-folders within these folders (eg: AppData\YourProgramName) to put files in to, otherwise you might find that your files are edited/deleted by other programs!

If you want the user to be able to navigate to the files using Explorer/My Computer, it is appropriate to save them to the Documents folder (known as "My Documents" in Windows XP and 2000), but note that the user can easily edit or delete them without using your program.

Most tools for creating an installation package provide options for placing files in these folders.

Note that the CommonAppData folder needs care, as the security settings of the folder affect who can edit the files. If you create the folder as part of the installation, you may be able to get the installer to set the security so that all users can edit the files - if not you will need to add code to your program to do that.


What code can I use to determine the locations of those folders?
Some people hard-code part of the locations, and try to determine the rest based on the user name, however this is not safe for a few reasons - the username does not need to be part of the folder name (if the username changes, the folder name might not); the 'hard-coded' part of the path varies based on the version and language of Windows; and the folder can be moved/renamed too.

The proper way to detect the location of these special folders is to use routines that Microsoft provide for you, as they detect the actual location properly.

There are API functions (routines built in to Windows) that can do it for you, but they are relatively complex. If you want to see them, search the forums for CSIDL or KnownFolderID

A simpler way is to use the Shell object provided with Windows, which is designed for use from VB and scripting languages (and has the same effect as the API's which use CSIDL or KnownFolderID). To use it, just add this code to your project:
Code:
  '### Declarations - place this part in the Declarations section of a code file (at the very top of the code)
Public Enum eSpecialFolders
  SpecialFolder_AppData = &H1A        'for the current Windows user, on any computer on the network [Windows 98 or later]
  SpecialFolder_CommonAppData = &H23  'for all Windows users on this computer [Windows 2000 or later]
  SpecialFolder_LocalAppData = &H1C   'for the current Windows user, on this computer only [Windows 2000 or later]
  SpecialFolder_Documents = &H5       'the Documents folder for the current Windows user
End Enum

  '### The routine - place this part before/after any other rotuine
Public Function SpecialFolder(pFolder As eSpecialFolders) As String
'Returns the path to the specified special folder (AppData etc)

Dim objShell  As Object
Dim objFolder As Object

  Set objShell = CreateObject("Shell.Application")
  Set objFolder = objShell.namespace(CLng(pFolder))

  If (Not objFolder Is Nothing) Then SpecialFolder = objFolder.Self.Path

  Set objFolder = Nothing
  Set objShell = Nothing

  If SpecialFolder = "" Then Err.Raise 513, "SpecialFolder", "The folder path could not be detected"

End Function
You can then use the routine like this:
Code:
strConfigFile = SpecialFolder(SpecialFolder_AppData) & "\YourApp\config.ini"
strSharedConfigFile = SpecialFolder(SpecialFolder_CommonAppData) & "\YourApp\config.ini"