Случайное появление AccessViolationException в GDI +

У нас возникла проблема, что иногда происходит AccessViolationException, а простое групповое поле рисуется белым фоном и красным крестом поверх него. Мы не можем достоверно воспроизвести эту ошибку, она просто возникает время от времени.

Мы ничего не делаем специально, мы просто показываем главное окно с меню, панелью инструментов, групповым полем на главной панели и некоторыми элементами гиперссылки внутри группового окна.

Из трассировки стека это, кажется, ошибка в Windows Forms или GDI +:

System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
   at System.Drawing.SafeNativeMethods.Gdip.GdipDrawLineI(HandleRef graphics, HandleRef pen, Int32 x1, Int32 y1, Int32 x2, Int32 y2)
   at System.Drawing.Graphics.DrawLine(Pen pen, Int32 x1, Int32 y1, Int32 x2, Int32 y2)
   at System.Windows.Forms.GroupBox.DrawGroupBox(PaintEventArgs e)
   at System.Windows.Forms.GroupBox.OnPaint(PaintEventArgs e)
   at System.Windows.Forms.Control.PaintWithErrorHandling(PaintEventArgs e, Int16 layer, Boolean disposeEventArgs)
   at System.Windows.Forms.Control.WmPaint(Message& m)
   at System.Windows.Forms.Control.WndProc(Message& m)
   at System.Windows.Forms.GroupBox.WndProc(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
   at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam) 

Ответы

Ответ 1

Короче говоря. Я узнал, что в Dot.Net есть "ошибка", где объекты перемещаются GC из одного места в другое в рамках оптимизации памяти или процесса дефрагментации. Обычно это происходит при отправке ссылки массива управляемой памяти (или изображения) на неуправляемый фрагмент кода. Данные перемещаются в другое место, и, поскольку ненаправленный код не знает об этом, он пытается получить доступ к "старому" местоположению данных. Это происходит только в режиме деблокирования, так как в режиме отладки оптимизация памяти отключена, поэтому ВСЕГДА отлаживается в режиме деблокирования, meh...

К сожалению, нет способа отключить процесс дефрагментации GC. Вы можете попробовать и вызвать GC.Collect() и дождаться его завершения до вызова функции GDI +, но это только улучшит ситуацию и не решит ее полностью.

Единственный способ, которым я мог обойти это, - это вручную заблокировать (вывести) данные и освободить их после возврата из кода без управления, да, обратно на С++. Изображения сложны, так как вам нужно найти точную ссылку на данные во всех классах и подклассах.

Надеюсь, что это поможет.

Ответ 2

Вы называли GdiplusShutdown до того, как все было выпущено? Я задал аналогичный вопрос здесь, где я назвал GdiplusShutdown до того, как мой Bitmap был уничтожен и также получил нарушение прав доступа

Ответ 3

мы сталкиваемся с аналогичной проблемой. У нас есть программное обеспечение, которое работает на многих машинах без проблем. На одном Windows XP SP 3 мы имеем следующее поведение:

  • Приложение запускается и запускается правильно
  • Через некоторое время мы начинаем получать AccessViolationException из собственного кода GDI. Приложение прекращает рендеринг некоторых элементов управления или сбоев, если исключение не обрабатывается.

Есть несколько мест, где происходит нарушение, например:

1) Исключение: System.AccessViolationException
Сообщение: попытка чтения или записи защищенной памяти. Это часто свидетельствует о том, что другая память повреждена.
Источник: System.Drawing

at System.Drawing.SafeNativeMethods.Gdip.GdipDrawImageI(HandleRef graphics, HandleRef image, Int32 x, Int32 y)
at System.Drawing.Graphics.DrawImage(Image image, Int32 x, Int32 y)
at System.Drawing.Graphics.DrawImageUnscaled(Image image, Int32 x, Int32 y)
at Aga.Controls.Tree.TreeViewAdv.DrawTree(PaintEventArgs e)
at Aga.Controls.Tree.TreeViewAdv.OnPaint(PaintEventArgs e)
at System.Windows.Forms.Control.PaintWithErrorHandling(PaintEventArgs e, Int16 layer, Boolean disposeEventArgs)
at System.Windows.Forms.Control.WmPaint(Message& m)
at System.Windows.Forms.Control.WndProc(Message& m)  

Код, похоже, не делает ничего необычного. Он отображает изображение в буфер, а затем вызывает: e.Graphics.DrawImageUnscaled(_bufferImage, 0, 0); Буфер не обращается ни к какому другому потоку.

2)

at System.Drawing.SafeNativeMethods.Gdip.GdipDrawLineI(HandleRef graphics, HandleRef pen, Int32 x1, Int32 y1, Int32 x2, Int32 y2)
at System.Drawing.Graphics.DrawLine(Pen pen, Int32 x1, Int32 y1, Int32 x2, Int32 y2)
at System.Drawing.Graphics.DrawLine(Pen pen, Point pt1, Point pt2)
at SomeMyNamespace.SomeMyControl.OnPaint(PaintEventArgs e)
at System.Windows.Forms.Control.PaintWithErrorHandling(PaintEventArgs e, Int16 layer, Boolean disposeEventArgs)
at System.Windows.Forms.Control.WmPaint(Message& m)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.ScrollableControl.WndProc(Message& m)
at System.Windows.Forms.ContainerControl.WndProc(Message& m)
at System.Windows.Forms.UserControl.WndProc(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

Здесь OnPaint метод не делает ничего, кроме рисования только одной строки. Перо (и его кисть), переданное в DrawLine, конечно, хорошо инициализировано и не расположено.