字体枚举选择函数-汉化技术论坛-galgame

字体枚举选择函数

图片[1]-字体枚举选择函数-汉化技术论坛-galgame

 

函数介绍:

EnumFontFamiliesEx 是 Windows GDI (图形设备接口) 中的一个函数,用于枚举系统中可用的字体族。

其中带W的这个函数是 EnumFontFamiliesEx 函数的 Unicode 版本。

如果是带A的话是对应的 ANSI 版本。

下面是对这个函数的详细介绍:

函数源代码如下:

int EnumFontFamiliesExW(
  HDC           hdc,
  LPLOGFONTW    lpLogfont,
  FONTENUMPROCW lpProc,
  LPARAM        lParam,
  DWORD         dwFlags
);

 

参数说明:

  1. hdc: 设备上下文的句柄。
  2. pLogfont: 指向 LOGFONTW 结构的指针,用于指定要枚举的字体的特征。可以设置为 NULL 以枚举所有字体。
  3. lpProc: 回调函数的指针,系统会为每个匹配的字体族调用这个函数。
  4. lParam: 传递给回调函数的应用程序定义的数据。
  5. dwFlags: 附加选项的标志。通常设置为 0。

 

功能:

– 这个函数会枚举系统中的字体族,对于每个匹配的字体族,它都会调用指定的回调函数。

– 可以通过 lpLogfont 参数来筛选特定的字体特征,如字体名称、字符集、字体粗细等。

– 回调函数可以收集字体信息,决定是否继续枚举,或者执行其他自定义操作。

使用场景:

  • 获取系统中所有可用的字体列表。
  • 查找具有特定特征的字体(如特定字符集的字体)。
  • 验证某个特定字体是否在系统中可用。
  • 在应用程序中创建字体选择器或字体管理功能。

示例代码片段:

BOOL CALLBACK EnumFontFamiliesExProc(
    ENUMLOGFONTEXA *lpelfe,
    NEWTEXTMETRICA *lpntme,
    DWORD FontType,
    LPARAM lParam)
{
    // 处理字体信息
    printf("Font Name: %s\n", lpelfe->elfLogFont.lfFaceName);
    return TRUE; // 继续枚举
}

// 在主函数中
HDC hdc = GetDC(NULL);
LOGFONTA lf = {0};
lf.lfCharSet = DEFAULT_CHARSET;
EnumFontFamiliesExA(hdc, &lf, (FONTENUMPROCA)EnumFontFamiliesExProc, 0, 0);
ReleaseDC(NULL, hdc);

 

 

A和W的主要区别:

  1. 参数类型:

    • lpLogfont 的类型是 LPLOGFONTA(而不是 LPLOGFONTW)
    • lpProc 的类型是 FONTENUMPROCA(而不是 FONTENUMPROCW)
  2. 字符编码:

    • EnumFontFamiliesExA 使用 ANSI 字符编码
    • EnumFontFamiliesExW 使用 Unicode 字符编码
  3. 结构体:

    • 使用 ENUMLOGFONTEXA 而不是 ENUMLOGFONTEXW
    • 使用 NEWTEXTMETRICA 而不是 NEWTEXTMETRICW

 

 

注意事项:

– 使用完 HDC 后,记得调用 ReleaseDC 释放它。
– 回调函数应该尽可能快地执行,以避免影响性能。
– W这个函数是 Unicode 版本,对应的 ANSI 版本是 EnumFontFamiliesExA。

通过使用 gdi32.EnumFontFamiliesExW,程序可以灵活地管理和使用系统中的字体资源,增强应用程序的字体处理能力。

 

实际运用:

在旮旯游戏程序里,大多数都是EnumFontFamiliesExA,这里我们需要让他枚举中文的字体,所以我们的关注的点在lf.lfCharSet这个参数。

这是 `LOGFONT` 结构中的一个重要成员,用于指定字体的字符集。

 

`lf.CharSet` 的定义:

在 `LOGFONT` 结构中,`lfCharSet` 被定义为 `BYTE` 类型(8位无符号整数)。它用于指定字体的字符集或代码页。

CharSet的主要用途:

  1.  筛选字体:在调用 `EnumFontFamiliesEx` 函数时,可以使用 `lfCharSet` 来筛选特定字符集的字体。
  2.  创建字体:当使用 `CreateFont` 或 `CreateFontIndirect` 创建字体时,`lfCharSet` 用于指定所需的字符集。

常用的 `lfCharSet` 值

  •  `ANSI_CHARSET` (0): 表示 ANSI 字符集,主要用于英语和西欧语言。
  •  `DEFAULT_CHARSET` (1): 默认字符集,让系统选择最合适的字符集。也就是你电脑是什么语言就选什么。(也有时候会根据脚本和游戏的默认,选择日语)
  •  `SYMBOL_CHARSET` (2): 用于符号字体。
  •  `SHIFTJIS_CHARSET` (128): 日语字符集。
  •  `HANGEUL_CHARSET` (129): 韩语字符集。
  •  `GB2312_CHARSET` (134): 简体中文字符集。
  •  `CHINESEBIG5_CHARSET` (136): 繁体中文字符集。
  • -`OEM_CHARSET` (255): OEM 字符集,通常用于控制台应用程序。

在 `EnumFontFamiliesEx` 中的使用:

  • 1. 枚举所有字符集的字体:
    LOGFONT lf = {0};
    lf.lfCharSet = DEFAULT_CHARSET;
  • 2. 枚举特定字符集的字体(例如,简体中文):
    LOGFONT lf = {0};
    lf.lfCharSet = GB2312_CHARSET;

重要说明:

1. 灵活性:使用 `DEFAULT_CHARSET` 可以枚举所有可用的字体,不限于特定字符集。

2. 性能考虑:指定特定的字符集可以减少枚举的字体数量,提高性能。

3. 字体匹配:当创建字体时,如果指定的字符集在请求的字体中不可用,Windows 会尝试找到最接近的匹配。

4. Unicode 和字符集:即使在使用 Unicode 函数(如 `EnumFontFamiliesExW`)时,`lfCharSet` 仍然很有用,因为它可以帮助筛选支持特定语言或符号集的字体。(但是,不一定做决定)

5. 系统依赖:可用的字符集可能因 Windows 版本和安装的语言包而异。(如果你系统没有这个对应的语言族的字体,那么他就枚举不出来的噢)

6. 字体替换:在某些情况下,Windows 可能会使用字体替换来显示不在指定字符集中的字符。(典型的就是游戏在使用默认的MS GOTHIC[MSゴシック]来显示中文的时候会出现粗细不一致的情况。

HOOK例子:

#include <windows.h>
#include <detours.h>
#include <stdio.h>

// 原始 CreateFontIndirectA 函数指针
static HFONT (WINAPI *TrueCreateFontIndirectA)(CONST LOGFONTA *lplf) = CreateFontIndirectA;

// Hook 后的 CreateFontIndirectA 函数
HFONT WINAPI HookedCreateFontIndirectA(CONST LOGFONTA *lplf)
{
    LOGFONTA modifiedLf = *lplf;  // 复制原始 LOGFONTA 结构

    // 修改字体名称为"宋体"
    strcpy_s(modifiedLf.lfFaceName, LF_FACESIZE, "宋体");

    // 设置字符集为 GB2312_CHARSET
    modifiedLf.lfCharSet = GB2312_CHARSET;

    // 使用修改后的 LOGFONTA 调用原始函数
    return TrueCreateFontIndirectA(&modifiedLf);
}

BOOL WINAPI DllMain(HINSTANCE hinst, DWORD dwReason, LPVOID reserved)
{
    if (DetourIsHelperProcess()) {
        return TRUE;
    }

    if (dwReason == DLL_PROCESS_ATTACH) {
        DetourRestoreAfterWith();

        DetourTransactionBegin();
        DetourUpdateThread(GetCurrentThread());
        DetourAttach(&(PVOID&)TrueCreateFontIndirectA, HookedCreateFontIndirectA);
        LONG error = DetourTransactionCommit();

        if (error == NO_ERROR) {
            OutputDebugStringA("CreateFontIndirectA successfully hooked");
        } else {
            OutputDebugStringA("Error hooking CreateFontIndirectA");
        }
    } 
    else if (dwReason == DLL_PROCESS_DETACH) {
        DetourTransactionBegin();
        DetourUpdateThread(GetCurrentThread());
        DetourDetach(&(PVOID&)TrueCreateFontIndirectA, HookedCreateFontIndirectA);
        DetourTransactionCommit();
    }
    return TRUE;
}

 

 

请登录后发表评论

    没有回复内容

随便看看