views:

767

answers:

6

My winform app uses xml files to store data, where should I store them so Vista users can write to them?

Thanks

A: 

why not in the applications directory (the same one the app is installed in)?

Mike_G
I do currently but the Program Files directory can't be written to by standard users i.e. non admins.
Even admins under Vista get interupted by the Vista permissions nonsense.
You *COULD* get around that by setting the 'requestedExecutionLevel level="requireAdministrator"', or level="highestAvailable" in the Manifest, but that's still not recommended, nor the best approach.
LarryF
A: 

Do you want your application data to be user-specific? Then you should consider putting it in C:\Users\%username%\%appname%\... Otherwise, @Mike_G's not wrong to suggest simply putting it in the same directory as your app.

EDIT: Except, as your Comment there notes, C:\Program Files\... isn't writable, in which case I would probably consider making it user-specific (with good defaults) in all cases, unless I had a good reason to want it consolidated in a single place. If that were the case, I would make it part of the app's configuration and let the user determine where they want (and can) save the data.

JMD
OK, I like the answer from @stevemegson better. ;) (Credit where it's due,and all that.)
JMD
A: 

If the data is for all users, then you could try using the "Shared Documents" area. On XP, this is located at C:\Documents and Settings\All Users\Documents. I'm not sure about Vista, but it's likely in C:\Users instead.

Andy
+4  A: 

Environment.GetFolderPath should tell you where Windows would like you to store things.

For user-specific data,

Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData));

typically returns C:\Users\%user%\AppData\Roaming

For common data,

Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData));

typically returns C:\ProgramData.

This will also do the right thing under older flavours of Windows; XP will typically return C:\Documents and Settings\%user%\Application Data and C:\Documents and Settings\All Users\Application Data.

stevemegson
Two caveats for this answer: - C:\ProgramData is read-only by default for regular users; - both CommonApplicationData and ApplicationData are not intended for files that represent documents.
Franci Penov
Indeed, ApplicationData is not even visible for the user. Store document-like or project-like under a subfolder of Environment.SpecialFolder.MyDocuments
Wim Coenen
@wcoenen ApplicationData is visible to the user. It's just not discoverable. :-)
Franci Penov
+1  A: 

Try using IsolatedStorage from the .Net Framework. It will do this work for you.

The Framework can manage those locations for you, instead of managing drives and folders and files and such. It's purpose is to have an area where you don't have to worry about user permissions.

The below code sequence is straight out of MSDN, but shows exactly how you would use these files.


using System;
using System.IO;
using System.IO.IsolatedStorage;

public class ReadingAndWritingToFiles{

   public static int Main(){

      // Get an isolated store for this assembly and put it into an
      // IsolatedStoreFile object.

      IsolatedStorageFile isoStore =  IsolatedStorageFile.GetStore(IsolatedStorageScope.User | IsolatedStorageScope.Assembly, null, null);

      // This code checks to see if the file already exists.

      string[] fileNames = isoStore.GetFileNames("TestStore.txt");
      foreach (string file in fileNames){
         if(file == "TestStore.txt"){

            Console.WriteLine("The file already exists!");
            Console.WriteLine("Type \"StoreAdm /REMOVE\" at the command line to delete all Isolated Storage for this user.");

            // Exit the program.

            return 0;
         }
      }

      writeToFile(isoStore);

      Console.WriteLine("The file \"TestStore.txt\" contains:");
      // Call the readFromFile and write the returned string to the
      //console.

      Console.WriteLine(readFromFile(isoStore));

      // Exit the program.

      return 0;

   }// End of main.


   // This method writes "Hello Isolated Storage" to the file.

   private static void writeToFile(IsolatedStorageFile isoStore){

      // Declare a new StreamWriter.

      StreamWriter writer = null;

      // Assign the writer to the store and the file TestStore.

      writer = new StreamWriter(new IsolatedStorageFileStream("TestStore.txt", FileMode.CreateNew,isoStore));

      // Have the writer write "Hello Isolated Storage" to the store.

      writer.WriteLine("Hello Isolated Storage");

      writer.Close();

      Console.WriteLine("You have written to the file.");

   }// End of writeToFile.


   // This method reads the first line in the "TestStore.txt" file.

   public static String readFromFile(IsolatedStorageFile isoStore){

      // This code opens the TestStore.txt file and reads the string.

      StreamReader reader = new StreamReader(new IsolatedStorageFileStream("TestStore.txt", FileMode.Open,isoStore));

      // Read a line from the file and add it to sb.

      String sb = reader.ReadLine();

      // Close the reader.

      reader.Close();

      // Return the string.

      return sb.ToString();

   }// End of readFromFile.
}
jro
Intriguing, especially reading about the different types of isolation: http://msdn.microsoft.com/en-us/library/eh5d60e1.aspx But it seems to force isolation per assembly strong name, which sounds problematic for sharing data with future versions.
Wim Coenen
+8  A: 

Use the Environment.GetFolderPath to get the most appropriate folder in an OS-independent manner.

In particular, you need one of the following SpecialFolder values:

  • ApplicationData - if the files are roaming, per-user and are for the application use only and don't represent documents the user might care about.
  • LocalApplicationData - if the files are non-roaming, per-user and are for the application use only and don't represent documents the user might care about.
  • CommonApplicationData - if the files are roaming, common for all user and are for the application use only and don't represent documents the user might care about. NOTE: On Vista this maps to C:\ProgramData, which is by default read-only for regular users (due to the fact that changing files in there might affect behavior of programs used by admins). You can either explicitly change the permissions on your app subfolder, or choose one of the other options.
  • MyDocuments - if the files are per-user and represent documents.

Note that there's no SpecialFolder enum value like CommonDocuments that would represent machine-wide document store, even though there is a folder intended to serve like one (C:\Documents and Settings\All Users\Documents on XP and C:\Users\Public\Documents on Vista). You will have to find the OS version yourself and choose the appropriate folder if you want to write to these locations.

Internally Environment.GetFolderPath uses the Win32 API SHGetFolderPath. The enumeration used by SHGetFolderPath gives you the well-known locations for several other special folders (including Common Documents). You might use directly SHGetFolderPath; you can find it's p/invoke definition and the corresponding CSIDL enum definition on PInvoke.net.

You can also use the IsolatedStorage. However, it is non-roamable, per-user, with limited quota and is not easily accessible to the user from Windows Explorer. As such, it is really a medium/low-trust equivalent to SpecialFolder.ApplicationData.

Franci Penov