AccessViolation on Graphics.DrawString()

Viewed 65

I have a windows Form which has some numbers on it (at this point only one number) and refreshes them regulary (numbers are randomly generated). Update process is going in a separate thread from the application one, so that the form can recieve events like user clicking buttons, window being resized etc. Main method:

class Program {       
        static void Main(string[] args) {
            Form o = new Overlay();
            Application.Run(o);
        }
    }

Form class:

public partial class Overlay : Form {
        Graphics g;
        Drawer drawer;
        public Overlay() {
            InitializeComponent();
            TopMost = true;
            TransparencyKey = Color.Black;
            BackColor = TransparencyKey;
            CheckForIllegalCrossThreadCalls = false;

            g = CreateGraphics();
            drawer = new Drawer();
        }

        protected override void OnLoad(EventArgs e) {
            base.OnLoad(e);
            Thread upd = new Thread(mainLoop);
            upd.Start();
        }

        void mainLoop() {
            while (true) {
                NetCoreEx.Geometry.Rectangle r;
                GetWindowRect(Handle, out r);
                Refresh();
                drawer.Update(g, new Rectangle(r.Left, r.Top, r.Width, r.Height));
            }
        }

Drawer:

class Drawer {
        Font font;

        public Drawer() {
            string workingDir = Environment.CurrentDirectory;
            string projDir = Directory.GetParent(workingDir).Parent.FullName;
            PrivateFontCollection collection = new PrivateFontCollection();
            collection.AddFontFile(projDir + "\\Resources\\Athelas-Regular.ttf");
            FontFamily fontFamily = new FontFamily("Athelas", collection);
            font = new Font(fontFamily, 16);
        }

        
        public void Update(Graphics g, Rectangle rect) {
            string stats = new Random().NextDouble().ToString();
            g.DrawString(stats, font, Brushes.Aqua, rect.Width / 2, (int)(rect.Height * 0.75));
        }
    }

The code looks quite simple to me, but for some reason after 5-10 seconds running normally application suddenly crashes with System.AccessViolateException on DrawString Method... Stacktrace

at System.Drawing.SafeNativeMethods.Gdip.GdipDrawString(HandleRef graphics, String textString, Int32 length, HandleRef font, GPRECTF& layoutRect, HandleRef stringFormat, HandleRef brush)
   at System.Drawing.Graphics.DrawString(String s, Font font, Brush brush, RectangleF layoutRectangle, StringFormat format)
   at System.Drawing.Graphics.DrawString(String s, Font font, Brush brush, Single x, Single y)
   at OverlayStatistics.Scripts.Drawer.Update(Graphics g, Rectangle rect) in C:\Users\Гриша\source\repos\OverlayStatistics\Scripts\Drawer.cs:line 30
   at OverlayStatistics.Overlay.mainLoop() in C:\Users\Гриша\source\repos\OverlayStatistics\Scripts\Overlay.cs:line 50
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()

Ive spent a lot of time debugging but still has no clue what I am doing wrong, any help?

1 Answers

What happens is the FontFamily instance get disposed and causes crashes in internal GDI calls. You can accelerate the behavior like this:

public void Update(Graphics g, Rectangle rect)
{
    string stats = new Random().NextDouble().ToString();
    g.DrawString(stats, font, Brushes.Aqua, rect.Width / 2, (int)(rect.Height * 0.75));
    GC.Collect(); // a good way to check for dispose issues
}

There are multiple way to fix it, for example just make sure the FontFamily instance is also a member of Drawer, ie:

class Drawer {
    Font font;
    FontFamily fontFamily;


    public Drawer() {
        ...
        fontFamily = new FontFamily("Athelas", collection);
        font = new Font(fontFamily, 16);
    }

    public void Update(Graphics g, Rectangle rect) {
        string stats = new Random().NextDouble().ToString();
        g.DrawString(stats, font, Brushes.Aqua, rect.Width / 2, (int)(rect.Height * 0.75));
    }
}
Related