2014-11-10 3 views
22

Ich habe versucht, Windows 8.1 eine Delphi XE6-Anwendung (ein Demo-Programm) erkennen zu lassen, die ich zu bauen versuche, und habe erkannt, dass meine Anwendung Per-Monitor DPI-bewusst ist, rein durch die Manifest-Technik. Delphi XE6 (und alle anderen ähnlich aktuellen Versionen von Delphi) machen das Hinzufügen eines Manifests zu einem Kinderspiel, innerhalb von Projektoptionen, und ich habe es getan.Wie kann ich Windows 8.1 darauf aufmerksam machen, dass meine Delphi-Anwendung Per Monitor DPI unterstützen soll?

Dies ist der Manifest-Inhalt, den ich mithilfe von MSDN-Ressourcen ermittelt habe. Ich vermute, dass es leicht falsch sein könnte.

Wenn Sie dieses Manifest ausprobieren möchten, erstellen Sie eine leere VCL-Anwendung, verwenden Sie diesen Inhalt als Ihr Manifest, und fügen Sie den Code hinzu (Code ist derzeit meiner Antwort auf diese Frage beigefügt).

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

Hat jemand das zur Arbeit gebracht? Was ich finde, ist, dass das oben genannte nicht erkannt wird. Wenn ich SetProcessDPIAwareness(Process_Per_Monitor_DPI_Aware) zuerst anrufen, dann GetProcessDPIAwareness(hProc,Awareness) anrufen, bekomme ich das notwendige Awareness = Process_Per_Monitor_DPI_Aware zurück, aber ich habe gelesen, dass es mögliche Nachteile zu diesem Ansatz gibt, und so würde ich einen Arbeitsmanifest-only Ansatz bevorzugen.

Wenn ich GetProcessDPIAwareness(hProc,Awareness) aufrufen, bekomme ich zurück "Awareness = Process_DPI_Unaware".

Meine andere Sorge ist, dass in den MSDN-Quellen angeben, ein zusätzliches Manifest hinzufügen. Hingegen benutze ich die IDE von Delphi XE6, um ONE und ONLY ONE manifest in meine Anwendung zu integrieren. Ich habe nie bemerkt, dass das Hinzufügen eines zusätzlichen Manifests im Vergleich zu nur einem Problem jemals ein Problem war, abgesehen davon, dass das Manifest-Managementsystem in Visual Studio 2010 möglicherweise lahm war und daher der Tipp existierte und somit keine Relevanz für andere IDEs hat /Sprachen.

In Visual Studio 2013 gibt es ein Kontrollkästchen direkt in den Projektoptionen, aber ich habe kein Visual Studio 2013, so dass ich ein funktionierendes Manifest nicht untersuchen kann.

Update:

Hier ist ein weiterer Schuss auf einem 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> 

Das obige Mini manifest ändert das Verhalten der Anwendung, aber nicht genau so, wie ich es wollte. Mit dem obigen kleinen Manifest wird der OLD Windows 8.0/Windows 7/Vista DPI-Erkennungsgrad erkannt.

Update 2:

Dank Remy für die Ideen. Interessanterweise scheint das Folgende ausreichend zu sein, um einen Anwendungsstart zu ermöglichen. Das Mischen der SMI/2005-Syntax mit dem oben genannten verursachte jedoch einen Side-by-Side-Startfehler. Sie können sehen, dass Microsoft auf ihren Manifesten ziemlich viel aufgewühlt hat. Beachten Sie, dass die folgenden nicht wirklich mein Problem lösen, aber es bietet noch eine weitere „potentielle Basenform“, die auf die wirkliche Lösung SCHLIEßEN sein könnte:

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

Update 3:

CODE RED ALERT! Verwenden Sie in einer Delphi VCL-Anwendung NICHT das folgende OS COMPATIBILITY-Flag: <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>. Microsoft hat ein gebrochenes Mauser-Verhalten, zerbrochenes Fenstergemälde auf schreckliche Weise, die ich nicht mal erraten konnte.Das Aktivieren dieses Flags verursachte sehr subtile Fehler in meiner Anwendung, einschließlich Malproblemen, Unfähigkeit, auf Steuerelemente zu klicken (Maus-Down-Nachrichten erreichten keine Steuerelemente, weil die Mauserfassung verloren ging) und viele andere Probleme.

+0

Zusätzliche Manifeste hinzugefügt z. durch das mt.exe-Tool werden nur in einem einzigen Manifest zusammengeführt (das in der Anwendung sein darf), nicht wahr? Ich glaube, du solltest nur deine App ändern. manifestieren, ohne zu versuchen, mehr von ihnen zu haben. – TLama

+0

Okay, das ist gut zu wissen. Ich vermute, dass ich einige obskure XML/Manifest Metadaten etwas falsch oben habe. –

+0

Ich würde vorschlagen, die Reihenfolge der Top-Level-XML-Elemente zu ändern. Mache die '' zuerst, dann die '' nächste, dann endlich die '' letzte. In dieser Reihenfolge wurden diese Funktionen in Windows eingeführt. XML soll nicht auftragsabhängig sein, kann aber sein, wenn ein XML-Schema verwendet wird. Der Parser von Windows kann auf die Reihenfolge angewiesen sein, es muss möglicherweise das Element Win8.1 '' vorhanden sein, bevor das Element ' 'verarbeitet wird. –

Antwort

10

Es ist auf der MSDN-Thema dokumentiert Writing DPI-Aware Desktop and Win32 Applications:

Markieren Sie die Anwendung per Monitor -DPI-bewusst durch Ändern des Anwendungsmanifests oder durch Aufrufen der SetProcessDpiAwarenessAPI. Wir empfehlen , dass Sie das Anwendungsmanifest verwenden, da hierdurch die DPI-Sicherheitsstufe festgelegt wird, wenn die Anwendung gestartet wird. Verwenden Sie die API nur in den folgenden Fällen:

  • Ihr Code in einer DLL ist, die über rundll32.exe läuft. Dies ist ein Startmechanismus, der das Anwendungsmanifest nicht unterstützt.
  • Sie müssen komplexe Laufzeitentscheidungen basierend auf der Betriebssystemversion oder anderen Überlegungen treffen. Wenn Sie beispielsweise die Anwendung als System-DPI unter Windows 7 und dynamisch unter Windows 8.1 als verwenden möchten, verwenden Sie die True/PM-Manifesteinstellung.

Wenn Sie die SetProcessDpiAwareness Methode verwenden, um die DPI Bewusstsein Ebene zu setzen, müssen Sie rufen SetProcessDpiAwareness vor jeder Win32API Anruf , die das System-Virtualisierung zu beginnen zwingt.

DPI Bewusstsein manifestieren Wert, Beschreibung

Falsch Stellt die Anwendung nicht DPI-aware.

Wahr Setzt die Anwendung auf System DPI-aware.

Per-Monitor Unter Windows 8.1, legt die Anwendung pro Monitor-DPI bewusst. Unter Windows Vista bis Windows 8 wird die -Anwendung auf nicht DPI-fähig gesetzt.

Wahr/PM Unter Windows 8.1 legt die Anwendung auf Monitor-DPI fest. Unter Windows Vista bis Windows 8 wird die Anwendung auf System-DPI gesetzt.

Sie nur die Standard-DPI bewusst Manifest verwenden, aber True/PM oder Per-Monitor statt Wahr angeben.

Das gleiche Thema gibt das DPI bewusst manifest wie folgt:

<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 Wahre mit dem gewählten Wert ersetzen.

+0

Ich akzeptiere diese Antwort, weil sie die genaue richtige Antwort auf meine Frage enthält.Jeder, der Beispielcode haben möchte, sollte auch meine Antwort lesen, wenn ich das dort ansetze, damit die Leute sehen können, wie sie die neuen 'shcore.dll'-Exporte importieren und aufrufen. Danke David, dass du MSDN so viel besser verstehen kannst als ich. :-) –

+1

Danke. Ich denke, ich muss meine App pro Monitor bewusst machen. Das wird einige Arbeit an meiner Bildliste Klasse ..... –

+0

Ich habe festgestellt, dass meine Anwendung, die einige klassische Single-System-DPI-basierte Form Resizing-Logik hat, mit Windows 8.1 SCHLECHT interagiert, es sei denn, Sie fügen ein Manifest wie das ich Sie haben die Option "Per-Monitor-DPI-Awareness" auf der Seite "Anzeigeeinstellungen" in der Systemsteuerung deaktiviert, indem Sie "Eine Skalierungsstufe auswählen" ausgewählt haben. Sie werden also feststellen, dass Ihre bestehende App ohne DPI-Erkennung einige seltsame Verhaltensweisen aufweist, wenn sie unter Windows 8.1 ausgeführt wird. Es gibt einige extrem seltsame "Rückwärtskompatibilitäts" -Hacks, die in den Windows 8.1-Top-Level-Client-Window-Manager-Code eingebaut sind. –

2

Entweder das Manifest ändern, auf das in Project | Optionen | Anwendung oder fügen Sie ein zusätzliches Manifest hinzu, das die Direktive $ R in der .dpr-Datei verwendet.

Auch Ihre asmv3: Anwendung Abschnitt sieht gut aus, außer dass ich denke, Sie müssen "True" mit einem Kleinbuchstaben t wie in "True" buchstabieren.

+0

Es stellt sich heraus, es ist' True/PM'. Wie immer scheint DavidH das Zeug zu haben! –

6

Diese manifestieren funktioniert, aber mit einigen Warnungen:

  • Beachten Sie die verschiedenen "Metadaten" Differenzen über asmv1 und asm.v2 und asmv3. Wahrscheinlich nicht wichtig.

  • Wie David darauf hingewiesen, ist es wahrscheinlich der True/PM Wert, anstatt True, die den Unterschied macht. Microsoft hat es offenbar dokumentiert. (grins)

  • Einige SMI/2011-Varianten werden OHNE Angst SxS Startfehler starten, aber ich habe keine SMI/2011-Variante gefunden, die funktioniert.

  • Nachdem ich eine Anwendung verwendet habe, die sowohl die Per Monitor API als auch die Betriebssystemkompatibilität als Windows 8.1 aktiviert hat, habe ich einige HORRIBLE Regressionen in meiner Anwendung gefunden. Microsoft hat das Mausfokusverhalten in Windows 8.1-Anwendungen geändert. Ich empfehle diesen Ansatz nicht. Melden Sie sich über CODE statt über MANIFEST an, und verwenden Sie das unten stehende Beispiel <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/> NICHT!

Hier ist das XML. Ich habe einige Probleme mit StackOverflow, der dieses XML-Dokument ausblendet. Wenn es also schlecht aussieht, sehen Sie StackOverflow-Fehler.

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

Und Sie können hat teh codez auch:

Codebeispiel:

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. 
Verwandte Themen