Randomly occurring AccessViolationException in GDI

2019-03-15 06:52发布

We've got the problem that sometimes an AccessViolationException occurs and a simple group box gets drawn with white background and a red cross on top of it. We can't reproducable this bug reliably, it just occurs from time to time.

We don't do anything special, we're just showing a main window with a menu, toolbar, the group box on the main panel and some hyperlink controls inside the group box.

From the stack trace it seems to be a bug in Windows Forms or 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) 

3条回答
聊天终结者
2楼-- · 2019-03-15 06:56

To make long story short. I found out that there is a ""bug"" in Dot.Net where objects are moved by the GC from one place to the other as part of memory optimization or Defragmentation process. This usually happens when sending a reference of a managed memory array (or image) to an un-managed piece of code. The data is being moved to a different location and as the un-managed code is not aware of this it tries to access the "old" location of the data. This only happens in Release mode as in Debug mode memory optimization is switched off so ALWAYS debug in Release mode, meh...

Unfortunately, there is no way to turn off the GC defragmentation process. You can try and call the GC.Collect() and wait for it to finish before calling your GDI+ function but this will only improve the situation and not solve it completely.

The only way I was able to bypass this is to manually lock (pin) the data and release it after returned from the un-managed code, yes, back to C++. Images are tricky as you need to find the exact reference to the data in all the classes and sub-classes.

Hope this helps.

查看更多
萌系小妹纸
3楼-- · 2019-03-15 07:04

Did you call GdiplusShutdown before everything was released? I asked a similar question here where I called GdiplusShutdown before my Bitmap was destroyed and also got access violation

查看更多
smile是对你的礼貌
4楼-- · 2019-03-15 07:05

we are running into similar problem. We have software which runs on many machines without issues. On one Windows XP SP 3 we have the following behavior:

  1. The application starts and runs correctly
  2. After some time, we start getting AccessViolationException from GDI native code. The application stops rendering some controls or crashes if the exception is not handled.

There are a few places where the violation comes from, e.g.:

1) Exception: System.AccessViolationException
Message: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
Source: 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)  

The code does not seem to do anything unusual. It renders an image to a buffer then calls: e.Graphics.DrawImageUnscaled(_bufferImage, 0, 0); The buffer is not accessed by any other thread.

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)

Here OnPaint method does not do anything other than painting just one line. Pen (and its Brush) passed to DrawLine is certainly well initialized and not disposed.

查看更多
登录 后发表回答