Local File Access

.Local File Access

"Silverlight contains restrictions on local file access. If the user initiates the file access or grants the application elevated security mode, some of the restrictions are removed. Otherwise the application can only create files in a limited-size virtual file system controlled by Silverlight."

Silverlight is a web technology, so it contains various security restrictions to provide a safe environment for browsing. Local file access is a major concern for applications running in a browser. Silverlight applications have their local file access restricted to a limited-size virtual file system unless the user agrees to grant the application additional access.

Four methods of accessing local files in Silverlight include:

  1. Isolated Storage - is a virtual file system restricted by user and origin URI.
  2. File Dialogs - are restricted to user-initiated actions.
  3. Elevated Trust/System.IO - is restricted to the MyDocument folders (e.g. C:/users/profileid/MyDocuments by using Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)).
  4. Elevated Trust/COM Automation/System.IO - is only restricted by normal system security (Windows Only Feature).



Isolated Storage

  • Isolated Storage is an obfuscated storage area containing files and directories. Isolated stores are usually scoped by user and assembly, so most other managed code will not be able to access the application's data. However, highly trusted managed code, unmanaged code, and administration tools can access any isolated storage area. Further, isolated storage is not encrypted and can be transient as the user can delete isolated storage.

  • Isolated storage can however persist across sessions. This provides a storage area for such data as user settings and recent user actions. This provides a similar function to the browser cookie, however there are several differences. A browser cookie is typically limited to 4 KB, while isolated storage can store 1MB before it has to request additional space from the user. Out-of-Browser applications are provided additional storage space; they have 25MB of initial isolated storage space. Isolated storage is not part of the browser cache, as are cookies. You can delete the browser history and isolated storage will not be affected. Also, isolated storage is not browser dependent, as are cookies. You can switch between different browsers and each browser will access the same isolated storage area. Cookies require a different cookie for each different browser. Finally, isolated storage data does not contain an expiration date, as do cookies.

  • Isolated storage is NOT encrypted, it is located at:

    C:\Users\[UserName]\AppData\LocalLow\Microsoft\Silverlight\is
.Isolated Storage on Local Drive


Viewing a File in Isolated Storage

  • Isolated storage can be deleted or disabled by the user in the Silverlight settings. If the user chooses to delete the isolated storage data, a dialog box will display with the message "This may change or reset the behavior of this Web site and will remove associated user data". If isolated storage is disabled by the user, the application will get an Initialization Failure exception when it tries to write to isolated storage.
.User Can Delete or Disable Isolated Storage


Silverlight Isolated Stores

IsolatedStorageFile Class
  • The IsolatedStorageFile - class represents an obfuscated storage area containing files and directories. This class contains two methods for obtaining an isolated store (note: these two classes are only valid in Silverlight):
    1. GetUserStoreForApplication - is the method most commonly used in Silverlight to obtain an isolated store. The provided isolated store is specific to the user and the assembly.

    2. GetUserStoreForSite - this provides an isolate store specific to the user within a particular domain. Domain-wide isolated stores can be used to share the store among a group of Silverlight application residing on the same domain.
  • You can perform all the usual file and directory functions in isolated storage (create/delete files and directories, read/write files, get directory information). The file management methods for isolated storage are similar to the normal file management methods (CreateDirectory(), CreateFile(), OpenFile(), FileExists(), GetFileNames()).

  • You can also read and write to isolated storage files with the usual StreamReader, StreamWriter, BinaryReader, and BinaryWriter classes.

  • The following example creates a text file in isolated storage and writes the current date and time to it. This file can be viewed as a regular text file, as shown in the "lsk1.txt" screen capture in the section above.



Writing a File to Isolated Storage

try
{               
    using (IsolatedStorageFile myStore = IsolatedStorageFile.GetUserStoreForApplication())
    {
        using (IsolatedStorageFileStream myStream = myStore.CreateFile("lsk1.txt"))
        {
            StreamWriter myWriter = new StreamWriter(myStream);
            myWriter.Write("The datetime is: {0}", DateTime.Now);
            myWriter.Close();
            lblErrorText.Text = "DateTime written.";
        }                   
    }
}
catch (Exception e)
{
    lblErrorText.Text = e.Message;
}

IsolatedStorageSettings Class
  • The IsolatedStorageSettings class provides a dictionary that stores key-value pairs in isolated storage. Two read-only properties determine the scope of the storage:
    1. The ApplicationSettings property set the isolated storage scope at the per-application, per-computer, and per-user level.

    2. The SiteSettings property set the isolated storage scope at the per-domain, per-computer, and per-user level.

  • The IsolatedStorageSettings class contains the following methods:
    1. The Add() method adds an entry to the dictionary for the key-value pair..

    2. The Clear() method resets the count of items stored in IsolatedStorageSettings to zero and releases all references to elements in the collection.

    3. The Contains() method determines if the application settings dictionary contains the specified key.

  • The following example creates a user setting at the application level in isolated storage. The user setting holds the last run date and time.



Writing a User Setting to Isolated Storage at Application Scope Level




    public partial class MainPage : UserControl
    {
        private IsolatedStorageSettings mySettings = IsolatedStorageSettings.ApplicationSettings;

        public MainPage()
        {
            InitializeComponent();

            DateTime lastRunDateTime = new DateTime();

            try
            {
                lastRunDateTime = (DateTime)IsolatedStorageSettings.ApplicationSettings["LastRunDateTime"];
            }
            catch (System.Collections.Generic.KeyNotFoundException)
            {
                MessageBox.Show("No DateTime found for application.");
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message);
            }

            if (mySettings.Contains("LastRunDateTime"))
            {
                String myString = "Last run date time is: " + lastRunDateTime.ToString();
                MessageBox.Show(myString);
                mySettings.Clear();
                mySettings["LastRunDateTime"] = DateTime.Now;
            }
            else
            {
                mySettings.Add("LastRunDateTime", DateTime.Now);
            }           
        }
    }

Requesting Additional Isolated Storage Space
  • Applications can present a request to the users for more isolated storage space by using the IncreaseQuotaTo() method of the IsolatedStorageFile class. When using the IncreaseQuotaTo() method:
    1. You must specify the number of bytes you want for the total size of the isolated storage quota. If you request an allocation that is smaller than the current quota, an exception will be thrown. The IsolatedStorage File class has three read-only properties which specify information about the allocation:
      1. Quota - contains the total number of bytes in the allocation.
      2. AvailableFeeSpace - contains the number of bytes still available in the allocation.
      3. UsedSpace - contains the number of bytes already used in the allocation.
    2. The request for additional isolated storage must be made in response to a user interaction. Specifically, the call to the IncreaseQuotaTo() method must be inside an event handler (e.g. button_clicked()). Otherwise the request will not be presented to the user, so there will be no additional allocation of space.

    3. A large amount of space can be requested, and if the user accepts, allocated to isolated storage. This is shown in the screen print below where 10GB was request and allocated. It is up to the developer to allocate isolated storage space appropriately.

    .Requesting the User for Additional Isolated Storage Space


    Application Requesting Increased Isolated Storage Allocation

    • The following screen capture shows the isolated storage was increased to 10GB. An unreasonable, huge amount of space for isolated storage.
    .Size of Additional Allocation Increased


    Large Amounts of Isolated Storage can be Allocated

    • Decreasing the size of the isolated storage allocation can only be performed by deleting the entire allocation. Once the allocation is deleted, it is reset to the initial allocation limits (1MB, or 25MB for OOB). The Remove()
      method of the IsolatedStorageFile class deletes all the directories and files in the isolated storage.


    Example Code for Requesting Additional Isolated Storage Space



    private void myButton_Click(object sender, RoutedEventArgs e)
    {
        try
        {               
            using (IsolatedStorageFile myStore = IsolatedStorageFile.GetUserStoreForApplication())
            {
                if (myStore.AvailableFreeSpace < 1048575)
                {
                    if (myStore.IncreaseQuotaTo(10737418240))
                    {
                        lblErrorText.Text = "Allocation Successful";
                    }
                    else
                    {
                        lblErrorText.Text = "Allocation Failed";
                    }
                }
            }
        }
        catch (Exception err)
        {
            lblErrorText.Text = err.Message;
        }       
    }



    File Dialogs

    • Silverlight provides two file dialogs classes for working with local files. Both of the classes provide blocking-calls which can only be used in response to a user initiated action (e.g. button click event). If used outside of a user-interaction event, the calls will be ignored by the application.

    • Neither of the classes are supported in full-screen mode. If called in full-screen mode the default Silverlight behavior is to fall out of full-screen mode before displaying the dialog.

    • After a file stream has been selected with the file dialog box, for text data use the OpenText() method and StreamReader/StreamWriter. For binary data use the OpenRead() method and BinaryReader /BinaryWriter.

    • The two Silverlight file dialog classes are:
      1. OpenFileDialog - Provides a standard dialog box that enables the user to select one or more files from a local or a networked computer. A selected file returns a read-only file stream. The OpenFileDialog class uses the following properties to configure the file selection options:
        1. Filter - gets or sets a filter string which specifies the file types and descriptions to display. Defaults to an empty string.

        2. FilterIndex - gets or sets a 1-based index value which specifies which filter will be selected by default when the dialog box is displayed. Defaults to 1.

        3. Multiselect - gets or sets a Boolean value which specifies whether the dialog box allows users to select multiple files. Defaults to false.

        4. InitialDirectory - gets or sets a string which specifies the directory displayed when the OpenFileDialog starts. Introduced in Silverlight 5. Requires elevated trust, otherwise it will throw an exception. Defaults to last directory selected (controlled by OS).

        5. File - gets a FileInfo object for the selected file. If multiple files are selected, returns the first selected file.

        6. Files - gets a collection of FileInfo objects for the selected files.



      2. SaveFileDialog - Provides a standard dialog box that enables the user to save a single file to a local or a networked computer. The SaveFileDialog class returns a writeable file stream. The SaveFileDialog class uses the following properties to configure the file selection options:
        1. Filter - gets or sets a filter string which specifies the file types and descriptions to display. Defaults to an empty string.

        2. FilterIndex - gets or sets a 1-based index value which specifies which filter will be selected by default when the dialog box is displayed. Defaults to 1.

        3. DefaultExt - gets or sets a string which specifies the file extension to use when the user does not specify a file extension and the filter property is not set to a particular file extension. (e.g. Filter is set All Files).

        4. DefaultFileName - gets or sets a string which specifies the file name to use if a file name is not specified by the user.

        5. SafeFileName - gets a string value for the file name of the selected file. The returned file name has all file path information removed for security purposes.

    Open File Dialog Box
    .Open File Dialog


    Example Code for OpenFileDialog using Text Data




    private void Select_File_Click(object sender, RoutedEventArgs e)
    {
        OpenFileDialog myDialog = new OpenFileDialog();

        // InitialDirectory introduced in Silverlight 5, requires elevated trust
        myDialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

        myDialog.Filter = "All Files (*.*) | *.* | Text Files (*.txt) | *.txt";
        myDialog.FilterIndex = 2;
        myDialog.Multiselect = false;

        Nullable<bool> fileSelected = myDialog.ShowDialog();

        if (fileSelected == true)
        {
            StreamReader myFileStream = myDialog.File.OpenText();
            MessageBox.Show(myFileStream.ReadToEnd());
            myFileStream.Close();
        }
    }



    Elevated Trust/System.IO

    .InBrowserSetting.xml Specifies Elevated Permissions


    InBrowserSetting.xml Specifies Elevated Permissions

    • Silverlight 4 introduced the elevated trust mode for OOB which allowed the application to read/write files in the My Documents folder (e.g. C:/users/profileid/MyDocuments) without requesting permissions from the user.

    • Silverlight 5 introduced the ability to add elevated trust to in-browser applications. The application no longer had to be an OOB in order to capable of having elevated trust. Outside of testing mode (localhost), the in-browser application requires a signed .xap file and a special entry added to the registry -- before the elevated trust will be permitted.

    • The elevated trust mode is enabled in the Silverlight projects settings by checking the Require elevated trust when running in-browser option. Once the elevated trust option is checked, the InBrowserSettings.xml file is automatically created and it contains a security settings denoting elevated permissions are required. Note: Silverlight for Windows Phone and Silverlight 3 do NOT support trusted applications.

    • The My Document folder should be specified using the Environment.SpecialFolder Enumeration.

    • Application should confirm they have elevated trust with the property Application.HasElevatedPermissions before running the code which requires elevated trust.

    • Below is a screen capture with a snippet of code creating a file in the My Documents directory. The code then writes a line of text to the file.
    Create and Write to File in My Documents Directory (Elevated Trust Required)
    .File Created in My Documents Directory


    File Created in My Documents Directory

    if (Application.Current.HasElevatedPermissions)
    {
        string myPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
        //string myFileName = System.IO.Path.Combine(myPath,"ktest.txt");
        string myFileName = "C:\\Users\\Kevin\\My Documents";
        using (StreamWriter myWriter = File.CreateText(myFileName))
        {
            myWriter.WriteLine("Kevin's test file");
            myWriter.Close();
        }
    }




    Elevated Trust/COM Automation/System.IO

    • Silverlight 4 introduced the ability of applications running with elevated trust to use COM automation. This allows the Silverlight applications to interact with Microsoft Office applications. However it also allows the Silverlight application to access the Windows APIs which implement the IDispatch interface. When using these APIs the Silverlight security restricts can be bypassed, making the operating-system security level the only restrictions on the Silverlight applications.

    • In addition to the check for testing if the application is running in elevated-trust mode, you should also check that COM automation is available. Additionally you could check the type of operating system running the application, as COM is a Windows-only technology (e.g. if (PlatformID.Win32NT))

    • Below is a screen capture and a code snippet for using COM to display the local file structure in a tree view control.
    .Tree View of Local Files using COM


    Using COM to Create a Tree View of all Local Files

    if ((! Application.Current.HasElevatedPermissions) ||
        (! AutomationFactory.IsAvailable))
    {
        MessageBox.Show("Automation is not available.");
    }
    else
    {
        dynamic myFileSystem = AutomationFactory.CreateObject("Scripting.FileSystemObject");
        dynamic myFolder = myFileSystem.GetFolder("c:\\");

        FileTree.Items.Add(new TreeViewItem() { Header = myFolder.Name, IsExpanded = true });

        foreach (dynamic mySubFolder in myFolder.SubFolders)
        {
            TreeViewItem tvItem = new TreeViewItem() { Header = mySubFolder.Name };
            ((TreeViewItem)FileTree.Items[0]).Items.Add(tvItem);

            foreach (dynamic file in mySubFolder.Files)
            {
                tvItem.Items.Add(new TreeViewItem() { Header = file.Name });
            }
        }
    }



    Reference Articles


    Top