Delphi High DPI переключает между собственным масштабированием и масштабированием Windows

Некоторые из моих клиентов хотят иметь возможность масштабировать мое приложение вручную (когда для Windows установлено значение 96 dpi), поэтому мне пришлось реализовать масштабирование. К сожалению, эти клиенты не могут пойти с настройкой Windows DPI на другое значение и позволить WIndows масштабировать мое приложение, потому что некоторые очень важные приложения, которые они используют, вообще не работают при разрешениях ‹> 96 DPI.

Мне удалось сделать мое приложение Delphi 10.1 достаточно хорошо масштабируемым даже на 200%, но чем выше становится коэффициент, тем больше некоторые пропорции становятся «не такими красивыми». Многие сторонние компоненты нуждаются в особой обработке для масштабирования, и даже в этом случае масштабирование не выполняется с точностью до 100%. Хотя приложения, масштабируемые окнами, выглядят немного размытыми при высоких разрешениях, все пропорции точны на 100%, и, по-моему, приложение выглядит намного более профессиональным.

Поэтому я спросил себя, можно ли создать параметр, который позволяет Windows выполнять масштабирование по умолчанию и масштабировать только самостоятельно, если клиент хочет масштабирование, отличное от текущего масштабирования Windows. Этот параметр размещен в манифесте исполняемого файла Windows, который читается при запуске приложения. Есть ли способ изменить его во время выполнения (ранний запуск приложения)? Создание двух исполняемых файлов с разными манифестами - определенно не лучшее решение.

Спасибо за любую помощь


person MichaSchumann    schedule 19.11.2016    source источник
comment
Вы упоминаете настройку <dpiAware>? Если да, см. Примечания в SetProcessDpiAwareness. .   -  person Sertac Akyuz    schedule 19.11.2016
comment
Спасибо, я попробую!   -  person MichaSchumann    schedule 19.11.2016
comment
Почему мой вопрос был отклонен?   -  person MichaSchumann    schedule 19.11.2016
comment
Вероятно, по той же причине, что и существующее закрытое голосование: непонятно, о чем вы спрашиваете. Я тоже не был уверен в том, что предлагаю ссылку, которую дал, но рискнул.   -  person Sertac Akyuz    schedule 19.11.2016
comment
@MichaSchumann Не беспокойтесь о голосовании против. Люди часто чувствуют, что им нужно что-то сделать, но не знают, что, и голосуют против, голосуют за закрытие по необоснованным причинам. В любом случае, спасибо, что поделились ответом. Очень полезно   -  person John Kouraklis    schedule 19.11.2016


Ответы (1)


Благодаря Sertac Akyuz я нашел решение своей проблемы. В части инициализации модуля, содержащего код масштабирования, я могу переключаться между 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 во всех последующих вычислениях масштабирования, и оно отлично работает.

person MichaSchumann    schedule 19.11.2016
comment
Подумав еще раз, я пришел к выводу, что мой подход намного сложнее, чем необходимо. Я думаю, что могу настроить приложение без поддержки dpi и масштабировать сверху, если пользователь этого потребует. Это позволит избежать высоких коэффициентов масштабирования на таких устройствах, как Surface или мои Lenovo 900, если я масштабирую на 140%, а устройству требуется еще 200%, что дает 280% на e. г. Surface pro. - person MichaSchumann; 19.11.2016
comment
Мое решение может помочь кому-то, кто ищет возможность переключаться между без учета dpi и с поддержкой dpi во время выполнения. Я (= болван) упустил из виду возможность комбинировать масштабирование Windows (без поддержки dpi) и мое собственное масштабирование (например, для людей с ослабленным зрением). Теперь он работает хорошо, и пропорции прекрасны, так как мой максимальный масштаб составляет 140%. - person MichaSchumann; 21.11.2016