Win32 EnumFontFamiliesEx doesn't call the callback I provide

Will Pittenger 306 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
.NET
.NET
Microsoft Technologies based on the .NET software framework.
3,840 questions
Windows Presentation Foundation
Windows Presentation Foundation
A part of the .NET Framework that provides a unified programming model for building line-of-business desktop applications on Windows.
2,769 questions
Windows API - Win32
Windows API - Win32
A core set of Windows application programming interfaces (APIs) for desktop and server applications. Previously known as Win32 API.
2,612 questions
C#
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.
10,912 questions
0 comments No comments
{count} votes

2 answers

Sort by: Most helpful
  1. Castorix31 85,131 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;
           }
    

  2. KOZ6.0 6,395 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

Your answer

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