尝试读取或写入受保护的内存。这通常表明其他内存已损坏

时间:2012-04-02 19:36:52

标签: c# wpf access-violation unhandled-exception memory-corruption

我真的不明白这段代码是怎么回事。请自行检查代码

    void dispatcherTimer_Tick(object sender, EventArgs e)
{
    string srUrl = lstLocalIndex[irLocalIndex] + lstMainIndex[irMainIndex].Replace("0;","");

    Task.Factory.StartNew(() =>
    {
        startNewWindow(srUrl);
    });

}


    void startNewWindow(string srUrl)
{
    NewWindowThread<TitleWindow, string>(c => new TitleWindow(c), srUrl);
}

现在这段代码就是发生错误的地方。我还会附上截图

        private void NewWindowThread<T, P>(Func<P, T> constructor, P param) where T : Window
    {
        Thread thread = new Thread(() =>
        {
            T w = constructor(param);
            w.Show();
            w.Closed += (sender, e) => w.Dispatcher.InvokeShutdown();
            try
            {
                System.Windows.Threading.Dispatcher.Run();
            }
            catch
            {

            }
        });
        thread.SetApartmentState(ApartmentState.STA);
        try
        {
            thread.Start();
        }
        catch
        {

        }
    }

此错误导致整个软件抛出错误并停止工作,即使我在新线程中调用它们:(

此行抛出错误 System.Windows.Threading.Dispatcher.Run();

请同时查看截图

enter image description here

C#4.0 WPF

3 个答案:

答案 0 :(得分:2)

我一直在与客户争论这个问题,这就是我找到的。

我们正在开发一个WPF应用程序,它可以执行大量的线程和后台工作程序处理。这个异常突然开始出现,我开始做一些挖掘。经过大约一个小时的调查,我终于找到了罪魁祸首:

        var worker = new BackgroundWorker();
        worker.DoWork += (o, ea) => Dispatcher.BeginInvoke(new Action(() =>
        {
            //do some heavy processing here, plus UI work, then call another method.

            //inside that other method, I found this:
            var thread = new Thread(() =>
            {
                //do some heavy processing.
            }) { IsBackground = true };
            thread.Start();
        }));

似乎已经发生的事情是后台工作人员正在完成其工作并从执行中返回。但是,在后台工作程序中创建的线程没有完成处理并返回只是发现它创建的线程已经超出范围,从而导致AccessViolationException。

为了调试这个,我建议密切注意异常发生的位置并仔细检查你的调用堆栈,这可能会也可能不会被破坏或丢失,具体取决于你是否在异常时在一个线程中被抛出。

答案 1 :(得分:1)

我前段时间遇到过类似问题。

发生错误是因为您的窗口超出了范围而垃圾收集器会将其销毁。

使用ShowDialog()可以解决问题。请注意,这样做不会阻止其他线程,因为窗口仅在调用线程中是模态的。

private void NewWindowThread<T, P>(Func<P, T> constructor, P param) where T : Window
{
    Thread thread = new Thread(() =>
    {
        System.Windows.Threading.Dispatcher.Run();
        T w = constructor(param);
        w.ShowDialog();
        w.Dispatcher.InvokeShutdown();
    });
    thread.SetApartmentState(ApartmentState.STA);
    try
    {
        thread.Start();
    }
    catch
    {
        // log&handle exceptions
    }
}

答案 2 :(得分:0)

您正在使用lambda作为线程函数。这个lambda在一个新线程上调用。在 在线程实际创建的那一刻,它将查找您提供的参数,这是一个局部变量srUrl,但是当这个发生时,您的函数(dispatcherTimer_Tick)已经退出,因此srUrl将位于堆栈的一部分中不再正确定义(因此访问违规)。简单的解决方法是在类中定义一个变量,并快速填充srLoc。更合适的解决方案是将srLoc实际传递为参数:

() =>
{
    startNewWindow(srUrl);
}

成为

(Action<string>){x => {startNewWindow(x);},
            new object[] {srUrl}

现在为函数调用保存了函数引用和字符串的正确副本,并且在线程启动时原始srUrl超出范围并不重要。我不确定是否task factory允许传递参数数组。调度员通常有一个超载,所以也许你想让你的窗口照顾这个。

现在你实际上这样做了几次,所以你可能需要在每次传递时包装参数。