How can I make Windows 8.1 aware that my Delphi application wants to support Per Monitor DPI?

后端 未结 3 629
攒了一身酷
攒了一身酷 2020-12-31 00:21

I have tried to make Windows 8.1 recognize a Delphi XE6 application (a demo program) that I have been trying to build, and have it recognize my application is Per-Monitor DP

相关标签:
3条回答
  • 2020-12-31 00:43

    Either modify the manifest pointed to in Project | Options | Application or include an additional manifest using the $R directive in the .dpr file.

    Also your asmv3:application section looks fine except that I think you need to spell "True" with a lower case t as in "true".

    0 讨论(0)
  • 2020-12-31 00:51

    It is documented on the MSDN topic Writing DPI-Aware Desktop and Win32 Applications:

    Mark the application as per monitor-DPI aware by modifying the application manifest or by calling the SetProcessDpiAwarenessAPI. We recommend that you use the application manifest because that sets the DPI awareness level when the application is launched. Use the API only in the following cases:

    • Your code is in a dll that runs via rundll32.exe. This is a launch mechanism that does not support the application manifest.
    • You need to make complex run-time decisions based on OS version or other considerations. For example, if you need the application to be system-DPI aware on Windows 7 and dynamically aware on Windows 8.1, use the True/PM manifest setting.

    If you use the SetProcessDpiAwareness method to set the DPI awareness level, you must call SetProcessDpiAwareness prior to any Win32API call that forces the system to begin virtualization.

    DPI awareness manifest value, Description

    False Sets the application to not DPI-aware.

    True Sets the application to system DPI–aware.

    Per-monitor On Windows 8.1, sets the application to per monitor-DPI aware. On Windows Vista through Windows 8, sets the application to not DPI–aware.

    True/PM On Windows 8.1, sets the application to per monitor-DPI aware. On Windows Vista through Windows 8, sets the application to system-DPI aware.

    You just use the standard DPI aware manifest but specify True/PM or Per-monitor instead of True.

    The same topic gives the DPI aware manifest as follows:

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

    So, replace True with your chosen value.

    0 讨论(0)
  • 2020-12-31 00:55

    This manifest works but with some warnings:

    • Note the various "metadata" differences about asmv1, and asm.v2 and asmv3. Probably not important.

    • As David pointed out probably its the True/PM value, instead of True that makes all the difference. Microsoft apparently DID document it. (grins)

    • Some SMI/2011 variants WILL launch WITHOUT dread SxS launch errors, but I haven't found an SMI/2011 variant that WORKS.

    • After using an application that both enabled Per Monitor API, and defined OS compatibility as Windows 8.1, I found some HORRIBLE regressions in my application. Microsoft has changed the mouse focus behaviour in Windows 8.1 level applications. I do NOT RECOMMEND THIS APPROACH. Opt in via CODE instead of via MANIFEST, and DO NOT USE the <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/> example below!

    Here is the XML. I am having some trouble with StackOverflow mangling this XML, so if it looks bad, you're seeing StackOverflow bugs.

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

    And you can has teh codez too:

    Code sample:

    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.
    
    0 讨论(0)
提交回复
热议问题