DPI の違うモニターで思うようにウィンドウを動かせない問題について。
PowerToys の FancyZones をものすごく参考にしました。
Windows 10、AutoHotkey v2 です。
DwmGetWindowAttribute
size := 16 buf := Buffer(size) DWMWA_EXTENDED_FRAME_BOUNDS := 9 DllCall( "Dwmapi.dll\DwmGetWindowAttribute", "Ptr", hWindow, "UInt", DWMWA_EXTENDED_FRAME_BOUNDS, "Ptr", buf.Ptr, "Int", size ) left := NumGet(buf, 0, "Int") top := NumGet(buf, 4, "Int") right := NumGet(buf, 8, "Int") bottom := NumGet(buf, 12, "Int")
ウィンドウの周りの影の部分を除いた、見たままの枠を返してくれます。
ただし、DPI スケーリングを考慮しません。
SetThreadDpiAwarenessContext
DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 := -4 DllCall( "SetThreadDpiAwarenessContext", "Int", DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 )
あまり理解できていませんが、DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
にしました。
スケーリング前の座標を取得・使用するようになっており、DwmGetWindowAttribute
と相性が良いです。
また、Google Chrome などを思い通りに設置できるようになりました。
一方で、DPI Awareness が進んでいないプロセスのウィンドウについては、DPI の違うモニターで思い通りに移動できなくなります。
FancyZones では、DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE
未満のプロセスのウィンドウは境界内部に収まるように位置決めしているようでした。
一時的に DPI Awareness Context を変更する
あまり試せていないません。
適用できないものがあったら更新します・・・。
DwmGetWindowAttribute
とGetWindowRect
で影分のマージンを取得DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
- ウィンドウの移動
- 対象が
DPI_AWARENESS_CONTEXT_PER_MONITOR
以上のとき- 座標を指定して移動
- 対象が
DPI_AWARENESS_CONTEXT_SYSTEM_AWARE
以下のときDPI_AWARENESS_CONTEXT_SYSTEM_AWARE
に落とすGetScaleFactorForMonitor
などで得られる scale factor を使って影分のマージンをスケーリング(200% -> 0.5 倍)- 座標を指定して移動
DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
に戻す
- 対象が
最後に
FancyZones 様様です。
機能も増えてますね。
こちらの関数が使えたりするといいなあと思うのですが、よくわからないので AutoHotkey のスクリプトを書いています。