views:

317

answers:

1

I have a regular .NET application. In this case I have a part that's being imported by MEF. It imports fine, but at a certain point, I want to save a List of objects to a file. In this case it's just saving a list of high scores:

class HighScores
{
    static HighScores()
    {
        CheckTrust();
    }

    [ImportingConstructor]
    public HighScores(
         [Import("/Services/LoggingService", typeof(ILoggingService))] 
         ILoggingService l
        )
    {
        logger = l;

    }

    private ILoggingService logger { get; set; }

    public IEnumerable<HighScore> Scores
    {
        get
        {
            return m_Scores;
        }
    }
    private List<HighScore> m_ScoresUnsorted = new List<HighScore>();
    private readonly ObservableCollection<HighScore> m_Scores = 
        new ObservableCollection<HighScore>();

    public void LogNewHighScore(string name, int score, int level)
    {
        m_ScoresUnsorted.Add(new HighScore(name, score, level));
        CreateObservable();

        if (IsFullTrust)
        {
            Stream stream = null;
            try
            {
                // this line causes exception
                stream = new FileStream("HighScores.dat", 
                         System.IO.FileMode.Create, FileAccess.Write);  
                BinaryFormatter b = new BinaryFormatter();
                b.Serialize(stream, m_ScoresUnsorted);
            }
            catch (Exception e)
            {
                logger.Error("Error writing high scores:", e);
            }
            finally
            {
                if (stream != null)
                {
                    stream.Close();
                }
            }
        }
    }

    private void CreateObservable()
    {
        m_ScoresUnsorted.Sort();
        m_Scores.Clear();
        for(int i = m_ScoresUnsorted.Count-1; i >= 0; i--)
        {
            m_Scores.Add(m_ScoresUnsorted[i]);
        }
    }

    static private void CheckTrust()
    {
        try
        {
            FileIOPermission permission =
                new FileIOPermission(PermissionState.Unrestricted);
            s_FullTrust = SecurityManager.IsGranted(permission);
        }
        catch (Exception)
        {
            // ignore
        }
    }

    static private bool s_FullTrust;
    static public bool IsFullTrust
    {
        get
        {
            return s_FullTrust;
        }
    }

}

I'm getting a System.Security.SecurityException on the new FileStream line. The weird thing is that if I just replace this with a TextWriter, it works. I don't understand what I'm doing wrong.

EDIT: More info... when I put this code in the constructor, it executes. If you follow the call stack back (when breaking in the sample above), it appears to be executing on the GUI thread. Specifically the WPF dispatcher is running a get operation on a property based on the fact that a PropertyChanged event fired. So maybe it has to do with a GUI refresher in WPF not being allowed to do file I/O? This kind of make sense... you wouldn't want to lock up the GUI for something like a file write...

A: 

I was able to get it to execute by taking the stream functionality and putting it on an "unsafe" ThreadPool thread, like this:

        ThreadPool.UnsafeQueueUserWorkItem(ignoredState =>
        {
            Stream stream = null;
            try
            {
                stream = new FileStream("HighScores.dat", System.IO.FileMode.Create, FileAccess.Write);
                BinaryFormatter b = new BinaryFormatter();
                b.Serialize(stream, "Test String");
            }
            catch (Exception e)
            {
                logger.Error("Error writing high scores:", e);
            }
            finally
            {
                if (stream != null)
                {
                    stream.Close();
                }
            }
        }, null);

Of course, that has a whole bunch of really bad synchronization issues that I have to clean up, but it works. I think what I'll do is lock inside the thread and have it make a copy of the list, then write the copy to disk. I'll have to get rid of the logger too since you can't safely access that across multiple threads.

Really wish I knew why I had to do this though.

Scott Whitlock