使用对 EnumFontFamiliesEx 的 DLL 调用在 C# 中枚举活动字体让我感到困惑
我的最终目标是列出 Windows 计算机上的所有活动字体。例如,如果您启动写字板或Word,您将看到它们全部。其中一些字体来自 windows\fonts 文件夹,但不是全部。有些是使用 gdi32.dll 调用 AddFontResource(...) 动态注册的。现在,您可能会认为 C# 支持检索整个列表,如果是这样,请告诉我!
然而,在花费了很多时间之后,获取已安装系统字体的常规 C# 调用只会检索所有实际活动字体中的一部分。例如,InstalledFontCollection.Families 找不到任何使用 AddFontResource 添加的字体(它找到了写字板中列出的大约 25% 的字体!)
虽然我想在 C# 中执行此操作,但似乎我必须使用更多 gdi32。 dll 调用。因此开始了我的 EnumFontFamiliesEx 问题。
我已经设法让以下代码正常工作,以至于我收到了一堆带有字体的回调。然而,结构 ENUMLOGFONTEX 似乎已损坏,例如 lpelfe.elfFullName 通常包含几个方形字符,而不是真实姓名。也许我在编组或结构定义中犯了一个错误,不太确定。我尝试了很多迭代和无休止的谷歌搜索,但没有成功。
任何帮助将非常感激。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Windows.Media;
using System.Runtime.InteropServices;
using Microsoft.Win32;
namespace FontRetriever
{
public partial class TestForm : Form
{
[DllImport("gdi32.dll")]
static extern int EnumFontFamiliesEx(IntPtr hdc,
[In] IntPtr pLogfont,
EnumFontExDelegate lpEnumFontFamExProc,
IntPtr lParam,
uint dwFlags);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public class LOGFONT
{
public int lfHeight;
public int lfWidth;
public int lfEscapement;
public int lfOrientation;
public FontWeight lfWeight;
[MarshalAs(UnmanagedType.U1)]
public bool lfItalic;
[MarshalAs(UnmanagedType.U1)]
public bool lfUnderline;
[MarshalAs(UnmanagedType.U1)]
public bool lfStrikeOut;
public FontCharSet lfCharSet;
public FontPrecision lfOutPrecision;
public FontClipPrecision lfClipPrecision;
public FontQuality lfQuality;
public FontPitchAndFamily lfPitchAndFamily;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string lfFaceName;
}
public enum FontWeight : int
{
FW_DONTCARE = 0,
FW_THIN = 100,
FW_EXTRALIGHT = 200,
FW_LIGHT = 300,
FW_NORMAL = 400,
FW_MEDIUM = 500,
FW_SEMIBOLD = 600,
FW_BOLD = 700,
FW_EXTRABOLD = 800,
FW_HEAVY = 900,
}
public enum FontCharSet : byte
{
ANSI_CHARSET = 0,
DEFAULT_CHARSET = 1,
SYMBOL_CHARSET = 2,
SHIFTJIS_CHARSET = 128,
HANGEUL_CHARSET = 129,
HANGUL_CHARSET = 129,
GB2312_CHARSET = 134,
CHINESEBIG5_CHARSET = 136,
OEM_CHARSET = 255,
JOHAB_CHARSET = 130,
HEBREW_CHARSET = 177,
ARABIC_CHARSET = 178,
GREEK_CHARSET = 161,
TURKISH_CHARSET = 162,
VIETNAMESE_CHARSET = 163,
THAI_CHARSET = 222,
EASTEUROPE_CHARSET = 238,
RUSSIAN_CHARSET = 204,
MAC_CHARSET = 77,
BALTIC_CHARSET = 186,
}
public enum FontPrecision : byte
{
OUT_DEFAULT_PRECIS = 0,
OUT_STRING_PRECIS = 1,
OUT_CHARACTER_PRECIS = 2,
OUT_STROKE_PRECIS = 3,
OUT_TT_PRECIS = 4,
OUT_DEVICE_PRECIS = 5,
OUT_RASTER_PRECIS = 6,
OUT_TT_ONLY_PRECIS = 7,
OUT_OUTLINE_PRECIS = 8,
OUT_SCREEN_OUTLINE_PRECIS = 9,
OUT_PS_ONLY_PRECIS = 10,
}
public enum FontClipPrecision : byte
{
CLIP_DEFAULT_PRECIS = 0,
CLIP_CHARACTER_PRECIS = 1,
CLIP_STROKE_PRECIS = 2,
CLIP_MASK = 0xf,
CLIP_LH_ANGLES = (1 << 4),
CLIP_TT_ALWAYS = (2 << 4),
CLIP_DFA_DISABLE = (4 << 4),
CLIP_EMBEDDED = (8 << 4),
}
public enum FontQuality : byte
{
DEFAULT_QUALITY = 0,
DRAFT_QUALITY = 1,
PROOF_QUALITY = 2,
NONANTIALIASED_QUALITY = 3,
ANTIALIASED_QUALITY = 4,
CLEARTYPE_QUALITY = 5,
CLEARTYPE_NATURAL_QUALITY = 6,
}
[Flags]
public enum FontPitchAndFamily : byte
{
DEFAULT_PITCH = 0,
FIXED_PITCH = 1,
VARIABLE_PITCH = 2,
FF_DONTCARE = (0 << 4),
FF_ROMAN = (1 << 4),
FF_SWISS = (2 << 4),
FF_MODERN = (3 << 4),
FF_SCRIPT = (4 << 4),
FF_DECORATIVE = (5 << 4),
}
[StructLayout(LayoutKind.Sequential)]
public class NEWTEXTMETRICEX
{
//public const int LF_FACESIZE = 32;
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;
}
private const byte DEFAULT_CHARSET = 1;
private const byte SHIFTJIS_CHARSET = 128;
private const byte JOHAB_CHARSET = 130;
private const byte EASTEUROPE_CHARSET = 238;
private const byte DEFAULT_PITCH = 0;
private const byte FIXED_PITCH = 1;
private const byte VARIABLE_PITCH = 2;
private const byte FF_DONTCARE = (0 << 4);
private const byte FF_ROMAN = (1 << 4);
private const byte FF_SWISS = (2 << 4);
private const byte FF_MODERN = (3 << 4);
private const byte FF_SCRIPT = (4 << 4);
private const byte FF_DECORATIVE = (5 << 4);
public TestForm()
{
InitializeComponent();
}
private int cnt = 0;
private void TestForm_Load(object sender, EventArgs e)
{
LOGFONT lf = CreateLogFont("");
IntPtr plogFont = Marshal.AllocHGlobal(Marshal.SizeOf(lf));
Marshal.StructureToPtr(lf, plogFont, true);
int ret = 0;
try
{
Graphics G = pictureBox1.CreateGraphics();
IntPtr P = G.GetHdc();
del1 = new EnumFontExDelegate(callback1);
ret = EnumFontFamiliesEx(P, plogFont, del1, IntPtr.Zero, 0);
System.Diagnostics.Trace.WriteLine("EnumFontFamiliesEx = " + ret.ToString());
}
catch
{
System.Diagnostics.Trace.WriteLine("Exception");
}
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct ENUMLOGFONTEX
{
public LOGFONT elfLogFont;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string elfFullName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string elfStyle;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string elfScript;
}
public delegate int EnumFontExDelegate(ref ENUMLOGFONTEX lpelfe, IntPtr lpntme, int FontType, int lParam);
public EnumFontExDelegate del1;
public int callback1(ref ENUMLOGFONTEX lpelfe, IntPtr lpntme, int FontType, int lParam)
{
try
{
++cnt;
Font F = Font.FromLogFont(lpelfe.elfLogFont);
Console.WriteLine(F.Name + " " + lpelfe.elfFullName);
}
catch
{
System.Diagnostics.Trace.WriteLine("What happened?");
}
return cnt;
}
public static LOGFONT CreateLogFont(string fontname)
{
LOGFONT lf = new LOGFONT();
lf.lfHeight = 0;
lf.lfWidth = 0;
lf.lfEscapement = 0;
lf.lfOrientation = 0;
lf.lfWeight = 0;
lf.lfItalic = false;
lf.lfUnderline = false;
lf.lfStrikeOut = false;
lf.lfCharSet = FontCharSet.DEFAULT_CHARSET;
lf.lfOutPrecision = 0;
lf.lfClipPrecision = 0;
lf.lfQuality = 0;
lf.lfPitchAndFamily = FontPitchAndFamily.FF_DONTCARE;
lf.lfFaceName = "";
return lf;
}
}
}
My ultimate goal is to list all active fonts on a windows computer. For example, if you start WordPad or Word, you will see them all. Some of these fonts comes from the windows\fonts folder, but not all. Some are registered dynamically using the gdi32.dll call AddFontResource(...). Now, you would think C# had support for retrieving this entire list, and if so, let me know, please!
However, after spending many hours on this, the regular C# calls to get installed system fonts retrieve just some of all the fonts that are actually active. InstalledFontCollection.Families, for example, does NOT find any of the fonts added with AddFontResource (It finds about 25% of the fonts listed in wordpad !)
While I want to do this in C#, it appears I have to use some more gdi32.dll calls. Thus started my EnumFontFamiliesEx issue.
I have managed to get the following code working to the point that I am getting a bunch of callbacks with fonts. However, the structure ENUMLOGFONTEX seems to be corrupted, and for example lpelfe.elfFullName usually contains a couple of square characters, never a real name. Maybe I have made a mistake in the marshalling, or the struct definition, not really sure. I've tried many iterations and endless googling, but to no avail.
Any help would be very much appreciated.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Windows.Media;
using System.Runtime.InteropServices;
using Microsoft.Win32;
namespace FontRetriever
{
public partial class TestForm : Form
{
[DllImport("gdi32.dll")]
static extern int EnumFontFamiliesEx(IntPtr hdc,
[In] IntPtr pLogfont,
EnumFontExDelegate lpEnumFontFamExProc,
IntPtr lParam,
uint dwFlags);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public class LOGFONT
{
public int lfHeight;
public int lfWidth;
public int lfEscapement;
public int lfOrientation;
public FontWeight lfWeight;
[MarshalAs(UnmanagedType.U1)]
public bool lfItalic;
[MarshalAs(UnmanagedType.U1)]
public bool lfUnderline;
[MarshalAs(UnmanagedType.U1)]
public bool lfStrikeOut;
public FontCharSet lfCharSet;
public FontPrecision lfOutPrecision;
public FontClipPrecision lfClipPrecision;
public FontQuality lfQuality;
public FontPitchAndFamily lfPitchAndFamily;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string lfFaceName;
}
public enum FontWeight : int
{
FW_DONTCARE = 0,
FW_THIN = 100,
FW_EXTRALIGHT = 200,
FW_LIGHT = 300,
FW_NORMAL = 400,
FW_MEDIUM = 500,
FW_SEMIBOLD = 600,
FW_BOLD = 700,
FW_EXTRABOLD = 800,
FW_HEAVY = 900,
}
public enum FontCharSet : byte
{
ANSI_CHARSET = 0,
DEFAULT_CHARSET = 1,
SYMBOL_CHARSET = 2,
SHIFTJIS_CHARSET = 128,
HANGEUL_CHARSET = 129,
HANGUL_CHARSET = 129,
GB2312_CHARSET = 134,
CHINESEBIG5_CHARSET = 136,
OEM_CHARSET = 255,
JOHAB_CHARSET = 130,
HEBREW_CHARSET = 177,
ARABIC_CHARSET = 178,
GREEK_CHARSET = 161,
TURKISH_CHARSET = 162,
VIETNAMESE_CHARSET = 163,
THAI_CHARSET = 222,
EASTEUROPE_CHARSET = 238,
RUSSIAN_CHARSET = 204,
MAC_CHARSET = 77,
BALTIC_CHARSET = 186,
}
public enum FontPrecision : byte
{
OUT_DEFAULT_PRECIS = 0,
OUT_STRING_PRECIS = 1,
OUT_CHARACTER_PRECIS = 2,
OUT_STROKE_PRECIS = 3,
OUT_TT_PRECIS = 4,
OUT_DEVICE_PRECIS = 5,
OUT_RASTER_PRECIS = 6,
OUT_TT_ONLY_PRECIS = 7,
OUT_OUTLINE_PRECIS = 8,
OUT_SCREEN_OUTLINE_PRECIS = 9,
OUT_PS_ONLY_PRECIS = 10,
}
public enum FontClipPrecision : byte
{
CLIP_DEFAULT_PRECIS = 0,
CLIP_CHARACTER_PRECIS = 1,
CLIP_STROKE_PRECIS = 2,
CLIP_MASK = 0xf,
CLIP_LH_ANGLES = (1 << 4),
CLIP_TT_ALWAYS = (2 << 4),
CLIP_DFA_DISABLE = (4 << 4),
CLIP_EMBEDDED = (8 << 4),
}
public enum FontQuality : byte
{
DEFAULT_QUALITY = 0,
DRAFT_QUALITY = 1,
PROOF_QUALITY = 2,
NONANTIALIASED_QUALITY = 3,
ANTIALIASED_QUALITY = 4,
CLEARTYPE_QUALITY = 5,
CLEARTYPE_NATURAL_QUALITY = 6,
}
[Flags]
public enum FontPitchAndFamily : byte
{
DEFAULT_PITCH = 0,
FIXED_PITCH = 1,
VARIABLE_PITCH = 2,
FF_DONTCARE = (0 << 4),
FF_ROMAN = (1 << 4),
FF_SWISS = (2 << 4),
FF_MODERN = (3 << 4),
FF_SCRIPT = (4 << 4),
FF_DECORATIVE = (5 << 4),
}
[StructLayout(LayoutKind.Sequential)]
public class NEWTEXTMETRICEX
{
//public const int LF_FACESIZE = 32;
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;
}
private const byte DEFAULT_CHARSET = 1;
private const byte SHIFTJIS_CHARSET = 128;
private const byte JOHAB_CHARSET = 130;
private const byte EASTEUROPE_CHARSET = 238;
private const byte DEFAULT_PITCH = 0;
private const byte FIXED_PITCH = 1;
private const byte VARIABLE_PITCH = 2;
private const byte FF_DONTCARE = (0 << 4);
private const byte FF_ROMAN = (1 << 4);
private const byte FF_SWISS = (2 << 4);
private const byte FF_MODERN = (3 << 4);
private const byte FF_SCRIPT = (4 << 4);
private const byte FF_DECORATIVE = (5 << 4);
public TestForm()
{
InitializeComponent();
}
private int cnt = 0;
private void TestForm_Load(object sender, EventArgs e)
{
LOGFONT lf = CreateLogFont("");
IntPtr plogFont = Marshal.AllocHGlobal(Marshal.SizeOf(lf));
Marshal.StructureToPtr(lf, plogFont, true);
int ret = 0;
try
{
Graphics G = pictureBox1.CreateGraphics();
IntPtr P = G.GetHdc();
del1 = new EnumFontExDelegate(callback1);
ret = EnumFontFamiliesEx(P, plogFont, del1, IntPtr.Zero, 0);
System.Diagnostics.Trace.WriteLine("EnumFontFamiliesEx = " + ret.ToString());
}
catch
{
System.Diagnostics.Trace.WriteLine("Exception");
}
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct ENUMLOGFONTEX
{
public LOGFONT elfLogFont;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string elfFullName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string elfStyle;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string elfScript;
}
public delegate int EnumFontExDelegate(ref ENUMLOGFONTEX lpelfe, IntPtr lpntme, int FontType, int lParam);
public EnumFontExDelegate del1;
public int callback1(ref ENUMLOGFONTEX lpelfe, IntPtr lpntme, int FontType, int lParam)
{
try
{
++cnt;
Font F = Font.FromLogFont(lpelfe.elfLogFont);
Console.WriteLine(F.Name + " " + lpelfe.elfFullName);
}
catch
{
System.Diagnostics.Trace.WriteLine("What happened?");
}
return cnt;
}
public static LOGFONT CreateLogFont(string fontname)
{
LOGFONT lf = new LOGFONT();
lf.lfHeight = 0;
lf.lfWidth = 0;
lf.lfEscapement = 0;
lf.lfOrientation = 0;
lf.lfWeight = 0;
lf.lfItalic = false;
lf.lfUnderline = false;
lf.lfStrikeOut = false;
lf.lfCharSet = FontCharSet.DEFAULT_CHARSET;
lf.lfOutPrecision = 0;
lf.lfClipPrecision = 0;
lf.lfQuality = 0;
lf.lfPitchAndFamily = FontPitchAndFamily.FF_DONTCARE;
lf.lfFaceName = "";
return lf;
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
感谢马蒂亚斯的回答。事实证明我遇到了一些问题,我也从其他论坛得到了一些答案,这有助于解决这些问题。所以这是最终的代码供大家欣赏。
Thanks for the answer Mattias. It turned out I had a couple of issues, and I got some answers from other forums as well that helped resolve them. So here is the final code for all to enjoy.
将 CharSet=CharSet.Auto 添加到 EnumFontFamiliesEx 上的 DllImport 属性应该可以解决该问题。就像 Merlyn 暗示的那样,这是一个 ANSI/Unicode 不匹配问题。
并且不要忘记释放使用 Marshal.AllocHGlobal 分配的内存。
Adding CharSet=CharSet.Auto to your DllImport attribute on EnumFontFamiliesEx should take care of the problem. Like Merlyn hints at, it's a ANSI/Unicode mismatch problem.
And don't forget to free the memory you allocate with Marshal.AllocHGlobal.