PrivateFontCollection.AddMemoryFont在Windows Server 2012 R2上产生随机错误

8

我有一个.NET 3.5应用程序,使用PrivateFontCollection.AddMemoryFont将字体加载到内存中,并使用这些字体生成图像。最近我在Windows Server 2012 R2上安装了它,但会出现间歇性错误。

这个问题可以通过以下方法来说明:

private Bitmap getImage(byte[] fontFile)
{
    using (PrivateFontCollection fontCollection = new PrivateFontCollection())
    {
        IntPtr fontBuffer = Marshal.AllocCoTaskMem(fontFile.Length);
        Marshal.Copy(fontFile, 0, fontBuffer, fontFile.Length);
        fontCollection.AddMemoryFont(fontBuffer, fontFile.Length);

        Bitmap image = new Bitmap(200, 50);
        using (Font font = new Font(fontCollection.Families[0], 11f, FontStyle.Regular))
        {
            using (Graphics graphics = Graphics.FromImage(image))
            {
                graphics.DrawString(String.Format("{0:HH:mm:ss}", DateTime.Now), font, Brushes.White, new PointF(0f, 0f));
            }
        }
        return image;
    }
}

在Windows 7上,这个功能一直都能正常工作。但是在Windows Server 2012 R2上,如果使用超过一个字体反复调用它,它就会失败。例如:

getImage(File.ReadAllBytes("c:\\Windows\\Fonts\\Arial.ttf"));

这个功能即使被调用了数百次,也能正常工作,但是如果使用多种字体进行调用:

getImage(File.ReadAllBytes("c:\\Windows\\Fonts\\Wingding.ttf"));
getImage(File.ReadAllBytes("c:\\Windows\\Fonts\\Arial.ttf"));

这段代码在最开始的几个调用(约20个)时工作正常,但之后会开始产生随机结果(第二次调用有时会返回带有wingdings字体的文本图像——即混淆了字体)。

我偶尔(很少)在DrawString调用中遇到“GDI+中发生通用错误”。

在Windows 7上没有出现任何这些错误。

我尝试了各种清理选项,但都没有成功。

作为一种解决方法,我尝试将字体文件写入磁盘,然后使用AddFontFile加载,但是(在Windows 2012 R2上),字体文件将被锁定整个进程的生命周期,因此无法删除。这使得这个选项是不可接受的。

如何让AddMemoryFont始终工作,或让AddFontFile解锁文件,任何帮助都将不胜感激。


标准的GDI+损失,释放字体并不会真正销毁它。它会被放回到缓存中,并假定将再次使用它。创建字体是一个重要的性能优化,因为代价相当昂贵。当你销毁它们的家时,私有字体就会使用已释放的内存。这会产生令人困惑的结果,包括严重崩溃。您需要保留集合以及IntPtr。 - Hans Passant
你找到解决方法了吗?我在Windows Server 2012R2上也遇到了同样的问题。这段代码在Windows Server 2003上运行良好。 - Rosdi Kasim
@HansPassant 我遇到了一个测试套件,即使没有处理任何字体或字体族,当调用AddMemoryFont()时,GDI+也会向集合中添加错误的字体族。 - Pierre Arnaud
1个回答

1
一个晚回答,但也许其他人会对它感到满意: 我遇到了完全相同的问题,在尝试了几个小时后,我发现对我有效的解决方案是将字体(从数据库中的字节数组)保存到本地文件中,并使用addFontFile方法加载该文件。 所有问题都消失了。虽然不是理想的解决方案,但却是有效的。
var path = Path.Combine(TemporaryFontPath, customFont.FontFileName);
if (!Directory.Exists(Path.GetDirectoryName(path)))
    Directory.CreateDirectory(Path.GetDirectoryName(path));
if(!File.Exists(path))
    File.WriteAllBytes(path, customFont.FontBytes);

using (var pvc = new PrivateFontCollection())
{
    pvc.AddFontFile(path);
    return pvc.Families.FirstOrDefault();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接