Delphi High DPI переключение между собственным масштабированием и масштабированием Windows
Некоторые из моих клиентов хотят иметь возможность масштабировать мое приложение вручную (когда для dpi Windows установлено значение 96), поэтому мне пришлось реализовать масштабирование. К сожалению, эти клиенты не могут настроить Windows DPI на другое значение и позволить Windows масштабировать мое приложение, потому что некоторые очень важные приложения, которые они используют, плохо работают при разрешениях <> 96 DPI.
Мне удалось довольно хорошо масштабировать приложение Delphi 10.1 даже на 200%, но чем выше коэффициент, тем больше некоторые пропорции становятся "не очень приятными". Многие компоненты сторонних производителей нуждаются в особой обработке для масштабирования, и даже в этом случае точность не достигает 100%. Хотя приложения, масштабируемые по окнам, выглядят немного размытыми при высоком разрешении, все пропорции на 100% точны, и, тем не менее, приложение выглядит гораздо более профессионально.
Поэтому я спросил себя, возможно ли создать настройку, которая позволит Windows выполнять масштабирование по умолчанию и выполнять масштабирование самостоятельно, только если клиент хочет масштабирование, отличное от текущего масштабирования Windows. Этот параметр размещается в манифесте Windows исполняемого файла, который читается при запуске приложения. Есть ли способ изменить его во время выполнения (ранний запуск приложения)? Создание двух исполняемых файлов с разными манифестами, безусловно, не является хорошим решением.
Спасибо за любую помощь
1 ответ
Благодаря Сертаку Акьюзу я нашел решение своей проблемы. В части инициализации модуля, содержащего код масштабирования, я могу переключаться между DPI-Awareness и Non-DPI-Awareness. Важно не указывать этот параметр в манифесте приложения, чего можно добиться, предоставив собственный манифест, подобный этому (используйте оформление элемента управления и запускайте с правами текущего пользователя):
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
publicKeyToken="6595b64144ccf1df"
language="*"
processorArchitecture="*"/>
</dependentAssembly>
</dependency>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel
level="asInvoker"
uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
</assembly>
Это фактическое переключение кода в зависимости от ключа реестра:
// Set DPI Awareness depending on a registry setting
with TRegIniFile.create('SOFTWARE\' + SRegName) do
begin
setting := readInteger('SETTINGS', 'scale', 0);
Free;
end;
handle := LoadLibrary('shcore.dll');
if handle <> 0 then
begin
setProcessDPIAwareness := GetProcAddress(handle, 'SetProcessDpiAwareness');
if Assigned(setProcessDPIAwareness) then
begin
if setting < 2 then
// setting <2 means no scaling vs Windows
setProcessDPIAwareness(0)
else
// setting 2: 120%, 3: 140% vs. Windows
// The actual used scaling factor multiplies by windows DPI/96
setProcessDPIAwareness(1);
end;
FreeLibrary(handle);
// Get windows scaling as Screen.PixelsPerInch was read before swiching DPI awareness
// Our scaling routines now work with WinDPI instead of Screen.PixelsPerInch
WinDPI:= Screen.MonitorFromWindow(application.handle).PixelsPerInch;
end;
Последняя строка этого фрагмента извлекает текущий DPI для текущего монитора, так как screen.pixelsperinch, кажется, инициализируется раньше и всегда возвращает 96, как для приложения, не поддерживающего dpi. Я использую значение winDPI во всех последующих вычислениях масштабирования, и он отлично работает.