В своём консольном проекте на C# я решил реализовать возможность смены шрифта консоли средствами самой программы. Алгоритм действий:
Проблема в том, что GetNumberOfConsoleFonts() постоянно возвращает 0, делая все другие шаги бессмысленными. В чём может быть причина? Можно ли как-нибудь иначе получить количество доступных для консоли шрифтов или их индексы?
[DllImport("kernel32.dll", SetLastError = true)]
static extern uint GetNumberOfConsoleFonts();
//Использование:
uint fontsCount = GetNumberOfConsoleFonts();
У меня установлены Windows 10 и Visual Studio 2019.
Дополнение от 26.08.2019:
В общем, я так и не нашёл способа заставить функции GetNumberOfConsoleFonts, GetConsoleFontInfo и SetConsoleFont заработать. Пока вместо этого использую следующий алгоритм:
HKEY_CURRENT_USER\Software\Microsoft\Shared
Tools\Panose (похоже, содержит имена и некоторые параметры шрифтов)
получаю все пары Имя:Значение.SetCurrentConsoleFontEx и GetCurrentConsoleFontEx, чтобы проверить может ли данный шрифт использоваться в консоли (если свойство FaceName структуры CONSOLE_FONT_INFO_EX что передаётся в SetCurrentConsoleFontEx совпадает с свойством FaceName структуры CONSOLE_FONT_INFO_EX что возвращается GetCurrentConsoleFontEx, значит консоль может использовать этот шрифт).В Windows 10 Microsoft внесли много улучшений в консоль. Это связано главным образом не с WinRT, а с появлением .NET Core и Windows Subsystem for Linux, которые оживили интерес к *NIX-софту, а он по большей части является именно консольным. Одно из таких улучшений - возможность использовать в консоли любой моноширинный шрифт. Видимо, именно из-за этого недокументированная функция GetNumberOfConsoleFonts теперь возвращает 0 - никаких особых "консольных шрифтов" больше не существует, и она потеряла свой смысл.
На более ранних версиях Windows она работает, но особой необходимости в ней нет, так как список поддерживаемых шрифтов можно получить из ветки реестра HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Console\TrueTypeFont (она тоже не документированная, но на основе реестра список шрифтов построить вроде попроще, чем по предложенному алгоритму). По умолчанию, в ней только Consolas и Lucida Console. В нее можно добавить и другие моноширинные шрифты, удовлетворяющие данным условиям - и они в какой-то мере будут работать - но это, скорее всего, плохая идея (см. Why are console windows limited to Lucida Console and raster fonts?).
На Windows 10 же список поддерживаемых консолью шрифтов - это просто список всех моноширинных шрифтов. Помимо реестра, его можно получить стандартными средствами GDI/GDI+:
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Drawing.Text;
class Program
{
public static bool IsFixedPitch(Graphics graphics, FontFamily fam)
{
Font font=new Font(fam, 10);
using (font)
{
IntPtr hDC = graphics.GetHdc();
TEXTMETRIC metrics;
IntPtr hFont = font.ToHfont();
try
{
IntPtr hPreviousFont = SelectObject(hDC, hFont);
bool res = GetTextMetrics(hDC, out metrics);
if (res == false) throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
SelectObject(hDC, hPreviousFont);
}
finally
{
DeleteObject(hFont);
graphics.ReleaseHdc(hDC);
}
return (metrics.tmPitchAndFamily & TMPF_FIXED_PITCH) == 0;
}
}
[DllImport("Gdi32.dll")]
static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);
[DllImport("Gdi32.dll", SetLastError = true)]
static extern bool GetTextMetrics(IntPtr hdc, out TEXTMETRIC lptm);
[DllImport("Gdi32.dll")]
static extern bool DeleteObject(IntPtr hdc);
[StructLayout(LayoutKind.Sequential)]
internal struct TEXTMETRIC
{
public int tmHeight;
public int tmAscent;
public int tmDescent;
public int tmInternalLeading;
public int tmExternalLeading;
public int tmAveCharWidth;
public int tmMaxCharWidth;
public int tmWeight;
public int tmOverhang;
public int tmDigitizedAspectX;
public int tmDigitizedAspectY;
public char tmFirstChar;
public char tmLastChar;
public char tmDefaultChar;
public char tmBreakChar;
public byte tmItalic;
public byte tmUnderlined;
public byte tmStruckOut;
public byte tmPitchAndFamily;
public byte tmCharSet;
}
const byte TMPF_FIXED_PITCH = 0x01;
static void Main(string[] args)
{
InstalledFontCollection coll = new InstalledFontCollection();
Graphics g = Graphics.FromHwnd(IntPtr.Zero);
using (g)
{
foreach (var family in coll.Families)
{
if (IsFixedPitch(g, family))
{
Console.WriteLine(family.GetName(0));
}
}
}
Console.ReadKey();
}
}
Основные этапы разработки сайта для стоматологической клиники
Продвижение своими сайтами как стратегия роста и независимости