2

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

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

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

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

我还发现,即使我捕获了异常并将其丢弃,程序也会在下次调用 Append 时挂起。所以,我认为这是 RichTextBox 中实际代码的问题。有人有想法么?

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

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

更新 1

所以它会处理一些测试,就像它与 XNA 有关,所以这可能是我对线程如何工作的误解。我无法在纯 WinForms 应用程序中重现该错误,但通过一个简单的 XNA 游戏轻松完成。我把这两个都压缩了,让你看看。为错误道歉。 https://dl.dropbox.com/u/16985121/StackOverFlowExamples.zip

4

3 回答 3

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的滚动条在收到新消息时向下移动,这意味着它打印消息并更新,即使它没有焦点。

我试图锁定富文本框以防止多线程,但它没有解决访问冲突问题。如果除了使用 focus() 函数之外还有其他解决方案来解决此问题,那就太好了。

谢谢。

于 2013-04-08T19:39:24.943 回答
1

在这里发现了另一个问题,它给了我一种完全规避这个问题的方法,可以毫无问题地产生相同的输出: https ://stackoverflow.com/a/8562457/568042

于 2013-04-15T16:55:24.627 回答
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();
        }
    }
于 2012-12-25T06:53:31.273 回答