2

as the title already may tell, I am encountering an AccessViolationException as I'm trying to paint a Control in .NET using a BufferedGraphics object. This happens after a while, ealier or later. Evaluating the object's address, I noticed that it kept increasing as the program went on. I've already taken a few measures to preserve memory, which seemed to help a bit, but did not ultimately solve the problem. It seems that the problem occurs independently of whether I dispose of the graphics object before the end of the function.

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

The exception occurs in this function when calling 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;
        }
    }

And this is the exception I am getting:

 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: 

Does anyone have an idea how to solve this? Thanks in advance!

Edit:

The Context variable is created globally like this:

    BufferedGraphicsContext _bufferedGraphicsContext = new BufferedGraphicsContext()

This was recommended in the documentation for the case that you would frequently use BufferedGraphics. The functions are part of a custom Control that allows for the user to enter binary function inputs and outputs. Since I didn't know of any similar control, I just wrote one myself. The above function is called everytime the Control needs to be repainted, e.g. if the user moves the mouse, clicks on something and so on, for all of these actions require a visual feedback. Generally, it works as desired, except for the Exception which occurs after using the Control for quite a while...

Edit 2:

Since the exception occurs almost exclusively on DrawString functions, would it be possible, that the NullReference (according to the HResult, if I am not mistaken) is not due to the drawing process itself, but rather due to one of the arguments or something?

XChalo
  • 21
  • 3
  • 2
    Please note: The `Graphics` object does not __contain__ any graphics; it is a **tool** that lets you draw onto a related bitmap, including a control's surface. Do not cache of 'buffer' it. For real DoubleBuffering you need to 'buffer' a Bitmap. Or use a DoubleBuffered control like PictureBox. - Not sure what `_bufferedGraphicsContext.Allocate` does but if it evetually leads to disposing of the e.Graphics you got your exception.. – TaW Jun 09 '16 at 13:50
  • 3
    Looks to me like you are thinking C++ and trying to write C#. You don't have to manage the graphics object. You don't even have to handle double-buffering yourself (although you might justify it in special cases). And that bit about forcing GC is a bad idea. If you think you need to do that then you've got a serious design flaw. – DonBoitnott Jun 09 '16 at 14:29
  • Thank you for your attention so far. – XChalo Jun 09 '16 at 21:00
  • 1
    @TAW The `Allocate` function, if I am not mistaken, allows me to create a `BufferedGraphics` object onto which I can apply the drawing functions and render it later onto the actual graphics when I'm done. I need this to prevent flickering, which does work quite nicely until the exception occurs. I suppose that it uses a bitmap internally. If I am recalling the documentation correctly, I am using the `BufferedGraphics` class the way it's meant to be, but still I can't explain myself why there is an AccessViolation... – XChalo Jun 09 '16 at 21:06
  • @DonBoitnott I've removed the GC bit, since it did not change much... I suppose you're right that I'm just imagining it the wrong way, but I wasn't able to find any other explantion. The manual double buffering seems necessary to me though because the automatic one does not prevent massive flickering, while this way it looks quite nice and smooth for quite a while before it crashes. This happens sooner with a bigger control and/or more iterations – XChalo Jun 09 '16 at 21:10
  • _The manual double buffering seems necessary to me though because the automatic one does not prevent massive flickering, while this way it looks quite nice and smooth_ Hm. first: Thanks for pointing me to this class again after quite a while.. Can you elaborate a little on what you are doing? And how you were using the regular double-buffering? - Also: Can you show how you create the `bufferedGraphicsContext` variable? – TaW Jun 09 '16 at 22:08
  • Of course, I'll update my question! – XChalo Jun 09 '16 at 22:23

1 Answers1

2

That could be not exactly your case (and I believe it's not actual for you anymore after two and a half years), but still.

There is the following line in your code:

Font seriffont = Design.CreateSerifFont(fontSize);

Looks like it's your own code, since I couldn't find any mentions of it in Google. So here is the implementation of that method that could lead to the error you observed:

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

I'd say, this is a bad design of PrivateFontCollection - it can be disposed while Font's from it are still in use and that would cause AccessViolationException. The code above can be fixed by moving fonts from a local scope to the class field, something like that:

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);
    }
}
Grief
  • 1,839
  • 1
  • 21
  • 40