DPIスケーリングは、オペレーティングシステムまたはアプリケーションによって実行される機能で、ディスプレイの「ドット/インチ」設定に比例してコンテンツの視覚的サイズを拡大する。一般的には、ディスプレイ解像度の異なるシステム上でも同じ物理的サイズでコンテンツを表示したり、少なくとも非常に高解像度のディスプレイでも使用できるようにする。コンテンツをより大きく快適に読むために、DPI設定を大きくすることがある。
A_ScreenDPI typically returns the DPI setting which the primary display had at the time the script started. This is known as the "system DPI", although processes started at different times can have different values.
AutoHotkeyに関連するDPIスケーリングには2種類あります:GUI DPIスケーリングとOS DPIスケーリング。
自動スケーリングは、デフォルトでGuiとGuiControlのメソッド/プロパティによって実行されるため、ハードコードされた位置、サイズ、マージンを持つGUIスクリプトは、高DPIスクリーン上で適切にスケーリングされる傾向があります。これがスクリプトの邪魔になる場合、あるいはスクリプトが独自のスケーリングを行う場合は、自動スケーリングを無効にすることができる。詳細は-DPIScaleオプションを参照。
DPIを意識していないアプリケーションの場合、オペレーティング・システムは、特定のシステム関数に渡されたり返されたりする座標を自動的にスケーリングします。This type of scaling typically affects AutoHotkey in two scenarios:
The exact scaling done depends on which system function is being called, the DPI awareness of the script and potentially the DPI awareness of the target window.
Windows 8.1以降では、セカンダリスクリーンは異なるDPI設定を持つことができ、「モニターごとのDPI対応」アプリケーションは、ウィンドウがスクリーン間を移動するときに動的に適応し、現在表示されているスクリーンのDPIに従ってウィンドウを拡大縮小することが期待されています。
モニターごとのDPIを意識しないアプリケーションの場合、システムはビットマップのスケーリングを実行し、ウィンドウがスクリーン間を移動するときにサイズを変更できるようにします。そして、アプリケーションが期待するグローバルDPI設定にスケーリングされた座標とサイズを報告することで、アプリケーションからこれを隠します。例えば、11インチの4Kスクリーンでは、96dpi(100%)で表示するようにデザインされたGUIはほとんど使用不可能だが、200%アップスケールすれば使用可能になる。
AutoHotkey v2.0 is not designed to perform per-monitor scaling, and therefore has not been marked as per-monitor DPI-aware. これは、たとえば、GUIウィンドウを100 %のDPIを持つ大きな外部スクリーンと200 %のDPIを持つ小さなスクリーンの間で移動させるときに便利です。しかし、自動スケーリングには否定的な意味合いもある。
システムの自動スケーリングが機能するように、MoveWindowや GetWindowRectなどのシステム関数は、それらが受け取るか返す座標を自動的にスケーリングします。AutoHotkeyがこれらの関数を使用して外部ウィンドウを操作する場合、座標がプライマリスクリーン上にない場合、しばしば予期しない結果が生じます。さらに混乱を招くことに、いくつかの関数は、スクリプトの最後にアクティブになったウィンドウがどのスクリーンに表示されていたかに基づいて座標をスケーリングする。
Windows 10のバージョン1607以降では、SetThreadDpiAwarenessContextシステム関数を使用して、実行時にプログラムのDPI認識設定を変更できます。たとえば、モニターごとのDPI認識を有効にすると、システムによるスケーリングが無効になるため、WinMoveや WinGetPosなどの組み込み関数は、DPIスケーリングに影響されずにピクセル単位の座標を受け取ったり返したりします。しかし、GUIが100 %のDPIのスクリーン用にサイズ調整され、その後200 %のDPIのスクリーンに移動した場合、自動的に調整されず、非常に使いにくくなる可能性があります。
モニターごとのDPI認識を有効にするには、通常DPIスケーリングの影響を受ける関数を使用する前に、以下の関数を呼び出します:
DllCall("SetThreadDpiAwarenessContext", "ptr", -3, "ptr")
Windows 10のバージョン1703以降では、-3を-4に置き換えて「Per Monitor v2」モードを有効にすることができます。これにより、ダイアログやメニュー、ツールチップなどの拡大縮小が可能になります。しかし、非クライアント領域(タイトル・バー)も拡大縮小されるため、スクリプトが(WM_DPICHANGEDメッセージに応答するなどして)調整するように設計されていない限り、ウィンドウのクライアント領域が小さくなりすぎる可能性があります。これは、GUIを作成する前にコンテキストを-3に設定し、ツールチップ、メニュー、ダイアログを作成する前に-4に設定することで回避できます。
When the window procedure for a window is called by the system, it automatically sets the current DPI awareness context to whichever context was in use when the window was created. The context for a new script thread therefore depends on whether it was launched directly from AutoHotkey's message loop or via a window procedure.
A per-monitor DPI aware GUI window is expected to adjust automatically when it receives a WM_DPICHANGED message. AutoHotkey v2.0 GUI windows do not respond to this message by default. If correctly implementing this type of dynamic scaling is too difficult, a simpler alternative is to temporarily disable per-monitor DPI awareness immediately prior to creating the GUI. 事例:
; Set the "system DPI aware" mode which is the default in AutoHotkey v2.0:
try dac := DllCall("SetThreadDpiAwarenessContext", 'ptr', -2, 'ptr')
; Create the GUI, which will permanently be "system DPI aware":
MyGui := Gui()
; Restore the previous mode for any subsequent function calls:
IsSet(dac) && DllCall("SetThreadDpiAwarenessContext", 'ptr', dac, 'ptr')
The additional lines have no effect if the OS does not support SetThreadDpiAwarenessContext or the program was already in system DPI aware mode.
If only some of the GUI's controls do not scale well, system DPI aware (or DPI unaware) controls can be hosted on a per-monitor DPI aware window. Mixed hosting must be enabled prior to creating the window (requires Windows 10 version 1803 or later):
; Create a GUI window which can host less-aware child windows:
try dhb := DllCall("SetThreadDpiHostingBehavior", 'int', 1)
MyGui := Gui()
IsSet(dhb) && DllCall("SetThreadDpiHostingBehavior", 'int', dhb)
; Add a "system DPI aware" control:
try dac := DllCall("SetThreadDpiAwarenessContext", 'ptr', -2, 'ptr')
MyListView := MyGui.AddListView()
IsSet(dac) && DllCall("SetThreadDpiAwarenessContext", 'ptr', dac, 'ptr')
Per-monitor DPI awareness can be enabled process-wide by setting the "dpiAware" and "dpiAwareness" elements in the compiled script's manifest (an embedded XML resource). For details of the proper use and effect of these settings, see Setting default awareness with the application manifest. For example, the manifest in AutoHotkey v2.0.19 includes the following:
<v3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings"
xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
<dpiAware>true</dpiAware>
<ws2:longPathAware>true</ws2:longPathAware>
</v3:windowsSettings>
As explained in the Microsoft documentation, it may be desirable to include both "dpiAware" and "dpiAwareness", which belong to different XML namespaces. As "longPathAware" and "dpiAwareness" belong to the same namespace, the XML can be optimized by moving some things around. The following enables per-monitor DPI awareness (v2 if available, otherwise v1):
<v3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings"> <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware> <dpiAwareness>PerMonitorV2</dpiAwareness> <longPathAware>true</longPathAware> </v3:windowsSettings>
The program's default DPI awareness can be overridden by compatibility settings, which can be set in the properties of an AutoHotkey executable file, in the properties of a shortcut file, or by setting the __COMPAT_LAYER environment variable to include the keyword DpiUnaware or the keyword HighDpiAware. Enabling DPI awareness using this method may have unwanted effects; in particular, MsgBox windows may not adjust automatically when moved between screens.