WPF
networkApp
windowMessage
myVisualStudioStudy

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

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

こちらのコメントにおいてWM_DPICHANGEDを受信できないか質問を受けました。

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

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

参考

C# 4Kディスプレイの解像度を取得する
にて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()にてコールする。
そのためには、以下の追加も必要となる。

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

動作確認

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

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

メインウィンドウ
qiita.png

コンソール出力
qiita.png

リンク