SetCurrentConsoleFontEx 不适用于长字体名称

SetCurrentConsoleFontEx isn't working for long font names

对于 16 个字符或更长的字体名称,我无法让它工作,但控制台本身显然没有此限制。有谁知道一种编程方式来设置将与内置 "Lucida Sans Typewriter" 或开源 "Fira Code Retina" 一起使用的字体?

以下代码有效:

我从各个地方复制了 PInvoke 代码,特别是 the PowerShell console host, and the Microsoft Docs

请注意,CONSOLE_FONT_INFOEX and SetCurrentConsoleFontEx 的相关文档并未提及这一点,该结构将字体定义为大小为 32 的 WCHAR 字段...

另请注意,这不是限制,但来自控制台对话框的限制,即字体必须具有 True Type 轮廓,并且必须真正固定宽度。使用此 API 您可以选择可变宽度字体,例如 "Times New Roman" ...

但是,在 API 中,它的名称必须 少于 16 个字符 -- 这是控制台本身没有的限制, 可能 是 API 中的错误,而不是我下面的代码

using System;
using System.Runtime.InteropServices;

public static class ConsoleHelper
{
    private const int FixedWidthTrueType = 54;
    private const int StandardOutputHandle = -11;

    [DllImport("kernel32.dll", SetLastError = true)]
    internal static extern IntPtr GetStdHandle(int nStdHandle);

    [return: MarshalAs(UnmanagedType.Bool)]
    [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    internal static extern bool SetCurrentConsoleFontEx(IntPtr hConsoleOutput, bool MaximumWindow, ref FontInfo ConsoleCurrentFontEx);

    [return: MarshalAs(UnmanagedType.Bool)]
    [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    internal static extern bool GetCurrentConsoleFontEx(IntPtr hConsoleOutput, bool MaximumWindow, ref FontInfo ConsoleCurrentFontEx);


    private static readonly IntPtr ConsoleOutputHandle = GetStdHandle(StandardOutputHandle);

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct FontInfo
    {
        internal int cbSize;
        internal int FontIndex;
        internal short FontWidth;
        public short FontSize;
        public int FontFamily;
        public int FontWeight;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        //[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.wc, SizeConst = 32)]
        public string FontName;
    }

    public static FontInfo[] SetCurrentFont(string font, short fontSize = 0)
    {
        Console.WriteLine("Set Current Font: " + font);

        FontInfo before = new FontInfo
        {
            cbSize = Marshal.SizeOf<FontInfo>()
        };

        if (GetCurrentConsoleFontEx(ConsoleOutputHandle, false, ref before))
        {

            FontInfo set = new FontInfo
            {
                cbSize = Marshal.SizeOf<FontInfo>(),
                FontIndex = 0,
                FontFamily = FixedWidthTrueType,
                FontName = font,
                FontWeight = 400,
                FontSize = fontSize > 0 ? fontSize : before.FontSize
            };

            // Get some settings from current font.
            if (!SetCurrentConsoleFontEx(ConsoleOutputHandle, false, ref set))
            {
                var ex = Marshal.GetLastWin32Error();
                Console.WriteLine("Set error " + ex);
                throw new System.ComponentModel.Win32Exception(ex);
            }

            FontInfo after = new FontInfo
            {
                cbSize = Marshal.SizeOf<FontInfo>()
            };
            GetCurrentConsoleFontEx(ConsoleOutputHandle, false, ref after);

            return new[] { before, set, after };
        }
        else
        {
            var er = Marshal.GetLastWin32Error();
            Console.WriteLine("Get error " + er);
            throw new System.ComponentModel.Win32Exception(er);
        }
    }
}

您可以在 PowerShell window 中使用它,方法是使用 Add-Type 和该代码,然后执行如下操作:

[ConsoleHelper]::SetCurrentFont("Consolas", 16)
[ConsoleHelper]::SetCurrentFont("Lucida Console", 12)

然后,使用您的控制台 "Properties" 对话框并手动切换到 Lucida Sans Typewriter ... 并尝试仅更改字体大小,指定相同的字体名称:

[ConsoleHelper]::SetCurrentFont("Lucida Sans Typewriter", 12)

你会得到这样的输出(显示三个设置:之前、我们尝试的和我们得到的):

Set Current Font: Lucida Sans Typewriter

FontSize FontFamily FontWeight FontName
-------- ---------- ---------- --------
      14         54        400 Lucida Sans Typeʈ
      12         54        400 Lucida Sans Typewriter
      12         54        400 Courier New

你看到 "before" 值末尾那个奇怪的字符了吗?每当字体超过 16 个字符时就会发生这种情况(由于 API 或编组中的问题,我得到了垃圾数据)。

实际的控制台字体名称显然没有长度限制,但也许无法使用名称超过 16 个字符或更长的字体?

不管怎样,我发现了 Fira Code Retina, a font that has exactly 16 characters in the name -- and I have a little bit more code than what's above in a gist here 这个问题,如果你愿意尝试的话...

我在控制台 API 中找到了一个 bug。从 Windows 10(内部人员)build 18267 开始修复。

在那个版本之前,没有办法绕过它——除了使用名称较短的字体,或者使用实际的 window 属性面板来设置它。

原来的 post 代码现在可以工作了...