【问题标题】:Protected Memory Exception on Images when Fonts are Called调用字体时图像上受保护的内存异常
【发布时间】:2015-08-07 20:30:48
【问题描述】:

编辑:所以,我知道发生了什么,我只是不明白为什么。我的Dictionary<string, object> 可以很好地存储字体,但是有一个特定的函数在运行,当它运行时,字典会被部分清除。

之前:

之后:

在这些图像之间运行的代码是:

    public static int countLines(string a)
    {
        int count = 0;
        string line;
        System.IO.StreamReader file;

        try
        {
            file = new System.IO.StreamReader(@a);
        }

        catch (Exception ex)
        {
            MessageBox.Show("Missing " + a + "\n\n" + ex.ToString(), "Exception");
            return 0;
        }

        while ((line = file.ReadLine()) != null)
        {

            if (line.Substring(0, 2) == "10") { count++; }

        }

        file.Close();

        return count;
    }

---结束编辑---

当我使用太多自定义字体时,我收到了this tracedump。我正在使用相同的solution listed here,并尝试了投票修复(删除 dispose 和一个编组调用),这会立即导致 GDI+ 错误。实际上,我有该代码的修改版本,因此我可以调用并获取不同的字体:

    #region Font setup
    //Setup code for custom fonts
    [DllImport("gdi32.dll")]
    private static extern IntPtr AddFontMemResourceEx(byte[] pbFont, int cbFont, IntPtr pdv, out uint pcFonts);

    [DllImport("gdi32.dll")]
    internal static extern bool RemoveFontMemResourceEx(IntPtr fh);

    static private IntPtr m_fh = IntPtr.Zero;
    static private PrivateFontCollection m_pfc = null;

    public static Font getFont(string fonttype, float size)
    {
        m_pfc = null;

        Font fnt = null;

        if (null == m_pfc)
        {
            Stream stmFont = null;

            // First load the font as a memory stream

            switch (fonttype)
            {
                case "MICR":
                    stmFont = Assembly.GetExecutingAssembly().GetManifestResourceStream("QATestFileGenTools.Fonts.MICR.ttf");
                    break;

                case "HAND":
                    Random rnd = new Random(DateTime.Now.Millisecond);
                    int rr = rnd.Next(0, 3);

                    if (rr == 0)
                    {
                        stmFont = Assembly.GetExecutingAssembly().GetManifestResourceStream("QATestFileGenTools.Fonts.hand1.ttf");
                        size += rnd.Next(0, 4);
                    }

                    if (rr == 1)
                    {
                        stmFont = Assembly.GetExecutingAssembly().GetManifestResourceStream("QATestFileGenTools.Fonts.hand2.ttf");
                        size += rnd.Next(2, 6);
                    }

                    if (rr == 2)
                    {
                        stmFont = Assembly.GetExecutingAssembly().GetManifestResourceStream("QATestFileGenTools.Fonts.hand3.ttf");
                        size += rnd.Next(6, 10);
                    }

                    break;

                case "SIG":
                    stmFont = Assembly.GetExecutingAssembly().GetManifestResourceStream("QATestFileGenTools.Fonts.signature.ttf");
                    break;

                default:
                    MessageBox.Show("Bad call to getFont()", "Error");
                    break;
            }

            if (null != stmFont)
            {

                // 
                // GDI+ wants a pointer to memory, GDI wants the memory.
                // We will make them both happy.
                //

                // First read the font into a buffer
                byte[] rgbyt = new Byte[stmFont.Length];
                stmFont.Read(rgbyt, 0, rgbyt.Length);

                // Then do the unmanaged font (Windows 2000 and later)
                // The reason this works is that GDI+ will create a font object for
                // controls like the RichTextBox and this call will make sure that GDI
                // recognizes the font name, later.
                uint cFonts;
                AddFontMemResourceEx(rgbyt, rgbyt.Length, IntPtr.Zero, out cFonts);


                // Now do the managed font
                IntPtr pbyt = Marshal.AllocCoTaskMem(rgbyt.Length);
                if (null != pbyt)
                {
                    Marshal.Copy(rgbyt, 0, pbyt, rgbyt.Length);

                    m_pfc = new PrivateFontCollection();
                    m_pfc.AddMemoryFont(pbyt, rgbyt.Length);
                    Marshal.FreeCoTaskMem(pbyt);
                }
            }
        }

        if (m_pfc.Families.Length > 0)
        {
            // Handy how one of the Font constructors takes a
            // FontFamily object, huh? :-)
            fnt = new Font(m_pfc.Families[0], size);
        }

        m_pfc.Dispose();
        return fnt;
    }
    //End setup for fonts
    #endregion

如果我删除 dispose() 和 FreeCoTaskMem() is this,我不可避免地会遇到 GDI+ 错误。

这两个问题都不会立即发生。在引发任何异常之前,我似乎可以平均运行 20-25 次调用。

任何想法将不胜感激。当我只使用一种字体时,我从来没有出现过错误(每次都会产生 1000+)。至少有趣的是,如果我只调用两种字体,这也不是问题。但是当“手”被混入其中时,这似乎就是问题所在。

调用它的方法是:

public static byte[] GenCheckImage(string outputPath, string frontBack, string MICRline, string amount, string[] imageOptions, Dictionary<string, object> dictCfgIn, string names = "John Smith", string date = null, string lar = null)//, string payeeName, string date)
    {

        System.Reflection.Assembly thisExe;
        thisExe = System.Reflection.Assembly.GetExecutingAssembly();

        if (date == null)
        {
            date = DateTime.Now.AddDays(-4).ToString("d");
        }

        if (lar == null)
        {
            lar = "Some Dollars & xx/100";
        }

        if (frontBack == "F")
        {
            //Open the front image to frontImage Image source from embedded resource

            System.IO.Stream file;

            Random rnd = new Random(DateTime.Now.Millisecond);
            int a = rnd.Next(imageOptions.Length);

           // if (a == 0)  {file = thisExe.GetManifestResourceStream("QATestFileGenTools.checkFront1.bmp");}
           // else if (a == 1)  {file = thisExe.GetManifestResourceStream("QATestFileGenTools.checkFront2.bmp");}
            file = thisExe.GetManifestResourceStream(imageOptions[a]);
            Bitmap b;

            using (System.Drawing.Image frontImage = System.Drawing.Image.FromStream(file))//;
            {


            byte[] binaryData = new Byte[file.Length];
            long bytesRead = file.Read(binaryData, 0,
                                    (int)file.Length);

            Bitmap b2 = new Bitmap(frontImage);

            file.Close();

            Font micrFont = getFont("MICR", 18);

            Font objFont;
            if ((bool)dictCfgIn["Handwriting"])
                objFont = getFont("HAND", 20);
            else
                objFont = new System.Drawing.Font("Arial", 20);

            Font sigFont = getFont("SIG", 30);
            string[] sigName = NSCreateRAW.CreateRaw.genName();
            string sigString = sigName[0] + sigName[1] + rnd.Next(1, 6).ToString();

            amount = amount.TrimStart('0'); //Insert(2, "XYZ")
            amount = amount.Insert((amount.Length - 2), ".");
            //Draw something
            //Bitmap b = new Bitmap(frontImage);
            b = new Bitmap(frontImage);
            Graphics graphics = Graphics.FromImage(b);

            if (imageOptions[a] == "QATestFileGenTools.checkFront1.bmp")  // ProfitStars Check Template
            {
                graphics.DrawString(date, objFont, Brushes.Black, 690, 150);
                graphics.DrawString(lar, objFont, Brushes.Black, 150, 280);
                graphics.DrawString(names, objFont, Brushes.Black, 200, 215);
                graphics.DrawString(MICRline, micrFont, Brushes.Black, 200, 490);
                graphics.DrawString(amount, objFont, Brushes.Black, 985, 230);
                graphics.DrawString(sigString, sigFont, Brushes.Black, 680, 400);
            }

            else if (imageOptions[a] == "QATestFileGenTools.checkFront2.bmp")  // TWS Check Template
            {
                graphics.DrawString(date, objFont, Brushes.Black, 735, 105);
                graphics.DrawString(lar, objFont, Brushes.Black, 160, 250);
                graphics.DrawString(names, objFont, Brushes.Black, 200, 165);
                graphics.DrawString(MICRline, micrFont, Brushes.Black, 200, 490);
                graphics.DrawString(amount, objFont, Brushes.Black, 1020, 185);
                graphics.DrawString(sigString, sigFont, Brushes.Black, 690, 380);
            }

            else if (imageOptions[a] == "QATestFileGenTools.checkFront3.bmp")  // "Blank" Check Template
            {
                graphics.DrawString(date, objFont, Brushes.Black, 935, 110);
                graphics.DrawString(lar, objFont, Brushes.Black, 180, 275);
                graphics.DrawString(names, objFont, Brushes.Black, 220, 210);
                graphics.DrawString(MICRline, micrFont, Brushes.Black, 220, 470);
                graphics.DrawString(amount, objFont, Brushes.Black, 1000, 210);
                graphics.DrawString(sigString, sigFont, Brushes.Black, 690, 395);
            }

            else
            {
                //graphics.DrawString(MICRline, micrFont, Brushes.Black, 200, 490);
                //graphics.DrawString(amount, objFont, Brushes.Black, 985, 230);
            }

            if ((bool)dictCfgIn["Bad Images"] && (bool)dictCfgIn["Skew Images"])
            {
                Point[] destinationPoints = {
                    new Point(0, 50),   // destination for upper-left point of  
                  // original 
                    new Point(1170, 0),  // destination for upper-right point of  
                  // original 
                    new Point(30, 585)};
                graphics.DrawImage(b, destinationPoints);
            }

            if ((bool)dictCfgIn["Bad Images"] && (bool)dictCfgIn["Bar Images"])
            {
                Pen penWide = new Pen(Color.Black, 45);
                Pen penNarrow = new Pen(Color.Black, 2);
                Point[] points1 =
                {
                     new Point(0   , 250),
                     new Point(1200, 250)
                };
                Point[] points2 =
                {
                    new Point (0   , 280),
                    new Point (1200, 280)
                };
                graphics.DrawLines(penWide, points1);
                graphics.DrawLines(penNarrow, points2);
            }
            }
            //Convert to TIF - requires BitMiracle.LibTiff.Classic
            byte[] tiffBytes = GetTiffImageBytes(b, false);

            //File.WriteAllBytes("checkfront.tif", tiffBytes);

            //Encoding enc = Encoding.GetEncoding(1252);
            //string result = enc.GetString(tiffBytes);

            //File.Write("check2.tif", result);

            return tiffBytes;
        }

第二段代码中的 using 包装器是新的 - 我认为它可能会有所帮助,但它根本没有改变结果。

【问题讨论】:

  • 阅读标签说明怎么样?
  • 我做的怎么样...怎么样?
  • 不够接近。检查一下。
  • 是的,什么都没有……
  • 您可能会考虑查阅编辑日志?

标签: c# fonts gdi+


【解决方案1】:

最后,事实证明非托管内存只是一个坏主意。其他进程需要内存,并且指针变得无效。

我最终改用了PrivateFontCollection。我仍然使用该集合中的字典,只是因为它使访问更容易,并使我的许多代码以类似的方式工作。在处理大型数据集时,我开始使用的代码示例太不稳定了。

我在这里修改了解决方案:http://www.emoticode.net/c-sharp/load-custom-ttf-font-file-and-use-it-on-any-winform-controls.html

【讨论】:

  • 这是一个简单的错误,在使用 Font 对象时不能调用 Marshal.FreeCoTaskMem()。 PrivateFontCollection 有同样的要求。
  • @HansPassant 我实际上删除了 FreeCoTaskMem() 和 Dispose() 并最终遇到了同样的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-08-05
  • 2014-08-07
  • 1970-01-01
  • 1970-01-01
  • 2010-11-17
  • 2021-09-03
  • 1970-01-01
相关资源
最近更新 更多