windows - starter - delphi tokyo community edition
¿Cómo puedo hacer que Windows 8.1 sepa que mi aplicación Delphi desea ser compatible con Per Monitor DPI? (3)
He tratado de hacer que Windows 8.1 reconozca una aplicación Delphi XE6 (un programa de demostración) que he intentado compilar, y que reconozca que mi aplicación es compatible con Per-Monitor DPI, puramente mediante la técnica Manifest. Delphi XE6 (y todas las demás versiones actualizadas de Delphi) hacen que agregar un manifiesto sea fácil de hacer, dentro de las Opciones del proyecto, y lo he hecho.
Este es el contenido más explícito que he determinado utilizando recursos de MSDN. Sospecho que podría ser un poco incorrecto.
Si desea probar este manifiesto, haga una aplicación VCL vacía, use este contenido como su manifiesto y agregue el código (el código se adjunta actualmente a mi respuesta a esta pregunta).
<?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>
¿Alguien ha conseguido que esto funcione? Lo que encuentro es que lo anterior no es reconocido. Si llamo SetProcessDPIAwareness(Process_Per_Monitor_DPI_Aware)
primero, luego GetProcessDPIAwareness(hProc,Awareness)
, Awareness = Process_Per_Monitor_DPI_Aware
necesario, pero he leído que existen posibles inconvenientes para ese enfoque, por lo que preferiría un manifiesto de trabajo solo enfoque.
Si llamo GetProcessDPIAwareness(hProc,Awareness)
, obtengo `Awareness = Process_DPI_Unaware ''.
Mi otra preocupación es que en las fuentes de MSDN especifican agregar un manifiesto ADICIONAL. Mientras que, estoy usando la capacidad IDE de Delphi XE6 para vincular UNO y SOLAMENTE UN manifiesto a mi aplicación. Nunca me había dado cuenta de que agregar un manifiesto adicional versus tener solo uno era un problema, aparte de que tal vez el sistema de administración de manifiesto en Visual Studio 2010 era cojo, y es por eso que el consejo existía, y por lo tanto no tiene relevancia para otros IDEs / Idiomas.
En Visual Studio 2013, hay una casilla de verificación dentro de las opciones del proyecto, pero no tengo Visual Studio 2013, así que no puedo examinar un archivo .manifest de trabajo.
actualizar:
Aquí hay otra oportunidad en un manifiesto:
<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>
El mini manifiesto anterior cambia el comportamiento de la aplicación, pero no exactamente como yo quería. Con el manifiesto pequeño anterior, se detecta el VIEJO nivel de reconocimiento de DPI de Windows 8.0 / Windows 7 / Vista.
actualización 2:
Gracias Remy por las ideas. Curiosamente, lo siguiente parece ser lo suficientemente válido como para permitir el lanzamiento de una aplicación. Sin embargo, mezclar la sintaxis de SMI / 2005 con lo anterior causó un error de inicio de lado a lado. Puedes ver que Microsoft ha estado batiendo sus manifiestos bastante. Tenga en cuenta que lo siguiente no resuelve realmente mi problema, pero proporciona otra "forma de base potencial" que podría estar CERCA de la solución real:
<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>
actualización 3:
¡CÓDIGO ALERTA ROJA! NO USE el siguiente indicador de COMPATIBILIDAD <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
SO en ninguna aplicación Delphi VCL: <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
. Microsoft tiene COMPORTAMIENTO DE CAPTURA DE RATÓN ROTO, PINTURA DE VENTANA ROTA en formas horribles que ni siquiera he podido adivinar. Al activar esta bandera, se produjeron errores muy sutiles en mi aplicación, incluidos problemas de pintura, incapacidad para hacer clic en los controles (los mensajes del mouse no llegan a los controles, debido a que se perdió la captura del mouse) y muchos otros problemas.
Está documentado en el tema de MSDN Escritura de aplicaciones DPI-Aware Desktop y Win32 :
Marque la aplicación según el conocimiento del monitor-DPI modificando el manifiesto de la aplicación o llamando al SetProcessDpiAwarenessAPI. Recomendamos que use el manifiesto de la aplicación porque establece el nivel de conocimiento DPI cuando se inicia la aplicación. Use la API solo en los siguientes casos:
- Su código está en un archivo DLL que se ejecuta a través de rundll32.exe. Este es un mecanismo de inicio que no admite el manifiesto de la aplicación.
- Debe tomar decisiones complejas en tiempo de ejecución basadas en la versión del sistema operativo u otras consideraciones. Por ejemplo, si necesita que la aplicación sea compatible con el sistema-DPI en Windows 7 y consciente dinámicamente en Windows 8.1, use la configuración del manifiesto True / PM.
Si usa el método SetProcessDpiAwareness para establecer el nivel de conocimiento de DPI, debe llamar a SetProcessDpiAwareness antes de cualquier llamada a Win32API que obligue al sistema a comenzar la virtualización.
Valor manifiesto de conocimiento DPI, Descripción
False Configura la aplicación para que no sea compatible con DPI.
Verdadero Establece la aplicación al sistema compatible con DPI.
Por monitor En Windows 8.1, configura la aplicación por monitor-DPI consciente. En Windows Vista a través de Windows 8, configura la aplicación como no compatible con DPI.
Verdadero / PM En Windows 8.1, configura la aplicación por monitor-DPI consciente. En Windows Vista a través de Windows 8, establece la aplicación en el sistema-DPI consciente.
Solo utiliza el manifiesto estándar de DPI, pero especifica True / PM o Per-monitor en lugar de True .
El mismo tema le da al manifiesto consciente de DPI de la siguiente manera:
<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>
Por lo tanto, reemplace True con el valor elegido.
Este manifiesto funciona pero con algunas advertencias:
Tenga en cuenta las diversas diferencias de "metadatos" sobre asmv1 y asm.v2 y asmv3. Probablemente no es importante.
Como David señaló, probablemente sea el valor
True/PM
, en lugar deTrue
que marca la diferencia. Al parecer, Microsoft lo documentó. (sonríe)Algunas variantes de SMI / 2011 se lanzarán SIN errores de lanzamiento de SxS de pánico, pero no he encontrado una variante de SMI / 2011 que FUNCIONE.
Después de usar una aplicación que habilitaba la API por monitor y la compatibilidad definida del sistema operativo como Windows 8.1, encontré algunas regresiones HORRIBLES en mi aplicación. Microsoft ha cambiado el comportamiento del foco del mouse en las aplicaciones de nivel de Windows 8.1. NO RECOMIENDO ESTE ENFOQUE. Opta por CODE en lugar de a través de MANIFEST, y NO
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
ejemplo<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
continuación.
Aquí está el XML. Tengo problemas con para modificar este XML, por lo que si se ve mal, verá errores de .
<?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>
Y puedes tener los codez también:
Muestra de código:
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.
Modifique el manifiesto señalado en Proyecto | Opciones | Aplicación o incluir un manifiesto adicional utilizando la directiva $ R en el archivo .dpr.
También su asmv3: sección de aplicación se ve bien, excepto que creo que debe deletrear "Verdadero" con una minúscula t en "verdadero".