【问题标题】:AccessViolationException with double buffered graphics带有双缓冲图形的 AccessViolationException
【发布时间】:2016-10-10 04:37:15
【问题描述】:

正如标题已经说明的那样,我在尝试使用 BufferedGraphics 对象在 .NET 中绘制控件时遇到了 AccessViolationException。这发生在一段时间后,或早或晚。评估对象的地址,我注意到它随着程序的进行而不断增加。 我已经采取了一些措施来保存记忆,这似乎有点帮助,但并没有最终解决问题。似乎问题的发生与我是否在函数结束之前处理图形对象无关。

    private void checkMemory()
    {
        long mem = GC.GetTotalMemory(false);
        //Console.WriteLine("Allocated memory: " + mem.ToString());
        if (mem > criticalMemorySize) 
        {
            Console.WriteLine("GC started");
            GC.Collect(1, GCCollectionMode.Forced, true);
        }
    }

    BufferedGraphics graphics;

    protected override void OnPaint(PaintEventArgs e)
    {
        lock (e.Graphics)
        {
            base.OnPaint(e);
            checkMemory();
            if (e.ClipRectangle.Width * e.ClipRectangle.Height == 0)
                return;
            if (graphics == null || graphics.Graphics == null || graphics.Graphics.ClipBounds != e.ClipRectangle)
              graphics = _bufferedGraphicsContext.Allocate(e.Graphics, e.ClipRectangle);

            PaintBackground(graphics.Graphics);
            PaintHeader(graphics.Graphics);
            PaintBody(graphics.Graphics);
            DrawGrid(graphics.Graphics);
            //...

            graphics.Render(e.Graphics);
            graphics.Graphics.Dispose();
            graphics.Dispose();
        }
    }

调用g.DrawString时该函数出现异常:

    private void PaintBody(Graphics g)
    {
        Font seriffont = Design.CreateSerifFont(fontSize);
        Point mousePos = PointToClient(MousePosition);
        int hfeed = headHeight + headLineWidth - 2; //Dunno 
        for (int y = 0; y < LineCount; y++)
        {
            int vfeed = 0;
            for (int x = 0; x < ColumnCount; x++)
            {
                String s = "";
                Rectangle area = new Rectangle(vfeed, hfeed, colwidth, cellHeight);
                switch (_tableData[y][x])
                {
                    case ExBool.DontCare:
                        s = "*";
                        break;
                    case ExBool.False:
                        s = "0";
                        break;
                    case ExBool.True:
                        s = "1";
                        break;
                    default:
                        s = " ";
                        break;
                }
                Color backColor = cellBackColor;
                if (_activeHeaderColumn == x)
                {
                    backColor = cellActiveColColor;
                }
                if (area.Contains(mousePos))
                {
                    backColor = cellHoverColor;
                }
                if (area.Contains(mousePos) &&
                    ((MouseButtons & MouseButtons.Left) == MouseButtons.Left))
                {
                    backColor = cellActiveColor;
                }
                g.FillRectangle(new SolidBrush(backColor), area);
                g.DrawString(s, seriffont, Brushes.Black, area,
                    new StringFormat() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center });
                if (x != InputValues.Count() - 1)
                {
                    vfeed += lineWidth + colwidth;
                }
                else
                {
                    vfeed += typeSepLineWidth + colwidth;
                }
            }
            hfeed += cellHeight + lineWidth;
        }
    }

这是我得到的例外:

 System.AccessViolationException was unhandled
  HResult = -2147467261
  Message=Es wurde versucht, im geschützten Speicher zu lesen oder zu schreiben.Dies ist häufig ein Hinweis darauf, dass anderer Speicher beschädigt ist.
  Source= System.Drawing
  StackTrace:
       bei System.Drawing.SafeNativeMethods.Gdip.GdipDrawString(HandleRef graphics, String textString, Int32 length, HandleRef font, GPRECTF& layoutRect, HandleRef stringFormat, HandleRef brush)
       bei System.Drawing.Graphics.DrawString(String s, Font font, Brush brush, RectangleF layoutRectangle, StringFormat format)
       bei KarnaughVeitch.Controls.TruthTableControl.PaintBody(Graphics g)
       bei KarnaughVeitch.Controls.TruthTableControl.OnPaint(PaintEventArgs e)
       bei KarnaughVeitch.Controls.TruthTableControl.TruthTableControl_MouseMove(Object sender, MouseEventArgs e)
       bei System.Windows.Forms.Control.OnMouseMove(MouseEventArgs e)
       bei System.Windows.Forms.Control.WmMouseMove(Message& m)
       bei System.Windows.Forms.Control.WndProc(Message& m)
       bei System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
       bei System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
       bei System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
       bei System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
       bei System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData)
       bei System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
       bei System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
       bei System.Windows.Forms.Application.Run(Form mainForm)
       bei KarnaughVeitch.Program.Main()
       bei System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
       bei System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       bei Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       bei System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       bei System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       bei System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       bei System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       bei System.Threading.ThreadHelper.ThreadStart()
  InnerException: 

有人知道如何解决这个问题吗? 提前致谢!

编辑:

上下文变量是这样全局创建的:

    BufferedGraphicsContext _bufferedGraphicsContext = new BufferedGraphicsContext()

在文档中建议您经常使用BufferedGraphics 的情况。 这些函数是自定义控件的一部分,允许用户输入二进制函数输入和输出。由于我不知道任何类似的控件,我只是自己写了一个。 每次需要重新绘制控件时都会调用上述函数,例如如果用户移动鼠标、点击某物等等,所有这些动作都需要视觉反馈。 一般来说,它可以按需要工作,除了在使用控件很长一段时间后发生的异常......

编辑 2:

由于异常几乎只发生在DrawString 函数上,是否有可能 NullReference(根据 HResult,如果我没记错的话)不是由于绘图过程本身,而是由于其中之一论据还是什么?

【问题讨论】:

  • 请注意:Graphics 对象不包含任何图形;它是一个工具,可让您在相关位图(包括控件表面)上绘图。不要缓存“缓冲”它。对于真正的 DoubleBuffering,您需要“缓冲”位图。或者使用像 PictureBox 这样的 DoubleBuffered 控件。 - 不确定_bufferedGraphicsContext.Allocate 做了什么,但如果它最终导致处理 e.Graphics 你得到你的例外..
  • 在我看来,您在思考 C++ 并尝试编写 C#。您不必管理图形对象。您甚至不必自己处理双缓冲(尽管在特殊情况下您可能会证明它是合理的)。关于强制 GC 的那一点是个坏主意。如果你认为你需要这样做,那么你就有一个严重的设计缺陷。
  • 感谢您到目前为止的关注。
  • @TAW Allocate 函数,如果我没记错的话,允许我创建一个 BufferedGraphics 对象,我可以在该对象上应用绘图函数,然后在我'我完成了。我需要这个来防止闪烁,在异常发生之前它确实工作得很好。我想它在内部使用位图。如果我正确地回忆了文档,我正在使用 BufferedGraphics 类,但我仍然无法解释为什么会有 AccessViolation...
  • @DonBoitnott 我已经删除了 GC 位,因为它没有太大变化......我想你是对的,我只是想错了,但我不能找到任何其他解释。手动双缓冲对我来说似乎是必要的,因为自动双缓冲不能防止大量闪烁,而这种方式在崩溃之前看起来相当漂亮和平滑一段时间。更大的控制和/或更多的迭代会更快地发生这种情况

标签: c# .net winforms access-violation double-buffering


【解决方案1】:

这可能不完全是你的情况(我相信两年半后你的情况不再是实际的),但仍然如此。

您的代码中有以下行:

Font seriffont = Design.CreateSerifFont(fontSize);

看起来这是您自己的代码,因为我在 Google 中找不到任何提及它的内容。所以这里是该方法的实现,可能会导致您观察到的错误:

static class Design {
    static Font CreateSerifFont() {
        var fonts = new PrivateFontCollection();
        fonts.AddFontFile(@"c:\some_font.ttf");
        return new Font(fonts.Families[0], 12);        
    }
}

我想说,这是 PrivateFontCollection 的一个糟糕设计 - 它可以在来自它的 Font 仍在使用时被丢弃,这会导致 AccessViolationException。可以通过将 fonts 从本地范围移动到类字段来修复上面的代码,如下所示:

static class Design {
    private static readonly PrivateFontCollection Fonts = new PrivateFontCollection();
    public  static readonly Font                  SerifFont;

    static Design() {
        Fonts.AddFontFile(@"c:\some_font.ttf");
        SerifFont = new Font(Fonts.Families[0], 12);
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-04
    • 2010-10-04
    • 2020-04-01
    • 2011-03-08
    • 2011-06-09
    • 1970-01-01
    相关资源
    最近更新 更多