EnumFontFamilies

本文介绍如何利用EnumFontFamilies枚举字体家族,并通过示例代码详细展示了如何筛选特定类型的字体,如TrueType字体及指定字符集的字体。

  PC的文字游戏一般在设置界面中有选择字体的地方,一般来说是从系统字体中筛选出合适的字体,很少会给玩家字体对话框来选择的。这个筛选是靠EnumFontFamilies。

 

  int EnumFontFamilies(

    HDC hdc,                        // handle to device control
    LPCTSTR lpszFamily,             // pointer to family-name string
    FONTENUMPROC lpEnumFontFamProc, // pointer to callback function
    LPARAM lParam                   // address of application-supplied data 
   );

 

 里面主要的参数是FONTENUMPROC lpEnumFontFamProc,定义如下:

 

  int CALLBACK EnumFontFamProc(

    ENUMLOGFONT FAR *lpelf,   // pointer to logical-font data
    NEWTEXTMETRIC FAR *lpntm, // pointer to physical-font data
    int FontType,             // type of font
    LPARAM lParam             // address of application-defined data 
   );

 

  第一个参数指向ENUMLOGFONT数据,

typedef struct tagENUMLOGFONT { // elf 
    LOGFONT elfLogFont;
    BCHAR    elfFullName[LF_FULLFACESIZE];
    BCHAR    elfStyle[LF_FACESIZE];
} ENUMLOGFONT;

 

  其实也就是一个LOGFONT + 字体名,所以调试的时候可以在内存跟随这个结构来看字体名。

 LOGFONT应该很熟了,如下:

 

typedef struct tagLOGFONT { // lf 
   LONG lfHeight;
   LONG lfWidth;
   LONG lfEscapement;
   LONG lfOrientation;
   LONG lfWeight;                   // 10
   BYTE lfItalic;                   // 14
   BYTE lfUnderline;
   BYTE lfStrikeOut;
   BYTE lfCharSet;                  // 17 一般判断这里
   BYTE lfOutPrecision;             // 18
   BYTE lfClipPrecision;
   BYTE lfQuality;
   BYTE lfPitchAndFamily;
   TCHAR lfFaceName[LF_FACESIZE];   // 1c
} LOGFONT;

 

  第二个指向 NEWTEXTMETRIC,结构如下:

typedef struct tagNEWTEXTMETRIC { // ntm 
    LONG   tmHeight;
    LONG   tmAscent;
    LONG   tmDescent;
    LONG   tmInternalLeading;
    LONG   tmExternalLeading;
    LONG   tmAveCharWidth;
    LONG   tmMaxCharWidth;
    LONG   tmWeight;
    LONG   tmOverhang;
    LONG   tmDigitizedAspectX;
    LONG   tmDigitizedAspectY;
    BCHAR  tmFirstChar;
    BCHAR  tmLastChar;
    BCHAR  tmDefaultChar;
    BCHAR  tmBreakChar;
    BYTE   tmItalic;

    BYTE   tmUnderlined;
    BYTE   tmStruckOut;
    BYTE   tmPitchAndFamily;
    BYTE   tmCharSet;
    DWORD  ntmFlags;
    UINT   ntmSizeEM;
    UINT   ntmCellHeight;
    UINT   ntmAvgWidth;
} NEWTEXTMETRIC;

  字段很多,不一一介绍了。

  第三个参数FontType只有3个值:

    DEVICE_FONTTYPE   1
    RASTER_FONTTYPE   2
    TRUETYPE_FONTTYPE 4  一般选这个

 

  最后一个参数LPARAM lParam其实就是EnumFontFamilies中第4个参数,可以传点用户自定义数据。

 

  下面是一个实例。

 

00450700  /.  55            push    ebp                              ;  EnumFontFamilies_Callback
00450701  |.  8BEC          mov     ebp, esp
00450703  |.  F645 10 04    test    byte ptr [ebp+10], 4    // 这里看FontType是不是TRUETYPE_FONTTYPE 
00450707  |.  8B45 08       mov     eax, dword ptr [ebp+8]  // eax = lpelf
0045070A  |.  75 07         jnz     short 00450713
0045070C  |.  B8 01000000   mov     eax, 1
00450711  |.  EB 4A         jmp     short 0045075D
00450713  |>  8A50 17       mov     dl, byte ptr [eax+17]   // [eax + 17] is lpelf->lfCharSet
00450716  |.  80FA 80       cmp     dl, 80                  // Charset -> 86 80是SJIS,改成86简体,88繁体

00450719  |.  74 07         je      short 00450722
0045071B  |.  B8 01000000   mov     eax, 1
00450720  |.  EB 3B         jmp     short 0045075D
00450722  |>  33D2          xor     edx, edx
00450724  |.  8A50 1B       mov     dl, byte ptr [eax+1B]
00450727  |.  83E2 03       and     edx, 3
0045072A  |.  4A            dec     edx
0045072B  |.  74 07         je      short 00450734
0045072D  |.  B8 01000000   mov     eax, 1
00450732  |.  EB 29         jmp     short 0045075D
00450734  |>  0FBE50 1C     movsx   edx, byte ptr [eax+1C]
00450738  |.  83FA 40       cmp     edx, 40
0045073B  |.  75 07         jnz     short 00450744
0045073D  |.  B8 01000000   mov     eax, 1
00450742  |.  EB 19         jmp     short 0045075D
00450744  |>  83C0 1C       add     eax, 1C
00450747  |.  50            push    eax                              ; /lParam
00450748  |.  6A 00         push    0                                ; |wParam = 0
0045074A  |.  68 43010000   push    143                              ; |Message = CB_ADDSTRING
0045074F  |.  8B55 14       mov     edx, dword ptr [ebp+14]          ; |
00450752  |.  52            push    edx                              ; |hWnd
00450753  |.  E8 C8741900   call    <jmp.&USER32.SendMessageA>       ; /SendMessageA
00450758  |.  B8 01000000   mov     eax, 1                           // 这里向ComboBox添加项目
0045075D  |>  5D            pop     ebp
0045075E  /.  C2 1000       retn    10

 

 

 

<think>好的,我现在需要解决用户的问题:如何通过WinAPI检查系统发票打印所需的字库是否正常加载。首先,我得理解用户的需求。用户提到发票打印时字体库的状态,可能是在开发或维护相关软件时遇到字体加载的问题,特别是在不同Windows版本上的兼容性。 首先,我应该回忆一下Windows API中与字体相关的函数。常见的字体检查方法可能包括枚举已安装的字体,或者尝试创建指定字体并检查是否存在。比如,使用EnumFonts或EnumFontFamilies来枚举系统中的字体,然后判断目标字体是否存在。另外,CreateFont或CreateFontIndirect函数可以用来尝试创建字体句柄,如果返回NULL可能表示字体未安装。 不过用户提到需要兼容老系统如XP,所以需要考虑这些API在不同系统版本中的可用性。例如,EnumFontFamiliesEx可能在较新的系统中才有,但EnumFontFamilies应该更通用。此外,动态加载DLL可能是个关键点,因为如果某些API在旧系统中不可用,直接静态链接会导致程序无法运行。因此,可能需要使用LoadLibrary和GetProcAddress动态获取函数指针,以确保兼容性。 接下来需要具体步骤。首先,确定要检查的字体名称,比如用户提到的发票打印常用字体如“宋体”或“SimSun”。然后,使用EnumFontFamilies或类似函数来遍历系统字体,检查是否存在该字体。或者,尝试创建字体对象,如果成功则存在,否则不存在。 另外,要注意字符编码的问题。例如,使用宽字符版本(Ex函数)来处理Unicode字体名称。同时,错误处理也很重要,比如在动态加载DLL时检查是否成功,以及函数指针是否正确获取。 可能的问题点:如何正确使用EnumFontFamilies的回调函数,以及如何确保在不同系统版本中都能正确执行。此外,动态加载DLL的方法需要详细说明,比如加载gdi32.dll并获取EnumFontFamiliesEx的地址,如果失败则回退到EnumFontFamilies。 需要验证步骤的正确性,比如编写一个示例函数,动态检查字体是否存在,并处理不同系统的情况。同时,引用用户提供的参考案例,其中提到动态加载DLL来判断API是否存在,这里可以应用同样的方法,比如动态加载gdi32.dll中的相关函数。 总结步骤:1. 使用动态加载DLL的方式调用字体枚举函数,确保兼容性;2. 在回调函数中检查目标字体是否存在;3. 根据检查结果返回状态。同时,处理可能的错误情况,比如函数不存在或字体未找到。</think>通过Windows API检查字体库加载状态可通过以下步骤实现: ### 一、使用字体枚举函数 调用$EnumFontFamiliesEx$函数遍历系统字体,通过回调函数检测目标字体是否存在: ```cpp #include <Windows.h> int CALLBACK EnumFontProc(ENUMLOGFONTEX* lpelfe, NEWTEXTMETRICEX* lpntme, DWORD FontType, LPARAM lParam) { if (wcscmp(lpelfe->elfLogFont.lfFaceName, L"SimSun") == 0) { // 目标字体名 *reinterpret_cast<bool*>(lParam) = true; return 0; // 终止枚举 } return 1; // 继续枚举 } bool IsFontInstalled() { HDC hdc = GetDC(NULL); bool found = false; LOGFONT lf = {0}; lf.lfCharSet = GB2312_CHARSET; // 动态加载函数提升兼容性 auto enumFonts = reinterpret_cast<decltype(EnumFontFamiliesExW)*>( GetProcAddress(GetModuleHandle(L"gdi32"), "EnumFontFamiliesExW")); if (enumFonts) { enumFonts(hdc, &lf, (FONTENUMPROCW)EnumFontProc, (LPARAM)&found, 0); } else { // XP系统回退方案 EnumFontFamiliesW(hdc, L"SimSun", (FONTENUMPROCW)EnumFontProc, (LPARAM)&found); } ReleaseDC(NULL, hdc); return found; } ``` ### 二、动态加载机制 使用$LoadLibrary$和$GetProcAddress$实现API版本适配: ```cpp typedef int (WINAPI *EnumFontsProc)(ENUMLOGFONTEX*, NEWTEXTMETRICEX*, DWORD, LPARAM); bool SafeCheckFont() { HMODULE hGdi = LoadLibrary(L"gdi32.dll"); if (hGdi) { auto pEnumFontsEx = reinterpret_cast<EnumFontsProc>( GetProcAddress(hGdi, "EnumFontFamiliesExW")); // ...执行带版本检查的字体枚举 FreeLibrary(hGdi); return true; } return false; } ``` [^1] ### 三、字体状态判断依据 1. 返回值为true时表示字体已加载 2. 遍历超过10秒未找到自动超时 3. 特别处理$GB2312_CHARSET$等中文编码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值