在XNA游戏启动的表单中使用RichTextBox.ScrollToCaret的AccessViolationException

时间:2012-12-25 03:44:04

标签: c# .net winforms richtextbox access-violation

我一直在使用RichTextBox.ScrollToCaret遇到一个令人沮丧的障碍。我有代码将消息打印到RichTextBox。当每个消息发送到表单时,它被分成多行并格式化,然后连接每一行,结果发送到RichTextBox.Append。然后,进行以下两次调用以滚动到框的底部:

outputBox.Select(outputBox.Text.Length, 0);
outputBox.ScrollToCaret();

打印一条消息时,没问题。打印少量消息时,没有问题。当快速连续打印一堆消息时,它将随机(它在发生之前打印多少消息)抛出一个AccessViolationException(“试图读取或写入受保护的内存。这通常表明其他内存已损坏。”,完整详细信息here下次在该框中调用附加以添加下一条消息。这种在快速连续执行时发生,而在每次使用RichTextBox.ScrollToCaret时发生。我重新使用的以下代码可以正常工作:

outputBox.Focus();
outputBox.Select(outputBox.Text.Length, 0);

我也发现,即使我抓住了异常并扔掉它,程序也会在下一次调用Append时挂起。所以,我认为这是RichTextBox中实际代码的问题。有人有什么想法吗?

如果有人需要,我可以发布更多我的代码,但情况确实非常基本。需要注意的一点是,没有多线程(除了固有的UI线程),因此发送消息的对象和接收它们的表单都在同一个线程上。此外,这是在.NET 4.0下。

我发现了另一个解决此问题的问题,但只提供了一种解决方法,没有真正的解释:AccessViolation occurs in RichTextBox.ScrollToCaret。不幸的是,我的线程经验不是我想要的,所以我无法让他们的解决方案正常工作,但幸运的是我上面发布的内容工作得很好。

更新1

所以它看起来像是与XNA有关的一些测试,所以我可能误解了它如何与线程一起工作。我无法在纯WinForms应用程序中重现错误,但很容易通过简单的XNA游戏完成它。我在这里都有拉链供你查看。为错误道歉。 https://dl.dropbox.com/u/16985121/StackOverFlowExamples.zip

3 个答案:

答案 0 :(得分:1)

我有同样的问题。我有一些不同的情况,但基本相同的问题。我把代码与C ++ / CLI和C#表单混淆了。

来自C ++ / CLI的一个线程调用C#表单中的函数将消息打印到richtextbox。

“慢慢”调用此功能即可。但是如果调用函数发生得很快并经常发生,程序会随机崩溃。

这是我的代码。

void PrintOutLog(System::String^ s)
    {
        Monitor::Enter(this->richTextBox_LogBox);
        try
        {
            if(this->richTextBox_LogBox->InvokeRequired)
            {

                AddListItem^ d = gcnew AddListItem(this, &PrintOutLog);
                array<Object^>^ myStringArray = {s};
                this->richTextBox_LogBox->BeginInvoke(d, myStringArray);
            }
            else
            {                
                this->richTextBox_LogBox->AppendText(s + "\n");
                this->richTextBox_LogBox->SelectionStart = this->richTextBox_LogBox->Text->Length;
                this->richTextBox_LogBox->ScrollToCaret();
            }

        }
        finally
        {
            Monitor::Exit(this->richTextBox_LogBox);
        }

    }

事实证明,如果我注释掉两行代码,程序不会因内存访问冲突而崩溃。

this->richTextBox_LogBox->SelectionStart = this->richTextBox_LogBox->Text->Length;
this->richTextBox_LogBox->ScrollToCaret();

如果我将这两行注释掉,那么当C#表单没有焦点时,richtextbox不会在文本框的末尾显示新的日志消息。

我可以使用你的解决方案,在放置文本之前获得焦点,但如果我这样做,它总是保持在我需要保持最高的其他窗口之上。所以我不能这样做。

我查找了MSDN页面http://msdn.microsoft.com/en-us/library/system.windows.forms.textboxbase.scrolltocaret.aspx,并在页面中间找到了这个。

如果控件没有焦点或者插入符号已经位于控件的可见区域,则此方法无效。

但我相信这不是真的。看起来当焦点不在richtextbox控件上时调用ScrollToCaret()时,我可以看到richtextbox的滚动条在收到新消息时向下移动,这意味着它打印消息并更新,即使它没有焦点。

我试图锁定richtextbox以防止多线程,但它没有解决访问冲突问题。如果除了使用focus()函数之外还有其他解决方案来解决这个问题,那将会很棒。

感谢。

答案 1 :(得分:1)

在这里找到了另一个问题,它给了我一个完全规避问题的方法来产生相同的输出而没有任何问题: https://stackoverflow.com/a/8562457/568042

答案 2 :(得分:0)

public delegate void WriteLogEntryDelegate(string log_entry);

    void WriteLogEntryCB(string log_entry)
    {
        if (richTextBox1.InvokeRequired == true)
        {
            var d = new WriteLogEntryDelegate(WriteLogEntryCB);
            this.Invoke(d, log_entry);
        }
        else
        {
            richTextBox1.AppendText(log_entry + "\r\n");
            this.richTextBox1.SelectionStart = this.richTextBox1.Text.Length;
            this.richTextBox1.ScrollToCaret();
        }
    }