Search code examples
c#memoryfontsprotected

Using a method to get Embedded Font Causes Protected Memory Error


I am using this code to get an embedded font:

/// <summary>
    /// Returns an Embedded Font
    /// </summary>
    /// <param name="ImagePath">String begins with namespace e.g MyProgram.Image.png</param>
    /// <returns></returns>
    public static Font GetEmbeddedFont(string FontPath, float Size)
    {
        Font _font = null;
        Thread getFontThread = new Thread(() => GetFont(FontPath, Size, out _font));
        getFontThread.Start();
        getFontThread.Join();
        return _font;            
    }
    #region GetFont
    private static void GetFont(string FontPath, float Size, out Font FontOut)
    {
        Font fnt = null;
        Assembly asm = Assembly.GetExecutingAssembly();
        Stream resStream = asm.GetManifestResourceStream(FontPath);
        if (null != resStream)
        {
            // 
            // 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[resStream.Length];
            resStream.Read(rgbyt, 0, rgbyt.Length);
            resStream.Close();
            // 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();    
        FontOut = fnt;            
    }

I use a Thread to attempt to wait for it to finish, because the error seems to occur, if this method is called multiple times within a short space of time.

How can I stop this error occuring, I think it has something to do with the method being called within quick succession of each other.

I get the exception here:

_font = value;
            using (Graphics g = _parent.CreateGraphics())
            {
                SizeF soize = g.MeasureString(_text, _font);
                _size = new Size((int)soize.Width, (int)soize.Height);
                _width = _size.Width;
                _height = _size.Height;
            }

On the line g.MeasureString(_text, _font);

However I know that the error is in the GetEmbeddedFont Method, as it only throws an error if the font is set using the GetEmbeddedFont method.

It will work fine once, but if it is used a second time to shortly after the first, it will throw the error.

And if I debug the code, _font returns this:

{Name = '((System.Drawing.Font)(_font)).fontFamily.Name' threw an exception of type 'System.ArgumentException' Size=15.0}

Solution

  • There's a documentation problem with AddMemoryFont(), it doesn't specify how long the pointer needs to remain valid. I've always chosen the conservative route and made sure to not release the memory until after the program stops using the private font. That has worked well, no access violations.

    I thus strongly recommend you remove the Marshal.FreeCoTaskMem() call. Either entirely, Windows automatically cleans up when the program terminates, or move it to, say, the FormClosed event. The same goes for your m_pfc.Dispose() call. Don't release resources until you're done with them.