Visual Studio | WPF > Window Message > WM_DPICHANGED を受信する

```txt:開発環境
Windows 8.1 Pro (64bit)
Microsoft Visual Studio 2017 Community
Sublime Text 2
```

[こちらのコメント](https://qiita.com/7of9/items/f183ebbe005bad353540#comment-b8a7aa8217cf638c6c1b)において`WM_DPICHANGED`を受信できないか質問を受けました。

試したところ、受信できませんでした。

いくつか調べて受信できる状況になったため、情報共有します。

### 参考

[C# 4Kディスプレイの解像度を取得する](https://qiita.com/gansaibow/items/73a1a7119831c019af74)
にてSetProcessDpiAwareness()の使い方が紹介されています。

情報感謝です。

https://stackoverflow.com/questions/32148151/setprocessdpiawareness-not-having-effect
において以下が参考になりました。
> answered Aug 21 '15 at 20:14
Aybe

> Edit your AssemblyInfo.cs and add the following:

```
[assembly: DisableDpiAwareness]
```

## 手順

### 1. AssemblyInfo.csに`DisableDpiAwareness`を指定

AssemblyInfo.csの最後に`DisableDpiAwareness`を指定

```AssemblyInfo.cs
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Media;

// アセンブリに関する一般情報は以下の属性セットをとおして制御されます。
// アセンブリに関連付けられている情報を変更するには、
// これらの属性値を変更してください。
[assembly: AssemblyTitle("171113_t1620_WM_DPICHANGED")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("171113_t1620_WM_DPICHANGED")]
[assembly: AssemblyCopyright("Copyright ©  2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

// ComVisible を false に設定すると、このアセンブリ内の型は COM コンポーネントから
// 参照できなくなります。COM からこのアセンブリ内の型にアクセスする必要がある場合は、
// その型の ComVisible 属性を true に設定してください。
[assembly: ComVisible(false)]

//ローカライズ可能なアプリケーションのビルドを開始するには、
//.csproj ファイルの <UICulture>CultureYouAreCodingWith</UICulture> を
//<PropertyGroup> 内部で設定します。たとえば、
//ソース ファイルで英語を使用している場合、<UICulture> を en-US に設定します。次に、
//下の NeutralResourceLanguage 属性のコメントを解除します。下の行の "en-US" を
//プロジェクト ファイルの UICulture 設定と一致するよう更新します。

//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]


[assembly: ThemeInfo(
    ResourceDictionaryLocation.None, //テーマ固有のリソース ディクショナリが置かれている場所
                                     //(リソースがページ、
                                     //またはアプリケーション リソース ディクショナリに見つからない場合に使用されます)
    ResourceDictionaryLocation.SourceAssembly //汎用リソース ディクショナリが置かれている場所
                                              //(リソースがページ、
                                              //アプリケーション、またはいずれのテーマ固有のリソース ディクショナリにも見つからない場合に使用されます)
)]


// アセンブリのバージョン情報は次の 4 つの値で構成されています:
//
//      メジャー バージョン
//      マイナー バージョン
//      ビルド番号
//      Revision
//
// すべての値を指定するか、次を使用してビルド番号とリビジョン番号を既定に設定できます
// 既定値にすることができます:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

// 2017/11/13追加
[assembly: DisableDpiAwareness]
```

### 2. SetProcessDpiAwarenessのコール

`SetProcessDpiAwareness(ProcessDPIAwareness.ProcessPerMonitorDPIAware);`をMainWindow()にてコールする。
そのためには、以下の追加も必要となる。

```csharp
        private enum ProcessDPIAwareness
        {
            ProcessDPIUnaware = 0,
            ProcessSystemDPIAware = 1,
            ProcessPerMonitorDPIAware = 2
        }

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

WndProc()でのWM_DPICHANGED 処理も含めたのが以下のコードになります。

```MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
//
using System.Windows.Interop;
using System.Runtime.InteropServices;

namespace _171113_t1620_WM_DPICHANGED
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        const int WM_DPICHANGED = 0x02E0;

        private enum ProcessDPIAwareness
        {
            ProcessDPIUnaware = 0,
            ProcessSystemDPIAware = 1,
            ProcessPerMonitorDPIAware = 2
        }

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

        public MainWindow()
        {
            InitializeComponent();
            SetProcessDpiAwareness(ProcessDPIAwareness.ProcessPerMonitorDPIAware);

            Loaded += (o, e) =>
            {
                var source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
                source.AddHook(new HwndSourceHook(WndProc));
            };
        }
        private static IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            if (msg == WM_DPICHANGED)
            {
                string timstr = DateTime.Now.ToLongTimeString();
                Console.WriteLine("DPI Changed : " + timstr);
            }
            return IntPtr.Zero;
        }
    }
}
```

### 動作確認

```txt:動作環境
- Windows 10 Pro (64bit)
- デュアルモニタ
  - 左モニタ: 「テキスト、アプリ、その他の項目のサイズ」 を100%
  - 右モニタ: 「テキスト、アプリ、その他の項目のサイズ」 を125%
```

ソフトを実行して、メインウィンドウをモニタ間移動する。

メインウィンドウ
![qiita.png](https://qiita-image-store.s3.amazonaws.com/0/32870/9cef063c-f5bd-848b-7a22-84e34e0c079e.png)

コンソール出力
![qiita.png](https://qiita-image-store.s3.amazonaws.com/0/32870/117c5cb0-ff40-8526-8de7-a401f8513b09.png)

### リンク

- [DisableDpiAwarenessAttribute クラス @ MSDN](https://msdn.microsoft.com/ja-jp/library/system.windows.media.disabledpiawarenessattribute(v=vs.110).aspx)
- [SetProcessDpiAwareness function @ MSDN](https://msdn.microsoft.com/ja-jp/library/windows/desktop/dn302122(v=vs.85).aspx)
- [WM_DPICHANGED message](https://msdn.microsoft.com/ja-jp/library/windows/desktop/dn312083(v=vs.85).aspx)