Подтвердить что ты не робот

Как я могу сделать Windows 8.1 осведомленным о том, что мое приложение Delphi хочет поддерживать Per Monitor DPI?

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

Это содержимое .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, но я прочитал, что есть потенциальные недостатки этого подхода, и поэтому я предпочел бы работать только с Manifest-only.

Если я назову GetProcessDPIAwareness(hProc,Awareness), я вернусь "Awareness = Process_DPI_Unaware".

Мое другое беспокойство заключается в том, что в источниках MSDN они указывают добавление ADDITIONAL манифеста. Принимая во внимание, что я использую способность Delphi XE6 IDE связывать ONE и ONLY ONE в моем приложении. Я никогда не замечал, что добавление какого-либо дополнительного манифеста по сравнению с тем, что только один из них когда-либо был проблемой, за исключением того, что, возможно, система управления .manifest в Visual Studio 2010 была хромой, и именно поэтому совет существовал и поэтому не имеет отношения к другим IDE/Языки.

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

обновление:

Вот еще один выстрел в манифесте:

<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>

Приведенный мини-манифест изменяет поведение приложения, но не совсем так, как я этого хотел. С приведенным выше маленьким манифестом обнаружен уровень осведомленности OLD Windows 8.0/Windows 7/Vista DPI.

update 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 было БРОКЕННОЕ ПОВЕДЕНИЕ ПОВЕДЕНИЯ МЫШЕЙ, БРОНИРОВАННОЕ ОКНО ЖИВОПИСЬ ужасными способами, о которых я даже не мог догадаться. Включение этого флага вызвало очень тонкие ошибки в моем приложении, в том числе проблемы с рисованием, невозможность щелкнуть элементы управления (мышиные сообщения, не достигающие элементов управления, из-за потери мыши) и многие другие проблемы.

4b9b3361

Ответ 1

Документировано в теме MSDN Написание приложений DPI-Aware Desktop и Win32:

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

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

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

Значение манифестности DPI, описание

False Устанавливает приложение не с поддержкой DPI.

True. Устанавливает приложение в системную информацию, поддерживающую DPI.

Per-monitor В Windows 8.1 задает приложение для каждого монитора-DPI. В Windows Vista через Windows 8 приложение не поддерживает DPI.

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

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

В той же теме представлен манифест 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 с выбранным вами значением.

Ответ 2

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

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

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

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

  • После использования приложения, которое включило Per Monitor API и определенную совместимость с ОС в качестве Windows 8.1, в моем приложении были найдены некоторые УЖАСНЫЕ регрессии. Microsoft изменила поведение фокуса мыши в приложениях уровня Windows 8.1. Я НЕ РЕКОМЕНДУЮ ЭТО ПОДХОД. Выбирайте через CODE вместо MANIFEST и НЕ ИСПОЛЬЗУЙТЕ пример <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.

Ответ 3

Или измените манифест, указанный в Project | Варианты | Приложение или включить дополнительный манифест, используя директиву $R в файле .dpr.

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