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

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

это .содержимое манифеста я определил с помощью ресурсов 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), Я возвращаюсь "осведомленность = Process_DPI_Unaware".

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

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

выше мини-манифест изменяет поведение приложения, но не совсем так, как я хотел. С вышеуказанным крошечным манифест, обнаружен старый уровень осведомленности 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 сломала поведение захвата мыши, сломала картину Окна ужасными способами, о которых я даже не мог догадаться. Включение этого флага вызвало очень тонкие ошибки в моем приложении, включая проблемы с покраской, невозможность нажать на элементы управления (мышь вниз сообщения, не достигающие элементов управления, из-за захвата мыши теряется), и многие другие проблемы.

3 ответов


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

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

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

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

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

False устанавливает приложение в не DPI-aware.

правда устанавливает для приложения значение system DPI-aware.

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

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

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

тот же раздел дает манифест DPI aware как следует:

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

Так, замените правда с выбранным значением.


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

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

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

  • некоторые варианты SMI / 2011 будут запускаться без ошибок запуска dread SxS, но я не нашел Вариант SMI / 2011, который работает.

  • после использования приложения, которое включено для монитора 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 = 070005;



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.

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

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