AccessViolationException с двойной буферизованной графикой

Как уже может быть сказано в заголовке, я столкнулся с AccessViolationException, когда я пытаюсь нарисовать элемент управления в.NET, используя BufferedGraphics объект. Это происходит через некоторое время, раньше или позже. Оценивая адрес объекта, я заметил, что он продолжал увеличиваться по мере выполнения программы. Я уже предпринял несколько мер для сохранения памяти, которые, казалось, немного помогли, но в конечном итоге не решили проблему. Кажется, что проблема возникает независимо от того, избавляюсь ли я от графического объекта до конца функции.

    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: 

У кого-нибудь есть идеи как это решить? Заранее спасибо!

Редактировать:

Переменная Context создается глобально следующим образом:

    BufferedGraphicsContext _bufferedGraphicsContext = new BufferedGraphicsContext()

Это было рекомендовано в документации для случая, когда вы будете часто использовать BufferedGraphics, Функции являются частью пользовательского элемента управления, который позволяет пользователю вводить входы и выходы двоичных функций. Поскольку я не знал ни о каком подобном контроле, я просто написал его сам. Вышеуказанная функция вызывается каждый раз, когда необходимо перекрасить элемент управления, например, если пользователь перемещает мышь, нажимает на что-либо и т. Д., Для всех этих действий требуется визуальная обратная связь. Как правило, он работает как требуется, за исключением исключения, которое возникает после длительного использования элемента управления...

Изменить 2:

Поскольку исключение происходит почти исключительно на DrawString функции, возможно ли, что NullReference (согласно HResult, если я не ошибаюсь) не из-за самого процесса рисования, а скорее из-за одного из аргументов или что-то?

0 ответов

Это может быть не совсем ваш случай (и я считаю, что он больше не актуален для вас через два с половиной года), но все же.

В вашем коде есть следующая строка:

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);
    }
}
Другие вопросы по тегам