Как я могу сообщить Windows 8.1, что мое приложение Delphi хочет поддерживать разрешение на каждый монитор?

Я попытался заставить Windows 8.1 распознавать приложение Delphi XE6 (демонстрационная программа), которое я пытался создать, и заставить его распознать, что мое приложение поддерживает Per-Monitor DPI, исключительно с помощью метода манифеста. Delphi XE6 (и все другие аналогично обновленные версии Delphi) упрощают добавление манифеста внутри параметров проекта, и я это сделал.

Это содержимое .manifest, которое я определил с помощью ресурсов MSDN. Я подозреваю, что это могло быть немного неправильно.

Если вы хотите попробовать этот манифест, создайте пустое приложение VCL, используйте это содержимое в качестве своего манифеста и добавьте код (код в настоящее время прилагается к моему ответу на этот вопрос).

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" >
  <!-- Per Monitor DPI Awareness in Windows 8.1 uses asmv3:application + asmv3:windowsSettings -->
  <asmv3:application>
    <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
      <dpiAware>True</dpiAware>
    </asmv3:windowsSettings>
  </asmv3:application>

  <!-- Dear Microsoft, Don't Lie to Me About What Version of Windows I am On -->
  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
    <application>
      <!-- Windows Vista and Windows Server 2008 -->
      <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
      <!-- Windows 7 and Windows Server 2008 R2 -->
      <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
      <!-- Windows 8 and Windows Server 2012 -->
      <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
      <!-- Windows 8.1 and Windows Server 2012 R2 -->
      <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
    </application>
  </compatibility>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity
        type="win32"
        name="Microsoft.Windows.Common-Controls"
        version="6.0.0.0"
        processorArchitecture="*"
        publicKeyToken="6595b64144ccf1df"
        language="*"
        />
    </dependentAssembly>
  </dependency>
</assembly>

Кто-нибудь заставил это работать? Я обнаружил, что вышеперечисленное не распознается. Если я сначала позвоню SetProcessDPIAwareness(Process_Per_Monitor_DPI_Aware), а затем позвоню GetProcessDPIAwareness(hProc,Awareness), я верну необходимое Awareness = Process_Per_Monitor_DPI_Aware, но я прочитал, что у этого подхода есть потенциальные недостатки, и поэтому я бы предпочел рабочий подход, основанный только на манифесте.

Если я позвоню GetProcessDPIAwareness(hProc,Awareness), я вернусь `Awareness = Process_DPI_Unaware '.

Еще меня беспокоит то, что в источниках MSDN указывается добавление ДОПОЛНИТЕЛЬНОГО манифеста. Принимая во внимание, что я использую возможность IDE Delphi XE6 связывать ОДИН и ТОЛЬКО ОДИН манифест в мое приложение. Я никогда не замечал, что добавление какого-либо дополнительного манифеста вместо того, чтобы иметь только один, когда-либо было проблемой, кроме того, возможно, система управления .manifest в Visual Studio 2010 была хромой, и поэтому совет существовал, и поэтому не имеет отношения к другим IDE / Языки.

В Visual Studio 2013 есть флажок прямо внутри параметров проекта, но у меня нет Visual Studio 2013, поэтому я не могу проверить рабочий .manifest.

Обновить:

Вот еще один снимок манифеста:

<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" >
  <asmv3:application>
    <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
      <dpiAware>true</dpiAware>
    </asmv3:windowsSettings>
  </asmv3:application>
</assembly>

Приведенный выше мини-манифест изменяет поведение приложения, но не совсем так, как я хотел. С помощью приведенного выше крошечного манифеста определяется СТАРЫЙ уровень осведомленности Windows 8.0 / Windows 7 / Vista DPI.

обновление 2:

Спасибо Реми за идеи. Интересно, что следующего достаточно для запуска приложения. Однако смешивание синтаксиса SMI / 2005 с приведенным выше привело к ошибке параллельного запуска. Вы можете видеть, что Microsoft довольно много перемешивает свои манифесты. Обратите внимание, что следующее на самом деле не решает мою проблему, но предоставляет еще одну «потенциальную базовую форму», которая может быть БЛИЖАЙШЕЙ к реальному решению:

 <assembly xmlns="urn:schemas-microsoft-com:asm.v3" manifestVersion="1.0" >
  <application>
    <windowsSettings xmlns="http://schemas.microsoft.com/SMI/2011/WindowsSettings">
      <dpiAware>true</dpiAware>
    </windowsSettings>
  </application>
</assembly>

обновление 3:

КРАСНЫЙ КОД! НЕ ИСПОЛЬЗУЙТЕ следующий флаг СОВМЕСТИМОСТИ ОС в любом приложении Delphi VCL: <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>. У Microsoft есть BROKEN MOUSE CAPTURE BEHAVIOR, BROKEN WINDOW PAINTING, ужасные способы, о которых я даже не мог догадаться. Включение этого флага вызвало очень тонкие ошибки в моем приложении, в том числе проблемы с рисованием, невозможность щелкнуть по элементам управления (сообщения о нажатии мыши не доходят до элементов управления из-за потери захвата мыши) и многие другие проблемы.


person Warren P    schedule 10.11.2014    source источник
comment
Добавлены дополнительные манифесты, например через инструмент mt.exe просто объединяются в один манифест (который разрешен в приложении), не так ли? Я считаю, что вам нужно просто изменить свое приложение. проявляться, не пытаясь получить их больше.   -  person TLama    schedule 10.11.2014
comment
Хорошо, это хорошо знать. Я подозреваю, что у меня есть какие-то неясные метаданные XML / Manifest, которые немного неверны выше.   -  person Warren P    schedule 10.11.2014
comment
Я бы посоветовал изменить порядок элементов XML верхнего уровня. Сделайте <dependency> первым, затем <compatibility>, затем, наконец, <application> последним. Это порядок, в котором эти функции были представлены в Windows. XML не должен быть чувствительным к порядку, но может быть, если используется схема XML. Парсер Windows может быть чувствительным к порядку, ему может потребоваться увидеть наличие элемента <compatibility> Win8.1, прежде чем он обработает элемент <dpiAware>.   -  person Remy Lebeau    schedule 11.11.2014
comment
Кроме того, несмотря на то, что в с поддержкой DPI говорится пространство имен XML для элемента <WindowsSettings> - http://schemas.microsoft.com/SMI/2005/WindowsSettings, документация по манифестам приложений говорит, что пространство имен http://schemas.microsoft.com/SMI/2011/WindowsSettings, поэтому попробуйте оба.   -  person Remy Lebeau    schedule 11.11.2014
comment
Я добавил свой код, поэтому любой, у кого есть Windows 8.1 и Delphi XE, может попробовать. В основной форме приложения запустите этот код: uses PerMonitorApi; ... SomeLabelControl.Visible := SystemCanSupportPerMonitorDpi( False);   -  person Warren P    schedule 11.11.2014


Ответы (3)


Он задокументирован в теме MSDN Написание приложений для настольных ПК и Win32 с поддержкой DPI:

Отметьте приложение в соответствии с поддержкой монитора-DPI, изменив манифест приложения или вызвав SetProcessDpiAwarenessAPI. Мы рекомендуем использовать манифест приложения, поскольку он устанавливает уровень осведомленности о DPI при запуске приложения. Используйте API только в следующих случаях:

  • Ваш код находится в dll, которая запускается через rundll32.exe. Это механизм запуска, который не поддерживает манифест приложения.
  • Вам необходимо принимать сложные решения во время выполнения на основе версии ОС или других соображений. Например, если вам нужно, чтобы приложение распознавало системный DPI в Windows 7 и динамически распознавало в Windows 8.1, используйте настройку манифеста True / PM.

Если вы используете метод SetProcessDpiAwareness для установки уровня осведомленности о DPI, вы должны вызвать SetProcessDpiAwareness перед любым вызовом Win32API, который заставляет систему начать виртуализацию.

Значение манифеста осведомленности о DPI, Описание

False: приложение не поддерживает DPI.

True Устанавливает приложение с учетом разрешения системы.

Для каждого монитора. В Windows 8.1 устанавливает приложение, поддерживающее разрешение на каждый монитор. В Windows Vista через Windows 8 устанавливает приложение, не поддерживающее DPI.

True / PM. В Windows 8.1 устанавливает приложение с учетом разрешения каждого монитора. В Windows Vista через Windows 8 устанавливает приложение с учетом системного разрешения DPI.

Вы просто используете стандартный манифест с учетом DPI, но указываете True / PM или Per-monitor вместо True.

В той же теме приводится следующий манифест с поддержкой DPI:

<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" >
  <asmv3:application>
    <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
      <dpiAware>true</dpiAware>
    </asmv3:windowsSettings>
  </asmv3:application>
</assembly>

Итак, замените True выбранным вами значением.

person David Heffernan    schedule 10.11.2014
comment
Я принимаю этот ответ, потому что он содержит точный правильный ответ на мой вопрос. Любой, кому нужен образец кода, должен также прочитать мой ответ, когда я его поместил, чтобы люди могли увидеть, как импортировать новые shcore.dll экспортные данные и вызывать их. Спасибо, Дэвид, за то, что он намного лучше меня разбирается в MSDN. :-) - person Warren P; 11.11.2014
comment
Спасибо. Думаю, мне придется заняться тем, чтобы мое приложение было осведомлено о каждом мониторе. Это потребует некоторой работы с моим классом списка изображений ... - person David Heffernan; 11.11.2014
comment
Я обнаружил, что мое приложение, которое имеет некоторую классическую логику изменения размера формы на основе односистемного DPI, ПЛОХО взаимодействует с Windows 8.1, если вы не добавите манифест, подобный тому, который у меня здесь, или вы не отключите Per-Monitor-DPI-Awareness из на странице «Параметры отображения» на панели управления, установив флажок «Я выберу один уровень масштабирования» ... Таким образом, вы можете обнаружить, что ваше существующее приложение, не поддерживающее разрешение на каждый монитор, ведет себя странно при работе в Win 8.1. В код Windows 8.1 верхнего уровня-клиент-оконный менеджер встроены несколько чрезвычайно странных уловок обратной совместимости. - person Warren P; 11.11.2014
comment
В настоящее время вы не можете сделать свои приложения полностью осведомленными о DPI монитора в Delphi. Это будет работать для людей, использующих настройки одного монитора или использующих одинаковое масштабирование для нескольких мониторов. Формы Delphi не обрабатывают сообщения WM_DPICHANGED при перетаскивании форм с одного монитора на другой. См. quality.embarcadero.com/browse/RSP-9679. - person Dalija Prasnikar; 20.11.2014
comment
@DalijaPrasnikar В настоящее время вы даже не можете настроить DPI системы своих приложений с помощью vanilla Delphi. Вы должны сами это поддерживать. Обработка WM_DPICHANGED может быть довольно интересной. Будет задействовано масштабирование формы и ее элементов управления. - person David Heffernan; 20.11.2014
comment
@DavidHeffernan Зависит от того, что вы имеете в виду под ванильным Delphi. В общем, у вас может быть приложение с поддержкой DPI, если вы предоставили настраиваемый манифест и убедились, что все элементы управления хорошо масштабируются. В зависимости от используемых вами элементов управления у вас может быть довольно простая работа или более сложная. Но с Windows 8.1 и DPI для каждого монитора вам придется тщательно взломать основные фреймворки Delphi, чтобы заставить его работать. - person Dalija Prasnikar; 20.11.2014
comment
@Dalija Управление масштабированием - самая простая часть. Scaled это делает, хотя и не очень хорошо. Глифы панели инструментов не масштабируются в списке изображений. Вот где вам нужно начать беспорядок. - person David Heffernan; 20.11.2014
comment
@DavidHeffernan Вы должны предоставить несколько списков изображений для разных размеров и назначить правильный во время выполнения. Вы должны сделать то же самое с другими ресурсами изображений. Сколько ресурсов разного размера вы будете использовать или вам придется передискретизировать изображения во время выполнения, зависит от ваших потребностей. Обычно мне нужны только изображения 1x и 2x, а затем я уменьшаю 2x изображения во время выполнения, когда это необходимо. - person Dalija Prasnikar; 21.11.2014
comment
У меня есть ресурсы с иконками 16, 24, 32, 48 и 64 пикселей. Я никогда не меняю их размер. Если мне нужен значок 20 пикселей, я использую значок 16 пикселей с прозрачной рамкой. При повторной выборке маленьких значков остаются артефакты. Но в этом суть. Требуется дополнительная работа, которая является сложной. - person David Heffernan; 21.11.2014
comment
Вы пробовали последние изменения 10.1 Berlin? Полезны ли они для вас при создании приложений с поддержкой DPI для каждого монитора или еще нет? - person Warren P; 02.05.2016
comment
@Warren Не уверен, что в Берлине много нового. Большие перемены в Сиэтле. Я пока не собираюсь по пользователям. Что мне не нравится, так это то, что неклиентская область, строка заголовка, похоже, не масштабируется. - person David Heffernan; 02.05.2016
comment
Хм. Существуют API-интерфейсы win32, которые заставляют неклиентскую область быть больше, не так ли? - person Warren P; 02.05.2016
comment
Насколько мне известно, нет. Взгляните на вопросы здесь. В частности, тот, который спросил здесь Коди Грей несколько дней назад. - person David Heffernan; 02.05.2016

Этот манифест работает, но с некоторыми предупреждениями:

  • Обратите внимание на различные различия в «метаданных» для asmv1, asm.v2 и asmv3. Наверное, не важно.

  • Как заметил Дэвид, вероятно, значение True/PM, а не True, имеет значение. Microsoft, по всей видимости, задокументировала это. (усмехается)

  • Некоторые варианты SMI / 2011 БУДУТ запускаться БЕЗ ужасных ошибок запуска SxS, но я не нашел вариант SMI / 2011, который РАБОТАЕТ.

  • После использования приложения, которое включало Per Monitor API и определяло совместимость с ОС как Windows 8.1, я обнаружил в своем приложении УЖАСНЫЕ регрессии. Microsoft изменила поведение фокуса мыши в приложениях уровня Windows 8.1. Я НЕ РЕКОМЕНДУЮ ДАННЫЙ ПОДХОД. Зарегистрируйтесь с помощью КОДА, а не с помощью МАНИФЕСТА, и НЕ ИСПОЛЬЗУЙТЕ <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/> пример ниже!

Вот XML. У меня возникли проблемы с тем, что StackOverflow искажает этот XML, поэтому, если он выглядит плохо, вы видите ошибки StackOverflow.

<?xml version="1.0" encoding="utf-8" ?>
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" >
  <assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
    <security>
      <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
        <requestedExecutionLevel level="asInvoker" uiAccess="false" />
      </requestedPrivileges>
    </security>
  </trustInfo>

  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
    <application>
      <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
    </application>
  </compatibility>

  <asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
    <asmv3:windowsSettings
         xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
      <dpiAware>True/PM</dpiAware>
    </asmv3:windowsSettings>
  </asmv3:application>
</asmv1:assembly>

И у вас тоже может быть код:

Пример кода:

unit PerMonitorApi;

interface

const
   Process_DPI_Unaware = 0;
   Process_System_DPI_Aware = 1;    // Old windows 8.0
   Process_Per_Monitor_DPI_Aware = 2; // Windows 8.1

function SystemCanSupportPerMonitorDpi(AutoEnable: Boolean): Boolean; // New Windows 8.1 dpi awareness available?

function SystemCanSupportOldDpiAwareness(AutoEnable: Boolean): Boolean; // Windows Vista/ Windows 7 Global System DPI functional level.

var
   _RequestedLevelOfAwareness:LongInt;
   _ProcessDpiAwarenessValue:LongInt;

implementation

uses
   System.SysUtils,
   WinApi.Windows;

type
   TGetProcessDPIAwarenessProc = function(const hprocess: THandle; var ProcessDpiAwareness: LongInt): HRESULT; stdcall;
   TSetProcessDPIAwarenessProc = function(const ProcessDpiAwareness: LongInt): HRESULT; stdcall;

const
   E_ACCESSDENIED = $80070005;



function _GetProcessDpiAwareness(AutoEnable: Boolean): LongInt;
var
   hprocess: THandle;
   HRESULT: DWORD;
   BAwareness: Integer;
   GetProcessDPIAwareness: TGetProcessDPIAwarenessProc;
   LibHandle: THandle;
   PID: DWORD;

   function ManifestOverride: Boolean;
   var
      HRESULT: DWORD;
      SetProcessDPIAwareness: TSetProcessDPIAwarenessProc;
   begin
      Result := False;
      SetProcessDPIAwareness := TSetProcessDPIAwarenessProc(GetProcAddress(LibHandle, 'SetProcessDpiAwareness'));
      if Assigned(SetProcessDPIAwareness) and (_RequestedLevelOfAwareness>=0) then
      begin
         HRESULT := SetProcessDPIAwareness(_RequestedLevelOfAwareness ); // If we do this we don't need the manifest change.
         Result := (HRESULT = 0) or (HRESULT = E_ACCESSDENIED)
         // if Result = 80070005 then ACESS IS DENIED, means already set.
      end
   end;

begin
   Result := _ProcessDpiAwarenessValue;
   if (Result = -1) then
   begin
      BAwareness := 3;
      LibHandle := LoadLibrary('shcore.dll');
      if LibHandle <> 0 then
      begin
         if (not AutoEnable) or ManifestOverride then
         begin
            // This supercedes the Vista era IsProcessDPIAware api, and is available in Windows 8.0 and 8.1,although only
            // windows 8.1 and later will return a per-monitor-dpi-aware result.
            GetProcessDPIAwareness := TGetProcessDPIAwarenessProc(GetProcAddress(LibHandle, 'GetProcessDpiAwareness'));
            if Assigned(GetProcessDPIAwareness) then
            begin
               PID := WinApi.Windows.GetCurrentProcessId;
               hprocess := OpenProcess(PROCESS_ALL_ACCESS, False, PID);
               if hprocess > 0 then
               begin
                  HRESULT := GetProcessDPIAwareness(hprocess, BAwareness);
                  if HRESULT = 0 then
                     Result := BAwareness;
               end;
            end;
         end;
      end;
   end;
end;

// If this returns true, this is a windows 8.1 system that has Per Monitor DPI Awareness enabled
// at a system level.
function SystemCanSupportPerMonitorDpi(AutoEnable: Boolean): Boolean;
begin
   if AutoEnable then
   begin
    _RequestedLevelOfAwareness := Process_Per_Monitor_DPI_Aware;
    _ProcessDpiAwarenessValue := -1;
   end;
   Result := _GetProcessDpiAwareness(AutoEnable) = Process_Per_Monitor_DPI_Aware;
end;


// If this returns true, This is either a Windows 7 machine, or a Windows 8 machine, or a
// Windows 8.1 machine where the Per-DPI Monitor Awareness feature has been disabled.
function SystemCanSupportOldDpiAwareness(AutoEnable: Boolean): Boolean;
begin
   if AutoEnable then
   begin
     _RequestedLevelOfAwareness := Process_Per_Monitor_DPI_Aware;
     _ProcessDpiAwarenessValue := -1;
   end;

   Result := _GetProcessDpiAwareness(AutoEnable) = Process_System_DPI_Aware;
end;


initialization
   _ProcessDpiAwarenessValue := -1;// not yet determined.
   _RequestedLevelOfAwareness := -1;

end.
person Warren P    schedule 10.11.2014

Либо измените манифест, указанный в Project | Опции | Application или включите дополнительный манифест с помощью директивы $ R в файл .dpr.

Также ваш раздел asmv3: application выглядит нормально, за исключением того, что, я думаю, вам нужно написать «True» с нижним регистром t, как в «true».

person Larsdk    schedule 10.11.2014
comment
Оказывается, это True/PM. Как обычно, DavidH, похоже, в этом разбирается! - person Warren P; 11.11.2014