c# - Graphics.DrawString() 上的 AccessViolation

标签 c# winapi graphics thread-safety

我有一个 Windows 窗体,上面有一些数字(此时只有一个数字)并定期刷新它们(数字是随机生成的)。更新过程与应用程序在一个单独的线程中进行,因此表单可以接收用户单击按钮、调整窗口大小等事件。 主要方法:

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

表单类:

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

抽屉:

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

代码对我来说看起来很简单,但由于某种原因,在正常运行 5-10 秒后,应用程序突然因 DrawString 方法上的 System.AccessViolateException 而崩溃... 堆栈跟踪

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

我花了很多时间调试,但仍然不知道我做错了什么,有什么帮助吗?

最佳答案

发生的事情是 FontFamily 实例被处置并导致内部 GDI 调用崩溃。您可以像这样加速行为:

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
}

有多种方法可以修复它,例如只需确保 FontFamily 实例也是 Drawer 的成员,即:

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

关于c# - Graphics.DrawString() 上的 AccessViolation,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65552667/

相关文章:

c# - Visual Studio 如何在不中断其 IEnumerator<T> 的 MoveNext 的情况下评估 IEnumerable?

c# - 如何根据所需的类注册依赖项(使用 ASP CORE 中的内置 IOC 容器)

java - 从图像中剪切形状

java - 绘图有问题

c# - ArgumentException 和 just Exception 有什么区别?

c# - 当类型已知时,类型 'T' 不能用作泛型类型或方法错误中的类型参数

c - 掌握Windows 10的声音管理

c++ - 为什么 DrawThemeText 不能正常工作?

c++ - mfc 目录选择器?

opengl - 有人可以描述 Ken Silverman 的 Voxlap 引擎使用的算法吗?