C# 带有双缓冲图形的AccessViolationException
正如标题所示,当我试图使用C# 带有双缓冲图形的AccessViolationException,c#,.net,winforms,access-violation,double-buffering,C#,.net,Winforms,Access Violation,Double Buffering,正如标题所示,当我试图使用BufferedGraphics对象在.NET中绘制控件时,我遇到了AccessViolationException。这种情况会在一段时间后发生,或早或晚。评估对象的地址时,我注意到它随着程序的进行而不断增加。 我已经采取了一些措施来保存内存,这似乎有点帮助,但并没有最终解决问题。这个问题似乎与我是否在函数结束前处理图形对象无关 private void checkMemory() { long mem = GC.GetTotalMemo
BufferedGraphics
对象在.NET中绘制控件时,我遇到了AccessViolationException。这种情况会在一段时间后发生,或早或晚。评估对象的地址时,我注意到它随着程序的进行而不断增加。
我已经采取了一些措施来保存内存,这似乎有点帮助,但并没有最终解决问题。这个问题似乎与我是否在函数结束前处理图形对象无关
private void checkMemory()
{
long mem = GC.GetTotalMemory(false);
//Console.WriteLine("Allocated memory: " + mem.ToString());
if (mem > criticalMemorySize)
{
Console.WriteLine("GC started");
GC.Collect(1, GCCollectionMode.Forced, true);
}
}
BufferedGraphics graphics;
protected override void OnPaint(PaintEventArgs e)
{
lock (e.Graphics)
{
base.OnPaint(e);
checkMemory();
if (e.ClipRectangle.Width * e.ClipRectangle.Height == 0)
return;
if (graphics == null || graphics.Graphics == null || graphics.Graphics.ClipBounds != e.ClipRectangle)
graphics = _bufferedGraphicsContext.Allocate(e.Graphics, e.ClipRectangle);
PaintBackground(graphics.Graphics);
PaintHeader(graphics.Graphics);
PaintBody(graphics.Graphics);
DrawGrid(graphics.Graphics);
//...
graphics.Render(e.Graphics);
graphics.Graphics.Dispose();
graphics.Dispose();
}
}
调用g.DrawString
时,此函数中出现异常:
private void PaintBody(Graphics g)
{
Font seriffont = Design.CreateSerifFont(fontSize);
Point mousePos = PointToClient(MousePosition);
int hfeed = headHeight + headLineWidth - 2; //Dunno
for (int y = 0; y < LineCount; y++)
{
int vfeed = 0;
for (int x = 0; x < ColumnCount; x++)
{
String s = "";
Rectangle area = new Rectangle(vfeed, hfeed, colwidth, cellHeight);
switch (_tableData[y][x])
{
case ExBool.DontCare:
s = "*";
break;
case ExBool.False:
s = "0";
break;
case ExBool.True:
s = "1";
break;
default:
s = " ";
break;
}
Color backColor = cellBackColor;
if (_activeHeaderColumn == x)
{
backColor = cellActiveColColor;
}
if (area.Contains(mousePos))
{
backColor = cellHoverColor;
}
if (area.Contains(mousePos) &&
((MouseButtons & MouseButtons.Left) == MouseButtons.Left))
{
backColor = cellActiveColor;
}
g.FillRectangle(new SolidBrush(backColor), area);
g.DrawString(s, seriffont, Brushes.Black, area,
new StringFormat() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center });
if (x != InputValues.Count() - 1)
{
vfeed += lineWidth + colwidth;
}
else
{
vfeed += typeSepLineWidth + colwidth;
}
}
hfeed += cellHeight + lineWidth;
}
}
有人知道如何解决这个问题吗?
提前谢谢
编辑:
全局创建上下文变量,如下所示:
BufferedGraphicsContext _bufferedGraphicsContext = new BufferedGraphicsContext()
对于经常使用BufferedGraphics
的情况,文档中建议这样做。
这些函数是自定义控件的一部分,该控件允许用户输入二进制函数输入和输出。因为我不知道有任何类似的控件,所以我自己写了一个。
每当控件需要重新绘制时(例如,如果用户移动鼠标、单击某物等),都会调用上述函数,因为所有这些操作都需要视觉反馈。
一般来说,它可以按需要工作,除了在使用控件一段时间后发生的异常
编辑2:
由于异常几乎完全发生在
DrawString
函数上,那么NullReference(根据HResult,如果我没有弄错的话)是否可能不是由于绘图过程本身,而是由于某个参数或其他原因造成的?这可能与您的情况不完全相同(我相信两年半后,这对你来说已经不现实了),但仍然如此
代码中有以下行:
Font seriffont = Design.CreateSerifFont(fontSize);
看起来这是你自己的代码,因为我在Google中找不到任何关于它的提及。下面是该方法的实现,它可能会导致你观察到的错误:
static class Design {
static Font CreateSerifFont() {
var fonts = new PrivateFontCollection();
fonts.AddFontFile(@"c:\some_font.ttf");
return new Font(fonts.Families[0], 12);
}
}
我想说的是,这是PrivateFontCollection
的一个糟糕设计-它可以在它的Font
仍在使用时进行处理,这将导致AccessViolationException。可以通过将Font
从本地范围移动到类字段来修复上面的代码,类似这样:
static class Design {
private static readonly PrivateFontCollection Fonts = new PrivateFontCollection();
public static readonly Font SerifFont;
static Design() {
Fonts.AddFontFile(@"c:\some_font.ttf");
SerifFont = new Font(Fonts.Families[0], 12);
}
}
请注意:
Graphics
对象不包含任何图形;它是一种工具,允许您在相关位图(包括控件的表面)上绘制。不要缓存“buffer”它。对于真正的双缓冲,您需要“buffer”位图。或者使用像PictureBox这样的双缓冲控件。-不确定是什么\u bufferedGraphicsContext.AlloCATE 确实如此,但是如果它确实导致了E图形的处理,那么你就得到了你的例外。看起来我是在思考C++,试图写C语言。你不必管理图形对象。你甚至不必自己处理双缓冲(尽管在特殊情况下你可以证明它)。。强制GC是一个坏主意。如果你认为你需要这样做,那么你就有了一个严重的设计缺陷。感谢你迄今为止的关注。@TAWAllocate
函数,如果我没有弄错的话,允许我创建一个BufferedGraphics
对象,我可以将绘图函数应用到该对象上,然后将其渲染到t上当我完成时,他会显示实际的图形。我需要它来防止闪烁,这在异常发生之前确实可以很好地工作。我假设它在内部使用位图。如果我正确地调用了文档,我会按原样使用BufferedGraphics
类,但我仍然无法解释为什么会有访问权限违反…@DonBoitnott我删除了GC位,因为它没有太大变化…我想你是对的,我只是以错误的方式想象它,但我找不到任何其他解释。手动双缓冲对我来说似乎是必要的,因为自动双缓冲不能防止大量闪烁,而这种方式看起来很不错在崩溃之前保持平滑一段时间。如果控制更大和/或迭代次数更多,这种情况会更快发生
static class Design {
private static readonly PrivateFontCollection Fonts = new PrivateFontCollection();
public static readonly Font SerifFont;
static Design() {
Fonts.AddFontFile(@"c:\some_font.ttf");
SerifFont = new Font(Fonts.Families[0], 12);
}
}