C# WinForms中DrawString报错:异步改造后参数无效
WinForms打印发票抛出ArgumentException:无效参数(异步改造后出现)
问题详情
原本正常的WinForms项目,在将方法改为异步对接Supabase后,打印发票时抛出如下异常:
System.ArgumentException: Parameter is not valid. at System.Drawing.Graphics.CheckErrorStatus(Int32 status) at System.Drawing.Graphics.DrawString(String s, Font font, Brush brush, RectangleF layoutRectangle, StringFormat format) at Panaderia_Supabase.Forms.Cobranza.PrintPage(Object sender, PrintPageEventArgs e) in D:\Dev\Nueva carpeta\Panaderia-Supabase\Forms\Cobranza.cs:line 217
已确认所有传入DrawString的参数均不为空,排查发现问题出在Graphics对象上:
自定义的GetHeight方法中,运行时graphics.NativeGraphics为有效地址0x0000024f04c04230,但打印时该值变为0x0000000000000000(IntPtr.Zero)。
public float GetHeight(Graphics graphics) { ArgumentNullException.ThrowIfNull(graphics, "graphics"); if (graphics.NativeGraphics == IntPtr.Zero) { throw new ArgumentException("graphics"); } SafeNativeMethods.Gdip.CheckStatus(SafeNativeMethods.Gdip.GdipGetFontHeight(new HandleRef(this, NativeFont), new HandleRef(graphics, graphics.NativeGraphics), out var size)); return size; }
尝试传入固定值仍无法解决,异常信息不够明确,寻求解决方案。
核心原因与解决方案
1. 异步操作导致UI线程上下文丢失
WinForms打印API(包括PrintPage事件)必须在UI线程执行。如果在PrintPage事件处理中调用了未正确同步上下文的异步方法,会导致Graphics对象的句柄失效。
- 解决:避免在
PrintPage事件内执行异步操作,提前在异步方法中获取好所有打印所需的数据,再触发打印流程。 - 如果必须在打印流程中调用异步方法,使用
Control.Invoke切回UI线程:private async void PrintButton_Click(object sender, EventArgs e) { // 提前异步获取数据,保留UI上下文 var invoiceData = await _supabaseClient.Invoices.GetAsync().ConfigureAwait(true); // 数据就绪后触发打印 printDocument.Print(); }
2. Graphics对象被提前释放或GC回收
Graphics对象与当前打印上下文绑定,不能在异步流程中传递、保存或延迟使用,必须在PrintPage事件的同步代码块内直接使用。
- 解决:确保所有依赖Graphics的操作(如
DrawString、GetHeight)都在PrintPage事件的同步逻辑中完成,不要将Graphics对象传入异步方法。
3. 验证Graphics对象有效性
在使用Graphics对象前,除了检查非空,还需验证其未被释放:
private void PrintPage(object sender, PrintPageEventArgs e) { if (e.Graphics.IsDisposed) { throw new InvalidOperationException("Graphics对象已被释放,无法执行打印操作"); } // 后续打印逻辑 }
4. 确保异步方法保留UI上下文
在调用Supabase的异步方法时,使用ConfigureAwait(true)确保回到UI线程上下文,避免后续操作在非UI线程执行:
var data = await _supabaseClient.SomeTable.Select().GetAsync().ConfigureAwait(true);
补充说明
如果以上方案仍无法解决问题,请提供PrintPage方法的完整代码,以及触发打印的异步逻辑代码,以便进一步定位问题。
内容的提问来源于stack exchange,提问作者Lucas Rius
相关产品推荐
相关产品推荐

