创建新的 FileStream 时出现 System.Security.SecurityException

发布于 2024-08-10 00:19:29 字数 2673 浏览 7 评论 0原文

我有一个常规的 .NET 应用程序。在本例中,我有一个由 MEF 导入的零件。它导入得很好,但在某个时刻,我想将对象列表保存到文件中。在本例中,它只是保存高分列表:

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;
        }
    }

}

我在新的 FileStream 行上收到 System.Security.SecurityException。奇怪的是,如果我用 TextWriter 替换它,它就可以工作。我不明白我做错了什么。

编辑:更多信息...当我将此代码放入构造函数中时,它会执行。如果您跟踪调用堆栈(当中断上面的示例时),它似乎正在 GUI 线程上执行。具体来说,WPF 调度程序基于 PropertyChanged 事件触发的事实对属性运行 get 操作。那么也许这与 WPF 中的 GUI 刷新不允许执行文件 I/O 有关?这是有道理的......你不会想锁定 GUI 来进行文件写入之类的事情......

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...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

说谎友 2024-08-17 00:19:29

我能够通过获取流功能并将其放在“不安全”的 ThreadPool 线程上来让它执行,如下所示:

        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);

当然,这有一大堆非常糟糕的同步问题,我必须清理它们,但它可以工作。我想我要做的就是锁定线程内部并让它制作列表的副本,然后将该副本写入磁盘。我也必须摆脱记录器,因为您无法跨多个线程安全地访问它。

我真的希望我知道为什么我必须这样做。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文