マウス位置でウィンドウ移動(MDI対応)【AutoHotKey】

前回通常ウィンドウの移動・リサイズを書きましたが、今回はMDIウィンドウ対応パターンです。
前回、よく考えたら「WinGet,hWnd,ID,A」じゃ駄目でしたね。
まぁ実際使用する際はアクティブウィンドウの事が多いけど、マウス位置のウィンドウハンドルを取得しなきゃ正確じゃないですよね。
その辺も含め手直ししました。


まず、MDI子ウィンドウのハンドルを取得するのに、こちらのブログのユーザー定義関数を使用します。


http://d.hatena.ne.jp/eamat/20050827

;-----------------------------------------------------------------
;  マウス下Windowのハンドル返す(MDIアプリ上の 子ウィンドウ対応)
;       対象: AHK v1.0.34以降
;   in  def_cmode   元のマウスモード 1:Relative(デフォルト) 0:Screen
;   戻り値 : Window ID (0:該当無し)
;-----------------------------------------------------------------
getMousePosWindowEx(def_cmode = 1)
{
    CoordMode,Mouse,Screen
    MouseGetPos, mx,my

    ;--- タイトルバーを持つウィンドウを探して返す ---
    ;マウス座標値直下のコントロールハンドル取得
    hWnd := DllCall("WindowFromPoint",Int,mx, Int,my, UInt)
    Loop
    {
        ;指定ハンドルのコントロールはタイトルバーもちウィンドウか?
        ;※ メニュー系ポップアップウィンドウは対象外
        ; 2005.09.16 Excel2000対応
        WinGet,ExStyle,ExStyle,ahk_id %hWnd%
        if (ExStyle & 0x100 = 0x100)                ;WS_EX_WINDOWEDGE
        {
            WinGet,Style,Style,ahk_id %hWnd%
            if (Style & 0x00C00000 = 0x00C00000)     ;WS_CAPTION
            Break
        }
        ;違うなら 1階層上のコントロールを検索
        hWnd := DllCall("GetParent",UInt,hWnd, UInt) ;親ウィンドウ検索
        ifEqual,hWnd,0,  Break                  ;Error
    }

    ;--- マウスの座標モードを戻しとく ---
    ;(組込み変数で CoordMode,Mouseの状態が取れないので苦肉の策)
    ifEqual def_cmode,1,    CoordMode,Mouse,Relative
    return hWnd
}


こちらが、本編。

;○MDI対応ウィンドウ移動
^!LButton::
	hWnd := getMousePosWindowEx()
	hMdiClient := DllCall("GetParent",UInt,hWnd)
	WinSet,Top,,ahk_id %hWnd%
	WinActivate,ahk_id %hWnd%
	if (hMdiClient) {
		WinGetClass,wCls,ahk_id %hWnd%
		getMDIErrMarginPixel(wCls,ex,ey)
		WinGetPos,x1,y1,w1,h1,ahk_id %hMdiClient%
		WinGetPos,x2,y2,w2,h2,ahk_id %hWnd%
		CoordMode,Mouse,Screen
		MouseGetPos,x4,y4
		Loop {
			MouseGetPos,x3,y3
			WinMove ahk_id %hWnd%,,(x2 - x1) - (x4 - x3) - ex, (y2 - y1) - (y4 - y3) - ey
			If GetKeyState("LButton","P") = 0 {
				return
			}
		}
	} else {
		MouseGetPos,x4,y4
		Loop {
			CoordMode,Mouse,Screen
			MouseGetPos,x3,y3
			WinMove ahk_id %hWnd%,,x3 - x4, y3 - y4
			If GetKeyState("LButton","P") = 0 {
				return
			}
		}
	}
return


;○MDI対応ウィンドウリサイズ
^+LButton::
	hWnd := getMousePosWindowEx()
	hMdiClient := DllCall("GetParent",UInt,hWnd) 
	WinGetPos,x2,y2,w2,h2,ahk_id %hWnd%
	WinSet,Top,,ahk_id %hWnd%
	WinActivate,ahk_id %hWnd%
	if (hMdiClient) {
		WinGetClass,wCls,ahk_id %hWnd%
		getMDIErrMarginPixel(wCls,ex,ey)
		WinGetPos,x1,y1,w1,h1,ahk_id %hMdiClient%
		CoordMode,Mouse,Screen
		MouseGetPos,x3,y3
		Loop {
			MouseGetPos,x4,y4
			WinMove ahk_id %hWnd%,,(x2 - x1) - ex, (y2 - y1) - ey, w2 - (x3 - x4), h2 - (y3 - y4)
			If GetKeyState("LButton","P") = 0 {
				return
			}
		}
	} else {
		CoordMode,Mouse,Screen
		MouseGetPos,x3,y3
		Loop {
			MouseGetPos,x4,y4
			WinMove ahk_id %hWnd%,,x2, y2, w2 - (x3 - x4), h2 - (y3 - y4)
			If GetKeyState("LButton","P") = 0 {
				return
			}
		}
	}
return


;◯MDI子ウィンドウ座標の誤差調整(苦肉の策)
getMDIErrMarginPixel(class_name, ByRef ex, ByRef ey) {
	if (class_name == "OpusApp") {
		ex := 2
		ey := 2
	} else if (class_name == "XLMAIN") {
		ex := 3
		ey := 3
	} else if (class_name == "SteinbergWindowClass") {
		ex := 2
		ey := 0
	} else if (class_name == "SteinbergMDIWindowClass") {
		ex := 2
		ey := 2
	} else {
		ex := 0
		ey := 0
	}
}


Ctrl+Alt+左ボタンというコマンドは前回と同じ。
MDIウィンドウかどうか自動的に判断して、通常のウィンドウの場合、MDI子ウィンドウの場合といったように自動的に処理が分かれるようにしました。
Ctrl+Alt+中ボタンでMDI用といった感じでも良かったのですが、WinMoverに慣れてしまってるので、WinMover風に。


若干変更してますが、前回のプログラムが組み込んであり、外側のIF文のElse以下が前回のプログラムになります。


態々getMDIErrMarginPixelと関数を用意してますが、これは苦肉の策です(笑)
組み込み関数の仕様なのか、自分の記述がいけないのか、ウィンドウによって数ピクセル程座標の誤差が生じる場合があるんですよね。
それで、どうしても生じてしまうウィンドウは、予めgetMDIErrMarginPixel関数に登録しておき、ずれを補正するようにしてます。
めちゃくちゃ腑に落ちないですけど(笑)



APIで学ぶWindowsプログラミング (日経BPパソコンベストムック)

APIで学ぶWindowsプログラミング (日経BPパソコンベストムック)