none
MFCアプリのモニタ毎の高DPI対応

    質問

  • MFCを使ったアプリをPer-Monitor DPI対応(モニタ毎の高DPI対応)にしています。
    ただ、プロジェクトのプロパティで、マニュフェストツール→入出力のDPI認識を「モニタごとの高いDPI認識」に設定していますが駄目でした。
    メインディスプレイがスケーリング150%、サブディスプレイがスケーリング100%の場合、サブディスプレイにアプリを移動すると、単にアプリ全体が拡大されるだけでした。
    メニューやタイトルバーなどは自動でディスプレイに合わせて最適にスケーリングされるのではないでしょうか?
    Windows 10 Creators Update、Visual Studio 2017 で動作・作成していますが駄目でした。
    何か設定が必要なのでしょうか?
    2017年5月8日 7:59

回答

  • マニフェストで「モニタごとの高い DPI 認識(Per monitor DPI Aware)」と設定されたウィンドウを、
    異なる DPI 設定のディスプレイ間で移動させるとき、タイトルバーやメニューは、自動的にスケーリングされないようです。スケーリングさせるには、フレーム ウィンドウ(トップレベル ウィンドウ)に WM_NCCREATE メッセージが来た時に、EnableNonClientDpiScaling 関数を呼び出す必要があるようです。

    // メッセージマップ
    BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
    	ON_WM_NCCREATE()
    END_MESSAGE_MAP()
    
    BOOL CMainFrame::OnNcCreate(LPCREATESTRUCT lpCreateStruct)
    {
    	typedef BOOL(WINAPI*fnTypeEnableNCScaling)(HWND);
    	const HMODULE hModule = GetModuleHandle(TEXT("user32.dll"));
    	if (hModule)
    	{
    		const fnTypeEnableNCScaling fnEnableNCScaling = (fnTypeEnableNCScaling)GetProcAddress(hModule, "EnableNonClientDpiScaling");
    		if (fnEnableNCScaling)
    		{
    			fnEnableNCScaling(this->GetSafeHwnd());
    		}
    	}
    	return CFrameWnd::OnNcCreate(lpCreateStruct);
    }


    参考サイト: https://msdn.microsoft.com/en-us/library/windows/desktop/mt748621%28v=vs.85%29.aspx

    • 編集済み kenjinoteMVP 2017年5月8日 8:52
    • 回答としてマーク 安沢 2017年5月8日 10:44
    2017年5月8日 8:20

すべての返信

  • 安沢 さん、

    Visual C++ の経験はなく、もっぱら VB.NET ですが、プロジェクトに app.manifest を追加して対応しています。
    Windows 10 Creators Update、プライマリーモニター 125 % (120 DPI)、セカンダリー 100 % (96 DPI) で気持ちよくスケーリングされました。
    ちょっと古いですが次の記事をご参照ください。

    アプリの高DPI(High DPI)対応について 第3回 ~ マニフェストでアプリのDPI対応レベルを変更する ~
    https://blogs.msdn.microsoft.com/ttanaka/2014/08/22/dpihigh-dpi-3-12503/

    _____________
    北窓舎:芦田

    2017年5月8日 8:19
  • マニフェストで「モニタごとの高い DPI 認識(Per monitor DPI Aware)」と設定されたウィンドウを、
    異なる DPI 設定のディスプレイ間で移動させるとき、タイトルバーやメニューは、自動的にスケーリングされないようです。スケーリングさせるには、フレーム ウィンドウ(トップレベル ウィンドウ)に WM_NCCREATE メッセージが来た時に、EnableNonClientDpiScaling 関数を呼び出す必要があるようです。

    // メッセージマップ
    BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
    	ON_WM_NCCREATE()
    END_MESSAGE_MAP()
    
    BOOL CMainFrame::OnNcCreate(LPCREATESTRUCT lpCreateStruct)
    {
    	typedef BOOL(WINAPI*fnTypeEnableNCScaling)(HWND);
    	const HMODULE hModule = GetModuleHandle(TEXT("user32.dll"));
    	if (hModule)
    	{
    		const fnTypeEnableNCScaling fnEnableNCScaling = (fnTypeEnableNCScaling)GetProcAddress(hModule, "EnableNonClientDpiScaling");
    		if (fnEnableNCScaling)
    		{
    			fnEnableNCScaling(this->GetSafeHwnd());
    		}
    	}
    	return CFrameWnd::OnNcCreate(lpCreateStruct);
    }


    参考サイト: https://msdn.microsoft.com/en-us/library/windows/desktop/mt748621%28v=vs.85%29.aspx

    • 編集済み kenjinoteMVP 2017年5月8日 8:52
    • 回答としてマーク 安沢 2017年5月8日 10:44
    2017年5月8日 8:20
  • 情報が古いです。質問者さんの

    マニュフェストツール→入出力のDPI認識を「モニタごとの高いDPI認識」に設定

    を行うとリンクオプションに

    /manifestinput:"C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Tools\MSVC\14.10.25017\Include\Manifest\PerMonitorHighDPIAware.manifest"

    が追加され結果的にマニフェストが追加されますので、その記事は役に立ちません。

    2017年5月8日 8:46
  • お教えいただいた方法で可能でした。

    ありがとうございます。

    2017年5月8日 10:44