Share via

Win32 EnumFontFamiliesEx doesn't call the callback I provide

Will Pittenger 311 Reputation points
2023-09-28T03:32:36.0033333+00:00

I wrote a class to test if a WPF font is fixed width. But the only way I could find to do that was to get the LOGFONT and test it. So I went through a lot of P/Invoke hoops to get there. I created a DC for the display and asked it to enumerate the fonts. My callback is never called. Can someone tell me why? EnumFontFamiliesEx is always returning 1. The only thing the MSDN documentation says about the return value is it's the last return value from the callback. But my callback was never called. My code is attached.

// Ignore Spelling: Util iptr
Developer technologies | Windows Presentation Foundation
Windows development | Windows API - Win32
Developer technologies | .NET | Other
Developer technologies | C#
Developer technologies | C#

An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.

0 comments No comments

2 answers

Sort by: Most helpful
  1. KOZ6.0 6,810 Reputation points
    2023-10-03T15:00:35.8333333+00:00

    If you want to know if it's a fixed pitch, I think you can use the following method.

    using System.Linq;
    using System.Windows.Media;
    
    internal static class FontFamilyHelper
    {
        public static bool FontIsFixedWidth(this FontFamily ffToCheck) {
            foreach (Typeface typeface in ffToCheck.GetTypefaces()) {
                if (typeface.TryGetGlyphTypeface(
                                out GlyphTypeface glyphTypeface)) {
                    double[] charWidths = Enumerable.Range('0', 'z' - '0' + 1)
                        .Select(c => glyphTypeface.AdvanceWidths[(char)c]).ToArray();
                    var min = charWidths.Min();
                    var max = charWidths.Max();
                    if (min != max) {
                        return false;
                    }
                }
            }
            return true;
        }
    }
    

    This function uses variable length if maximum and minimum width values from '0' to 'z' are different, otherwise it uses fixed length.

    0 comments No comments

  2. Castorix31 91,866 Reputation points
    2023-09-28T04:39:32.12+00:00

    I don't see your attached code, but this test works for me :

                    using (Graphics gr = Graphics.FromHwnd(IntPtr.Zero))
                    {
                        IntPtr hDC = gr.GetHdc();
                        LOGFONT lf = new LOGFONT();
                        lf.lfCharSet = DEFAULT_CHARSET;
                        IntPtr pLogFont = Marshal.AllocHGlobal(Marshal.SizeOf(lf));
                        Marshal.StructureToPtr(lf, pLogFont, true);
                        EnumFontFamiliesEx(hDC, pLogFont, EnumFontFamExProc, IntPtr.Zero, 0);                   
                        Marshal.FreeHGlobal(pLogFont);
                        gr.ReleaseHdc(hDC);
                    }
    
            public int EnumFontFamExProc(ref ENUMLOGFONTEX lpelfe, ref NEWTEXTMETRICEX lpntme, uint FontType, IntPtr lParam)
            {          
                // code...
                return 1;
            }
    

    with :

           [DllImport("Gdi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
           public static extern int EnumFontFamiliesEx(IntPtr hdc, [In] IntPtr pLogfont, FONTENUMPROC lpEnumFontFamExProc, IntPtr lParam, uint dwFlags);
    
           public delegate int FONTENUMPROC(ref ENUMLOGFONTEX lpelfe, ref NEWTEXTMETRICEX lpntme, uint FontType, IntPtr lParam);
    
           public const int ANSI_CHARSET = 0;
           public const int DEFAULT_CHARSET = 1;
           public const int SYMBOL_CHARSET = 2;
    
           public const int LF_FACESIZE = 32;
           public const int LF_FULLFACESIZE = 64;        
    
           [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
           public struct ENUMLOGFONTEX
           {
               public LOGFONT elfLogFont;
               [MarshalAs(UnmanagedType.ByValTStr, SizeConst = LF_FULLFACESIZE)]
               public string elfFullName;
               [MarshalAs(UnmanagedType.ByValTStr, SizeConst = LF_FACESIZE)]
               public string elfStyle;
               [MarshalAs(UnmanagedType.ByValTStr, SizeConst = LF_FACESIZE)]
               public string elfScript;
    
               public override string ToString()
               {
                   return elfFullName;
               }
           }
    
           [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
           public struct NEWTEXTMETRIC
           {
               public int tmHeight;
               public int tmAscent;
               public int tmDescent;
               public int tmInternalLeading;
               public int tmExternalLeading;
               public int tmAveCharWidth;
               public int tmMaxCharWidth;
               public int tmWeight;
               public int tmOverhang;
               public int tmDigitizedAspectX;
               public int tmDigitizedAspectY;
               public char tmFirstChar;
               public char tmLastChar;
               public char tmDefaultChar;
               public char tmBreakChar;
               public byte tmItalic;
               public byte tmUnderlined;
               public byte tmStruckOut;
               public byte tmPitchAndFamily;
               public byte tmCharSet;
               int ntmFlags;
               int ntmSizeEM;
               int ntmCellHeight;
               int ntmAvgWidth;
           }
    
           [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
           public struct FONTSIGNATURE
           {
               [MarshalAs(UnmanagedType.ByValArray)]
               int[] fsUsb;
               [MarshalAs(UnmanagedType.ByValArray)]
               int[] fsCsb;
           }
    
           public struct NEWTEXTMETRICEX
           {
               NEWTEXTMETRIC ntmTm;
               FONTSIGNATURE ntmFontSig;
           }
    
           [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
           public class LOGFONT
           {            
               public int lfHeight;
               public int lfWidth;
               public int lfEscapement;
               public int lfOrientation;
               public int lfWeight;
               public byte lfItalic;
               public byte lfUnderline;
               public byte lfStrikeOut;
               public byte lfCharSet;
               public byte lfOutPrecision;
               public byte lfClipPrecision;
               public byte lfQuality;
               public byte lfPitchAndFamily;
               [MarshalAs(UnmanagedType.ByValTStr, SizeConst = LF_FACESIZE)]
               public string lfFaceName;
           }
    

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.