web-dev-qa-db-ja.com

DPI認識-1つのリリースでは認識せず、他のリリースではシステム認識

ですから、この本当に奇妙な問題があります。私たちのアプリケーションはC#/ WinFormsアプリです。 6.0リリースでは、アプリケーションはDPIに対応していません。 6.1リリースでは、突然DPIに対応するようになりました。
6.0リリースでは、高DPIで実行すると、Windowsビットマップスケーリングが使用されます。これは、画面レイアウトに影響を与えないため、問題ありません。 6.1リリースでは、何らかの理由でDPIに対応するようになったため、ユーザーインターフェイスが壊れます。
現在、これを修正する立場にはありません。数百の画面があるため、DPI対応モードですべてを正しく機能させるには時間がかかります。

これは、SysInternals ProcessExplorerを使用して確認しました。 6.0リリースではnawareと表示されますが、6.1リリースでは最初はnawareと表示されますが、その後System Awareに変更されます。
後者は、コードがEXEからアセンブリに入力されたときに発生しますDLLすべてのユーザーインターフェイスコードが含まれています(EXEは基本的に非常に薄いシェルです。実際に実行されるのはすべてです)。プレゼンテーション層アセンブリでControllerクラスを呼び出します。)

以下を確認しました。

  • どちらのバージョンも、VSS2017を使用してリリースモードで構築されています。
  • どちらのバージョンも同じ.NETFramework(4.5)を対象としています
  • どちらのバージョンも同じDevExpressバージョンを使用しています。
  • どちらのバージョンにも同じアプリケーションマニフェストがあり、not DPI認識設定が有効になっています。
  • どちらのバージョンにも、DPI関連のWindowsAPIへの呼び出しはありません。
  • Sys Internalsといくつかのメッセージボックスを使用して、6.1リリースが認識される時点(プレゼンテーションアセンブリへのエントリポイント)とその時点で読み込まれるDLL(DevExpress、その他の依存関係)を決定し、小さなダミーアプリを作成しました。同じDLLを参照し、これらがロードされていることを確認しました。そのダミーアプリはDPIに対応しません。
  • 両方のリリース間でメインのcsprojファイルを比較しましたが、意味のある違いはありません。
    • どちらのリリースも、WPFから何も参照していません。

6.1リリースが突然DPI対応になった理由がわかりません。他に何を見るべきかわからないので、このリリースをDPI非認識モードに戻す修正が必要です。それは私たちのリリースを延期しています。どんなポインタでも本当にありがたいです。この時点で何でも試してみます。

12
Roel Vlemmings

この質問で報告された問題について
[。 -対応(システム対応)。

  • また、アプリケーションはapp.manifest<windowsSettings>の解釈に依存しています。ここで、DPI認識定義がない場合、デフォルトで(下位互換性のために)DPI非認識になります。

  • WPFアセンブリへの直接参照や、DPI関連のAPI呼び出しはありません。

  • アプリケーションには、サードパーティのコンポーネント(および場合によっては外部の依存関係)が含まれています。


利用可能な画面解像度(および関連するDPIスケーリング設定)の多様性を考えると、DPI対応がUIプレゼンテーションの関連する側面になっているため、ほとんどのコンポーネントプロデューサーはHigh-DPIに適応しており、その製品はDPI対応(DPI変更時にスケーリング)が検出された場合)、DPI対応アセンブリを使用します(多くの場合、WPFアセンブリを参照し、定義上DPI対応)。

これらのDPI対応コンポーネントの1つがプロジェクトで(直接的または間接的に)参照されると、DPI対応が明示的に無効にされていない場合、DPI非対応アプリケーションはDPI対応になります。

Assembly DPI-Awarenessを宣言するためのより直接的な(そして推奨される)方法は、アプリケーションマニフェストで明示的に宣言することです。

Visual Studio 2017より前のアプリケーションマニフェスト設定については、 Hans Passant 回答を参照してください。
DPI設定が高いマシンで実行するようにアプリを構成する方法

Visual Studio2015-Upd.1およびVisualStudio 2017 app.manifestでは、この設定は既に存在しているため、コメントを外す必要があります。セクションを設定します:<dpiAware>false</dpiAware>

<?xml version="1.0" encoding="utf-8"?>
<Assembly manifestVersion="1.0" xmlns="urn:schemas-Microsoft-com:asm.v1">
  <assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>

  //(...)

  <!-- Indicates that the application is DPI-aware and will not be automatically scaled by Windows at higher
       DPIs. Windows Presentation Foundation (WPF) applications are automatically DPI-aware and do not need 
       to opt in. Windows Forms applications targeting .NET Framework 4.6 that opt into this setting, should 
       also set the 'EnableWindowsFormsHighDpiAutoResizing' setting to 'true' in their app.config. -->

  <application xmlns="urn:schemas-Microsoft-com:asm.v3">
    <windowsSettings>
      <dpiAware xmlns="http://schemas.Microsoft.com/SMI/2005/WindowsSettings">false</dpiAware>
    </windowsSettings>
  </application>

//(...)

</Assembly>

詳細については、次のMSDNの記事を参照してください。
Windowsでの高DPIデスクトップアプリケーション開発
プロセスのデフォルトのDPI認識を設定する

もう1つの方法は、次のWindowsAPI関数を使用してプロセスコンテキストのDPI認識を設定することです。

Windows 7
SetProcessDPIAware

[DllImport("user32.dll", SetLastError=true)]
static extern bool SetProcessDPIAware();

Windows 8.1
SetProcessDpiAwareness

[DllImport("shcore.dll")]
static extern int SetProcessDpiAwareness(ProcessDPIAwareness value);

enum ProcessDPIAwareness
{
    DPI_Unaware = 0,
    System_DPI_Aware = 1,
    Per_Monitor_DPI_Aware = 2
}

Windows 10、バージョン1703
SetProcessDpiAwarenessContext()
(モニターごとのDPI認識を選択する場合は、Context_PerMonitorAwareV2を使用してください)

以下も参照してください: 混合モードDPIスケーリングおよびDPI対応API-MSDN

Windows 10、バージョン1809(2018年10月)
新しい DPI_AWARENESS_CONTEXT が追加されました:DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED

GDIベースのコンテンツの品質が向上したことを認識していないDPI。このモードはDPI_AWARENESS_CONTEXT_UNAWAREと同様に動作しますが、ウィンドウが高DPIモニターに表示されたときに、システムがテキストやその他のGDIベースのプリミティブのレンダリング品質を自動的に向上させることもできます。

GetWindowDpiAwarenessContext() 関数を使用してウィンドウのDPI_AWARENESS_CONTEXTハンドルを取得し、 GetThreadDpiAwarenessContext() を使用して現在のスレッドのDPI_AWARENESS_CONTEXTハンドルを取得します。次に GetAwarenessFromDpiAwarenessContext()DPI_AWARENESS構造体からDPI_AWARENESS_CONTEXT値を取得します。

[DllImport("user32.dll", SetLastError=true)]
static extern IntPtr GetWindowDpiAwarenessContext(IntPtr hWnd);

[DllImport("user32.dll", SetLastError=true)]
static extern IntPtr GetThreadDpiAwarenessContext();

[DllImport("user32.dll", SetLastError=true)]
static extern int GetAwarenessFromDpiAwarenessContext(InPtr DPI_AWARENESS_CONTEXT);


[DllImport("user32.dll", SetLastError=true)]
static extern int SetProcessDpiAwarenessContext(ContextDPIAwareness value);

// Virtual enumeration: DPI_AWARENESS_CONTEXT is *contextual*. 
// This value is returned by GetWindowDpiAwarenessContext() or GetThreadDpiAwarenessContext()
// and finalized by GetAwarenessFromDpiAwarenessContext(). See the Docs.
enum ContextDPIAwareness
{
    Context_Unaware = ((DPI_AWARENESS_CONTEXT)(-1)),
    Context_SystemAware = ((DPI_AWARENESS_CONTEXT)(-2)),
    Context_PerMonitorAware = ((DPI_AWARENESS_CONTEXT)(-3)),
    Context_PerMonitorAwareV2 = ((DPI_AWARENESS_CONTEXT)(-4)),
    Context_UnawareGdiScaled = ((DPI_AWARENESS_CONTEXT)(-5))
}

DPI認識はスレッドベースであるため、これらの設定は特定のスレッドに適用できます。これは、DPI認識を実装するためにユーザーインターフェイスを再設計するときに役立ち、システムが重要性の低いコンポーネントをスケーリングしながら、より重要な機能に焦点を合わせることができます。

SetThreadDpiAwarenessContext
SetProcessDpiAwarenessContext()と同じパラメーター)

Assemblyinfo.cs
WPFアセンブリを参照するサードパーティ/外部コンポーネントがアプリケーションのDPI認識ステータスを再定義する場合、この自動動作を無効にして、プロジェクトにパラメータを挿入することができますAssemblyinfo.cs

[Assembly: System.Windows.Media.DisableDpiAwareness]
9
Jimi