如何以编程方式将控制台字体设置为 Lucida?

Posted

技术标签:

【中文标题】如何以编程方式将控制台字体设置为 Lucida?【英文标题】:How do I programatically set console font to Lucida? 【发布时间】:2017-02-17 22:55:27 【问题描述】:

Lucida Console 是 Windows 7 上预装的 TTF 字体,我想以编程方式在控制台应用程序中设置它。

    出于某种原因,SetCurrentConsoleFontEx“甚至没有在此范围内声明”。我#include <windows.h>应该在哪里定义。

    我会在CONSOLE_FONT_INFOEX 中添加什么内容?

    @Alf 建议的#define _WIN32_WINNT 0x0601 无效

右键单击控制台标题并在那里手动选择字体很容易,但我宁愿在代码中这样做。

它应该在 Windows XP+ 上运行,我在 Windows 7 上使用 MinGW g++ 4.8.1。

【问题讨论】:

根据您的编译器,您可能需要define _WIN32_WINNT before including <windows.h>。您要使用的功能似乎不适用于 Windows XP,需要 Windows server 2008。 在 Windows XP 中,我认为您可以通过修改控制台窗口的信息以繁琐的方式执行此操作。我记得该位置特定于窗口的启动方式。如果来自快捷方式,则存储在快捷方式中,否则存储在注册表中。 FWIW SetcurrentConsoleFontEx 应该是 SetCurrentConsoleFontEx。如果你在一个地方弄错了...... 我认为不可能在 XP 上以编程方式执行此操作。你会接受只有 Vista+ 的解决方案吗? @Anders:是的。我也很感激问题中的 #1:我搜索了 wincon.h,只有 CONSOLE_FONT_INFO 结构(没有 EX),没有关于字体的功能。 【参考方案1】:

如果SetCurrentConsoleFontExCONSOLE_FONT_INFOEX 即使在设置_WIN32_WINNT 后仍不存在,那么您使用的是过时的SDK。 MinGW 并不少见,因为它使用非官方的 3rd-party 头文件。

在 Windows 上处理控制台字体是有问题的,因为控制台将其字体存储在一个内部数组中。您可能还必须使用一些未记录的函数。

在 Vista 和更高版本上,只需调用 SetCurrentConsoleFontEx。问题当然是文档非常糟糕,CONSOLE_FONT_INFOEX 结构用于 Set 和 Get 函数,而没有告诉您使用了哪些成员。

设置时似乎只需要设置cbSizeFaceName,其他都可以为零:

HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_FONT_INFOEX cfie;
ZeroMemory(&cfie, sizeof(cfie));
cfie.cbSize = sizeof(cfie);
lstrcpyW(cfie.FaceName, L"Lucida Console");
SetCurrentConsoleFontEx(hStdOut, false, &cfie);

如果要设置特定的字体大小,可以设置dwFontSize.Y。请注意FontFamily,如果您将其设置为错误的值,Windows 将恢复为默认终端字体。

在 Vista 之前的系统上,您可以在字体数组中访问的唯一内容是大小:

#if 1 // Using old SDK?
typedef struct _CONSOLE_FONT_INFOEX 
ULONG cbSize;
DWORD nFont;
COORD dwFontSize;
UINT FontFamily;
UINT FontWeight;
WCHAR FaceName[LF_FACESIZE];
 CONSOLE_FONT_INFOEX, *PCONSOLE_FONT_INFOEX;
typedef BOOL (WINAPI*SETCURRENTCONSOLEFONTEX)(HANDLE hConsoleOutput,BOOL bMaximumWindow,CONSOLE_FONT_INFOEX*lpConsoleCurrentFontEx);
SETCURRENTCONSOLEFONTEX SetCurrentConsoleFontEx = (SETCURRENTCONSOLEFONTEX) GetProcAddress(LoadLibraryA("KERNEL32"), "SetCurrentConsoleFontEx");
typedef BOOL (WINAPI*GETCURRENTCONSOLEFONTEX)(HANDLE hConsoleOutput,BOOL bMaximumWindow,CONSOLE_FONT_INFOEX*lpConsoleCurrentFontEx);
GETCURRENTCONSOLEFONTEX GetCurrentConsoleFontEx = (GETCURRENTCONSOLEFONTEX) GetProcAddress(LoadLibraryA("KERNEL32"), "GetCurrentConsoleFontEx");
#endif

static DWORD PrintFontInfoNT4(HANDLE hCon)

    CONSOLE_FONT_INFO cfi;
    BOOL succ = GetCurrentConsoleFont(hCon, false, &cfi);
    printf("Get succ=%d nFont=%u dwFontSize=%dx%d\n", succ, cfi.nFont, cfi.dwFontSize.X, cfi.dwFontSize.Y);
    return succ ? cfi.nFont : -1;


static DWORD PrintFontInfoNT6(HANDLE hCon)

    CONSOLE_FONT_INFOEX cfie;
    ZeroMemory(&cfie, sizeof(cfie));
    cfie.cbSize = sizeof(cfie);
    BOOL succ = GetCurrentConsoleFontEx(hCon, false, &cfie);
    printf("GetEx succ=%d nFont=%u size=%dx%d fam=%#x wei=%u name=%ls\n", succ, cfie.nFont, cfie.dwFontSize.X, cfie.dwFontSize.Y, cfie.FontFamily, cfie.FontWeight, cfie.FaceName);
    return succ ? cfie.nFont : -1;


static void TestNT4()

    HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
    typedef DWORD (WINAPI*GETNUMBEROFCONSOLEFONTS)();
    GETNUMBEROFCONSOLEFONTS GetNumberOfConsoleFonts = (GETNUMBEROFCONSOLEFONTS) GetProcAddress(LoadLibraryA("KERNEL32"), "GetNumberOfConsoleFonts");
    typedef BOOL (WINAPI*SETCONSOLEFONT)(HANDLE hConOut, DWORD nFont);
    SETCONSOLEFONT SetConsoleFont = (SETCONSOLEFONT) GetProcAddress(LoadLibraryA("KERNEL32"), "SetConsoleFont");

    // This is the best you can do on NT/2000/XP/2003 without hacks
    DWORD orgFont = PrintFontInfoNT4(hStdOut);
    printf("GetNumberOfConsoleFonts=%u orgFont=%u\n", GetNumberOfConsoleFonts(), orgFont);
    for (DWORD i = 0, c = GetNumberOfConsoleFonts(); i < c; ++i)
    
        SetConsoleFont(hStdOut, i);
        PrintFontInfoNT4(hStdOut);
#if _WIN32_WINNT >= 0x0600
        PrintFontInfoNT6(hStdOut);
#endif
        Sleep(1000);
    
    SetConsoleFont(hStdOut, orgFont); // Restore the original font

这没什么用,因为你无法分辨你设置的是哪种字体。如果您仍然坚持在 Vista 之前的系统上设置字体,您就不得不动手了。首先,您需要决定要将更改应用到哪些终端窗口。

终端默认值存储在HKEY_CURRENT_USER\Console 下,每个应用程序的设置可以存储在子键中。如果应用程序是通过快捷方式启动的,则这些默认值可以是 overridden。

如果您只想更改正在运行的控制台,那么事情会变得更加困难,但 Windows 本身当然知道如何直接设置字体。当您在控制台菜单中选择“属性”并应用字体更改时,您可以看到这一点。这可能会因版本而异,但我相信它使用映射内存和秘密消息。您可以通过在WinDbg 中设置断点来开始您的调查;正确设置符号后,键入bp console!Write* TabEnter(如果有多个写入函数,请在所有写入函数上设置它)。您必须弄清楚它正在使用的结构的布局,因此您可能必须同时应用一个控制台设置并转储/比较内存。

编辑:

看起来有些人已经想通了。 This bug analysis paper 有一个结构定义,我也有found a SetConsolePalette function,它实现了映射内存技巧。 ReactOS 可能不会使用完全相同的实现,但您也可以take a look at it。

您仍然需要在具有 SetCurrentConsoleFontEx 的系统上调用它,因为当 Windows 开始使用 conhost.exe 时内部结构可能发生了变化。

【讨论】:

明确说明可能是个好主意,Vista 之前的版本依赖于未记录的实现细节,这些细节可能会在没有事先通知的情况下发生变化。

以上是关于如何以编程方式将控制台字体设置为 Lucida?的主要内容,如果未能解决你的问题,请参考以下文章

以编程方式将 UILabel 字体设置为用户定义的运行时属性

修改过的Lucida Sans typewriter 字体,另推荐两个不错的编程字体

如何以编程方式设置 wC hR 字体大小?

以编程方式更改 PowerShell 控制台字体

以编程方式将 CFF 字体转换为 OpenType 字体

以编程方式将CFF字体转换为OpenType字体