В своём консольном проекте на 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();
}
}
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Допустим у меня есть класс User, в классе User есть string? поле CountryПри сериализации файла создаётся
вопросик есть, как можно передать команде сразу два параметра, при этом что один из параметров состояние текущего элемента
У меня есть рабочее приложение, написанное для себя, которое перемещает файлыДля финального завершения осталось доделать, чтобы оно работало...
Требуется сохранить видео поток на жесткий диск, который транслируется на форму(WindowsForms) через библиотеку VLCИмеется ip и порт источника трансляции(Tcp)