SetCurrentConsoleFontEx не работает для длинных имен шрифтов - PullRequest
0 голосов
/ 16 сентября 2018

Я не могу заставить это работать для имен шрифтов, которые являются 16 символами или больше, но у самой консоли, очевидно, нет этого ограничения. Кто-нибудь знает программный способ установки шрифта, который будет работать со встроенной «Lucida Sans Typewriter» или с открытым исходным кодом «Fira Code Retina»?

Этот следующий код работает:

Я скопировал код PInvoke из разных мест, в частности хост консоли PowerShell и Microsoft Docs

Обратите внимание, что соответствующие документы для CONSOLE_FONT_INFOEX и SetCurrentConsoleFontEx не говорят об этом, и структура определяет грань шрифта как поле WCHAR размером 32 ...

Также обратите внимание на то, что не является ограничением, но является ограничением из диалогового окна консоли, заключается в том, что шрифт должен иметь контуры True Type и должен иметь действительно фиксированную ширину. Используя этот API, вы можете выбирать шрифты переменной ширины, такие как «Times New Roman» ...

Однако в API оно должно содержать меньше 16 символов в имени - это ограничение, которое отсутствует в самой консоли, и может быть ошибка в API, а не мой код ниже ? * ​​1025 *

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, используя Add-Type с этим кодом, а затем сделать что-то вроде этого:

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

Затем используйте диалоговое окно «Свойства» консоли и вручную переключитесь на 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 , шрифтом, который имеет ровно 16 символов в имени - и у меня немного больше кода, чем выше в Суть здесь , если вы хотите экспериментировать ...

1 Ответ

0 голосов
/ 31 октября 2018

Я обнаружил ошибку в Консольном API. Это исправлено в Windows 10 (инсайдерской) сборки 18267.

До этого выпуска не было никакого способа обойти это - кроме использования шрифтов с более короткими именами или фактической панели свойств окна, чтобы установить его.

Оригинальный почтовый индекс работает сейчас ...

...