こんばんはー、モノトーンです。
(ネタバレ含む余談) 新世界樹やってて、フレドリカちゃんと仲良くなりまして、彼女のことをリッキィって呼ぶようになりました。あー、リッキィかっこかわいいなぁ、っていう。あ、新世界樹やってない人わかりませんねww いや、かわゆすよ。あ、本題行きましょうか。
概要
- Windows 8.1 のスケーリングをいじってて発見したこと。
- [おまけ] スケーリングの関数群
1. Windows 8.1 のスケーリングをいじってて発見したこと。
Windows 8.1 のスケーリング用に追加された API で Raw Dpi (デバイスの実際の dots per inch) が取れるようになりました。
STDAPI GetDpiForMonitor( _In_ HMONITOR hmonitor, _In_ MONITOR_DPI_TYPE dpiType, _Out_ UINT *dpiX, _Out_ UINT *dpiY );
こちらの API です。これにモニターハンドル HMONITOR、MDT_RAW_DPI
を指定し、DPI を取得すると、ほぼ物理デバイスに一致する DPI を取得することが可能です。ちなみに、MDT_EFFECTIVE_DPI
を指定すると DWM (Desktop Window Manager) が使う DPI を取得することができます。
2. [おまけ] スケーリングの関数群
スケーリングの関数について取り出してまとめておきます。XP 時代からの名残りの API もあるので、過去の OS に対応するならそちらの API も併用しないといけないかも。(このあたりは要検証)
/* sdkddkver.h ll. 38-40 */
#define _WIN32_WINNT_WIN7 0x0601
#define _WIN32_WINNT_WIN8 0x0602
#define _WIN32_WINNT_WINBLUE 0x0603
/* WinUser.h ll. 2355-2357 */
#if( WINVER >= 0x0601 )
#define WM_DPICHANGED 0x02E0
#endif
/* WinUser.h ll. 13709-13725 */
#if( _WIN32_WINNT >= 0x0600 )
#define USER_DEFAULT_SCREEN_DPI 96
WINUSERAPI BOOL WINAPI SetProcessDPIAware( VOID );
WINUSERAPI BOOL WINAPI IsProcessDPIAware( VOID );
#endif
/* WinUser.h ll. 8961-8976 */
#if( WINVER >= 0x0600 )
WINUSERAPI BOOL WINAPI LogicalToPhysicalPoint( _In_ HWND hWnd, _Inout_ LPPOINT lpPoint );
WINUSERAPI BOOL WINAPI PhysicalToLogicalPoint( _In_ HWND hWnd, _Inout_ LPPOINT lpPoint );
#endif
/* WinUser.h ll. 8978-8993 */
#if( WINVER >= 0x0603 )
WINUSERAPI BOOL WINAPI LogicalToPhysicalPointForPerMonitorDPI( _In_opt_ HWND hWnd, _Inout_ LPPOINT lpPoint );
WINUSERAPI BOOL WINAPI PhysicalToLogicalPointForPerMonitorDPI( _In_opt_ HWND hWnd, _Inout_ LPPOINT lpPoint );
#endif
/* ShellScalingApi.h ll. 50-90 */
#if (NTDDI_VERSION >= NTDDI_BLUE)
STDAPI GetScaleFactorForMonitor(_In_ HMONITOR hMon, _Out_ DEVICE_SCALE_FACTOR *pScale);
STDAPI RegisterScaleChangeEvent(_In_ HANDLE hEvent, _Out_ DWORD_PTR *pdwCookie);
STDAPI UnregisterScaleChangeEvent(_In_ DWORD_PTR dwCookie);
#ifndef DPI_ENUMS_DECLARED
typedef enum PROCESS_DPI_AWARENESS
{ PROCESS_DPI_UNAWARE = 0, PROCESS_SYSTEM_DPI_AWARE = 1, PROCESS_PER_MONITOR_DPI_AWARE = 2 } PROCESS_DPI_AWARENESS;
typedef enum MONITOR_DPI_TYPE
{ MDT_EFFECTIVE_DPI = 0, MDT_ANGULAR_DPI = 1, MDT_RAW_DPI = 2, MDT_DEFAULT = MDT_EFFECTIVE_DPI } MONITOR_DPI_TYPE;
#define DPI_ENUMS_DECLARED
#endif
STDAPI SetProcessDpiAwareness( _In_ PROCESS_DPI_AWARENESS value );
STDAPI GetProcessDpiAwareness( _In_opt_ HANDLE hprocess, _Out_ PROCESS_DPI_AWARENESS *value );
STDAPI GetDpiForMonitor( _In_ HMONITOR hmonitor, _In_ MONITOR_DPI_TYPE dpiType, _Out_ UINT *dpiX, _Out_ UINT *dpiY );
#endif
Windows 8.1 で新しく追加された API は「ShellScalingApi.h」に存在するので注意。これ、探すのに WinUser.h になかったので、「あれ? まだ使えないの?」って思ってしまったw
総括
実際、この API 呼び出すのは簡単なんですよね。アプリ側のレンダリング問題のほうがやばい。今、High Dpi 向けの native アプリケーションのクラス整備している段階なんですけど、やっぱりクラス整備するのは難しいですね。Dpi 絡みもうまく解決できるような設計にしないといけないので。まあがんばります。
以上。