将 gui 停靠到变量 active window
docking gui to variable active window
我看到很多示例允许我打开程序并使用 winwait 然后 winexist 将 gui 停靠到它。
相反,我想做的是将我的图形用户界面停靠在 window 处于活动状态的位置。我尝试了一百万种方法,请帮忙。
(同样,当我确实将其附加到 window 时,它并没有完美地居中生成,因为在主脚本中,每当我尝试这样做时:
WinGetPos cX, cY, cW, cH, ahk_id %ChildhWnd%
offset1 := (mw / 2) - (cw /2)
当我按下热键时它抛出一个错误说 x y 无效
以下:我没有完全自己制作的脚本。我自己想出了数学,但从我不记得的资源中大量借用了。它几乎可以满足我的需求,但还不够
; #Warn ; Enable warnings to assist with detecting common errors.
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory.
#SingleInstance, force
^T::
hookProcAdr := RegisterCallback("HookProc")
hHook := SetWinEventHook(0x800B,0x800B,0,hookProcAdr,0,0,0) ; EVENT_OBJECT_LOCATIONCHANGE
Gui, +hwndChildhWnd +AlwaysOnTop
Gui, add, text,,some text in a small gui that will move around with a notepad window
Gui, add, Button,,Button
MainhWnd := WinExist() ;<---------what do i do here?
;--------------------------------------------;
;-----i commented my second problem here-----;
;--------------------------------------------;
WinGetPos, mX, mY, mW, mH, ahk_id %MainhWnd%
;WinGetPos cX, cY, cW, cH, ahk_id %ChildhWnd% <--------{why cant i put child window here-+
; |
offset2 := (mw / 2) ; - (cw /2) <------------------------with offset subtraction here <-+
cX := mX + offset2
cY := mY
Gui, show, x%cX% y%cY%
return
;-------------------;
;-----functions-----;
;-------------------;
HookProc(hWinEventHook, event, hwnd)
{
global MainHwnd, ChildhWnd
if (hwnd = MainHwnd)
{
SetWinDelay, -1
WinGetPos hX, hY, hW, hH, ahk_id %MainhWnd%
WinGetPos cX, cY, cW, cH, ahk_id %ChildhWnd%
offset1 := (hw / 2) - (cw / 2)
X := hX + offset1
Y := hY
WinMove ahk_id %ChildhWnd%,,X,Y,w%cw%,h%ch%
}
}
SetWinEventHook(eventMin, eventMax, hmodWinEventProc, lpfnWinEventProc, idProcess, idThread, dwFlags) {
DllCall("CoInitialize", "uint", 0)
return DllCall("SetWinEventHook", "uint", eventMin, "uint", eventMax, "uint", hmodWinEventProc, "uint", lpfnWinEventProc, "uint", idProcess, "uint", idThread, "uint", dwFlags)
}```
那里几乎没有错误,window 切换部分必须添加一些额外的逻辑和魔法。
此外,我打算将此脚本从旧语法转换为使用现代表达式语法。
所以首先,在这一行 MainhWnd := WinExist()
。
我们想从当前活动的 window 开始,所以让我们像这样使用 A
(docs):
MainhWnd := WinExist("A")
然后这部分代码:
WinGetPos, mX, mY, mW, mH, ahk_id %MainhWnd%
;WinGetPos cX, cY, cW, cH, ahk_id %ChildhWnd% <--------{why cant i put child window here-+
; |
offset2 := (mw / 2) ; - (cw /2) <------------------------with offset subtraction here <-+
cX := mX + offset2
cY := mY
Gui, show, x%cX% y%cY%
这里你无法获取child window的位置,因为它还不存在。
如果 gui 是 pre-determined,你可以知道它的宽度是多少。
如果它的宽度没有改变,也没有必要在每次 window 移动时都获取它的宽度。
所以也许只需将预定宽度存储在一个变量中就可以了。
如果无法预先确定宽度,例如,您可以快速显示图形用户界面并抓住它的宽度,然后立即移动它。
ChildWindowWidthHalf := 349/2
WinGetPos, mX, mY, mW, , % "ahk_id " MainhWnd
offset2 := (mw / 2) - ChildWindowWidthHalf
cX := mX + offset2
cY := mY
Gui, show, % "x" cX " y" cY
在这里您还可以看到我第一次出现放弃旧语法并切换到 expression,例如这里连接一个字符串和一个变量:
ahk_id %MainhWnd%
→ % "ahk_id " MainhWnd
基本上,一个 %
后跟一个 space,强制命令的参数计算一个表达式,而不是期望一个遗留文本参数。
如果这一切对您来说都是全新的,我建议您查看文档中的 this 页面。
然后是HookProc
函数中的问题:
HookProc(hWinEventHook, event, hwnd)
{
global MainHwnd, ChildhWnd
if (hwnd = MainHwnd)
{
SetWinDelay, -1
WinGetPos hX, hY, hW, hH, ahk_id %MainhWnd%
WinGetPos cX, cY, cW, cH, ahk_id %ChildhWnd%
offset1 := (hw / 2) - (cw / 2)
X := hX + offset1
Y := hY
WinMove ahk_id %ChildhWnd%,,X,Y,w%cw%,h%ch%
}
}
先到这里
global MainHwnd, ChildhWnd
我们可以放弃变量 MainHwnd
,因为它对我们没有用。我们希望 MainHwnd
成为当前活动的 window,而不是我们在脚本开始时确定的那个 window。
并且让我们添加这个变量 ChildWindowWidthHalf
.
if (hwnd = MainHwnd)
这张支票对我们来说也没有意义,应该完全删除。
虽然,我强烈建议用这个 if (hwnd = WinActive("A"))
替换它,这样你就可以过滤掉与当前活动 window 移动无关的消息。
这个这个:
SetWinDelay, -1
没有理由每次都设置它,我们可以在脚本顶部设置一次。
在此:
WinGetPos hX, hY, hW, hH, ahk_id %MainhWnd%
WinGetPos cX, cY, cW, cH, ahk_id %ChildhWnd%
offset1 := (hw / 2) - (cw / 2)
我们对 MainhWnd
不感兴趣,如上所述,让我们用直接从 windows 消息中获得的 hwnd 替换它。传递给 HookProc
函数的第三个参数的 hwnd。
WinGetPos, hX, hY, hW, , % "ahk_id " hwnd
然后我们就可以摆脱获取 child window 的位置。没用。
然后让我们也切换偏移量以再次使用预定的 child window 宽度:
offset1 := (hW / 2) - ChildWindowWidthHalf
然后进入WinMove
command:
WinMove ahk_id %ChildhWnd%,,X,Y,w%cw%,h%ch%
它被错误地使用了。宽度和高度参数前面没有 w
和 h
字符。而且我们没有理由改变 window 的宽度或高度。它应该是这样的:
WinMove, % "ahk_id " ChildhWnd, , % X, % Y
And now you should have a working script as follows:
SetWinDelay, -1
hookProcAdr := RegisterCallback("HookProc")
hHook := SetWinEventHook(0x800B,0x800B,0,hookProcAdr,0,0,0) ; EVENT_OBJECT_LOCATIONCHANGE
Gui, +hwndChildhWnd +AlwaysOnTop
Gui, add, text, , % "some text in a small gui that will move around with a notepad window"
Gui, add, Button, , Button
MainhWnd := WinExist("A")
ChildWindowWidthHalf := 349/2
WinGetPos, mX, mY, mW, , % "ahk_id " MainhWnd
offset2 := (mw / 2) - ChildWindowWidthHalf
cX := mX + offset2
cY := mY
Gui, show, % "x" cX " y" cY
return
HookProc(hWinEventHook, event, hwnd)
{
global ChildhWnd, ChildWindowWidthHalf
if (hwnd = WinActive("A"))
{
WinGetPos, hX, hY, hW, , % "ahk_id " hwnd
offset1 := (hW / 2) - ChildWindowWidthHalf
X := hX + offset1
Y := hY
WinMove, % "ahk_id " ChildhWnd, , % X, % Y
}
}
SetWinEventHook(eventMin, eventMax, hmodWinEventProc, lpfnWinEventProc, idProcess, idThread, dwFlags)
{
DllCall("CoInitialize", "uint", 0)
return DllCall("SetWinEventHook", "uint", eventMin, "uint", eventMax, "uint", hmodWinEventProc, "uint", lpfnWinEventProc, "uint", idProcess, "uint", idThread, "uint", dwFlags)
}
但它只会在 window 移动时更新 child window 的位置,而不是当您激活另一个 window.[=75= 时]
因此,您还需要 window 获得焦点。
这可以通过 shell 钩子很好地完成。
我将在代码中用注释来解释这一点,这里是相关文档链接:
RegisterShellHookWindow, RegisterWindowMessage, OnMessage
;register our script to receive shell messages
DllCall("RegisterShellHookWindow", UInt, ChildhWnd)
;define a unique message which we'll monitor
MsgId := DllCall("RegisterWindowMessage", Str, "SHELLHOOK")
;monitors the the message and fires the our
;user defined function "MsgMonitor"
OnMessage(MsgId, "MsgMonitor")
MsgMonitor(wParam, lParam) ;wParam = message, lParam = hwnd
{
;HSHELL_WINDOWACTIVATED = 0x04
;HSHELL_RUDEAPPACTIVATED = 0x8004
if (wParam = 0x8004 || wParam = 0x8004)
;we can borrow the HookProc function so we dont
;need to type the same code again
;first two parameters arent needed
HookProc("whatever", "whatever", lParam)
}
And now finally you should arrive at a finished product like this:
SetWinDelay, -1
hookProcAdr := RegisterCallback("HookProc")
hHook := SetWinEventHook(0x800B,0x800B,0,hookProcAdr,0,0,0) ; EVENT_OBJECT_LOCATIONCHANGE
Gui, +hwndChildhWnd +AlwaysOnTop
Gui, add, text, , % "some text in a small gui that will move around with a notepad window"
Gui, add, Button, , Button
;register our script to receive shell messages
DllCall("RegisterShellHookWindow", UInt, ChildhWnd)
;define a unique message which we'll monitor
MsgId := DllCall("RegisterWindowMessage", Str, "SHELLHOOK")
;monitors the the message and fires the our
;user defined function "MsgMonitor"
OnMessage(MsgId, "MsgMonitor")
MainhWnd := WinExist("A")
ChildWindowWidthHalf := 349/2
WinGetPos, mX, mY, mW, , % "ahk_id " MainhWnd
offset2 := (mw / 2) - ChildWindowWidthHalf
cX := mX + offset2
cY := mY
Gui, show, % "x" cX " y" cY
return
MsgMonitor(wParam, lParam) ;wParam = message, lParam = hwnd
{
;HSHELL_WINDOWACTIVATED = 0x04
;HSHELL_RUDEAPPACTIVATED = 0x8004
if (wParam = 0x8004 || wParam = 0x8004)
;we can borrow the HookProc function so we dont
;need to type the same code again
;first two parameters arent needed
HookProc("whatever", "whatever", lParam)
}
HookProc(hWinEventHook, event, hwnd)
{
global ChildhWnd, ChildWindowWidthHalf
if (hwnd = WinActive("A"))
{
WinGetPos, hX, hY, hW, , % "ahk_id " hwnd
offset1 := (hW / 2) - ChildWindowWidthHalf
X := hX + offset1
Y := hY
WinMove, % "ahk_id " ChildhWnd, , % X, % Y
}
}
SetWinEventHook(eventMin, eventMax, hmodWinEventProc, lpfnWinEventProc, idProcess, idThread, dwFlags)
{
DllCall("CoInitialize", "uint", 0)
return DllCall("SetWinEventHook", "uint", eventMin, "uint", eventMax, "uint", hmodWinEventProc, "uint", lpfnWinEventProc, "uint", idProcess, "uint", idThread, "uint", dwFlags)
}
我看到很多示例允许我打开程序并使用 winwait 然后 winexist 将 gui 停靠到它。
相反,我想做的是将我的图形用户界面停靠在 window 处于活动状态的位置。我尝试了一百万种方法,请帮忙。
(同样,当我确实将其附加到 window 时,它并没有完美地居中生成,因为在主脚本中,每当我尝试这样做时:
WinGetPos cX, cY, cW, cH, ahk_id %ChildhWnd%
offset1 := (mw / 2) - (cw /2)
当我按下热键时它抛出一个错误说 x y 无效
以下:我没有完全自己制作的脚本。我自己想出了数学,但从我不记得的资源中大量借用了。它几乎可以满足我的需求,但还不够
; #Warn ; Enable warnings to assist with detecting common errors.
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory.
#SingleInstance, force
^T::
hookProcAdr := RegisterCallback("HookProc")
hHook := SetWinEventHook(0x800B,0x800B,0,hookProcAdr,0,0,0) ; EVENT_OBJECT_LOCATIONCHANGE
Gui, +hwndChildhWnd +AlwaysOnTop
Gui, add, text,,some text in a small gui that will move around with a notepad window
Gui, add, Button,,Button
MainhWnd := WinExist() ;<---------what do i do here?
;--------------------------------------------;
;-----i commented my second problem here-----;
;--------------------------------------------;
WinGetPos, mX, mY, mW, mH, ahk_id %MainhWnd%
;WinGetPos cX, cY, cW, cH, ahk_id %ChildhWnd% <--------{why cant i put child window here-+
; |
offset2 := (mw / 2) ; - (cw /2) <------------------------with offset subtraction here <-+
cX := mX + offset2
cY := mY
Gui, show, x%cX% y%cY%
return
;-------------------;
;-----functions-----;
;-------------------;
HookProc(hWinEventHook, event, hwnd)
{
global MainHwnd, ChildhWnd
if (hwnd = MainHwnd)
{
SetWinDelay, -1
WinGetPos hX, hY, hW, hH, ahk_id %MainhWnd%
WinGetPos cX, cY, cW, cH, ahk_id %ChildhWnd%
offset1 := (hw / 2) - (cw / 2)
X := hX + offset1
Y := hY
WinMove ahk_id %ChildhWnd%,,X,Y,w%cw%,h%ch%
}
}
SetWinEventHook(eventMin, eventMax, hmodWinEventProc, lpfnWinEventProc, idProcess, idThread, dwFlags) {
DllCall("CoInitialize", "uint", 0)
return DllCall("SetWinEventHook", "uint", eventMin, "uint", eventMax, "uint", hmodWinEventProc, "uint", lpfnWinEventProc, "uint", idProcess, "uint", idThread, "uint", dwFlags)
}```
那里几乎没有错误,window 切换部分必须添加一些额外的逻辑和魔法。
此外,我打算将此脚本从旧语法转换为使用现代表达式语法。
所以首先,在这一行 MainhWnd := WinExist()
。
我们想从当前活动的 window 开始,所以让我们像这样使用 A
(docs):
MainhWnd := WinExist("A")
然后这部分代码:
WinGetPos, mX, mY, mW, mH, ahk_id %MainhWnd%
;WinGetPos cX, cY, cW, cH, ahk_id %ChildhWnd% <--------{why cant i put child window here-+
; |
offset2 := (mw / 2) ; - (cw /2) <------------------------with offset subtraction here <-+
cX := mX + offset2
cY := mY
Gui, show, x%cX% y%cY%
这里你无法获取child window的位置,因为它还不存在。
如果 gui 是 pre-determined,你可以知道它的宽度是多少。
如果它的宽度没有改变,也没有必要在每次 window 移动时都获取它的宽度。
所以也许只需将预定宽度存储在一个变量中就可以了。
如果无法预先确定宽度,例如,您可以快速显示图形用户界面并抓住它的宽度,然后立即移动它。
ChildWindowWidthHalf := 349/2
WinGetPos, mX, mY, mW, , % "ahk_id " MainhWnd
offset2 := (mw / 2) - ChildWindowWidthHalf
cX := mX + offset2
cY := mY
Gui, show, % "x" cX " y" cY
在这里您还可以看到我第一次出现放弃旧语法并切换到 expression,例如这里连接一个字符串和一个变量:
ahk_id %MainhWnd%
→ % "ahk_id " MainhWnd
基本上,一个 %
后跟一个 space,强制命令的参数计算一个表达式,而不是期望一个遗留文本参数。
如果这一切对您来说都是全新的,我建议您查看文档中的 this 页面。
然后是HookProc
函数中的问题:
HookProc(hWinEventHook, event, hwnd)
{
global MainHwnd, ChildhWnd
if (hwnd = MainHwnd)
{
SetWinDelay, -1
WinGetPos hX, hY, hW, hH, ahk_id %MainhWnd%
WinGetPos cX, cY, cW, cH, ahk_id %ChildhWnd%
offset1 := (hw / 2) - (cw / 2)
X := hX + offset1
Y := hY
WinMove ahk_id %ChildhWnd%,,X,Y,w%cw%,h%ch%
}
}
先到这里
global MainHwnd, ChildhWnd
我们可以放弃变量 MainHwnd
,因为它对我们没有用。我们希望 MainHwnd
成为当前活动的 window,而不是我们在脚本开始时确定的那个 window。
并且让我们添加这个变量 ChildWindowWidthHalf
.
if (hwnd = MainHwnd)
这张支票对我们来说也没有意义,应该完全删除。
虽然,我强烈建议用这个 if (hwnd = WinActive("A"))
替换它,这样你就可以过滤掉与当前活动 window 移动无关的消息。
这个这个:
SetWinDelay, -1
没有理由每次都设置它,我们可以在脚本顶部设置一次。
在此:
WinGetPos hX, hY, hW, hH, ahk_id %MainhWnd%
WinGetPos cX, cY, cW, cH, ahk_id %ChildhWnd%
offset1 := (hw / 2) - (cw / 2)
我们对 MainhWnd
不感兴趣,如上所述,让我们用直接从 windows 消息中获得的 hwnd 替换它。传递给 HookProc
函数的第三个参数的 hwnd。
WinGetPos, hX, hY, hW, , % "ahk_id " hwnd
然后我们就可以摆脱获取 child window 的位置。没用。
然后让我们也切换偏移量以再次使用预定的 child window 宽度:
offset1 := (hW / 2) - ChildWindowWidthHalf
然后进入WinMove
command:
WinMove ahk_id %ChildhWnd%,,X,Y,w%cw%,h%ch%
它被错误地使用了。宽度和高度参数前面没有 w
和 h
字符。而且我们没有理由改变 window 的宽度或高度。它应该是这样的:
WinMove, % "ahk_id " ChildhWnd, , % X, % Y
And now you should have a working script as follows:
SetWinDelay, -1
hookProcAdr := RegisterCallback("HookProc")
hHook := SetWinEventHook(0x800B,0x800B,0,hookProcAdr,0,0,0) ; EVENT_OBJECT_LOCATIONCHANGE
Gui, +hwndChildhWnd +AlwaysOnTop
Gui, add, text, , % "some text in a small gui that will move around with a notepad window"
Gui, add, Button, , Button
MainhWnd := WinExist("A")
ChildWindowWidthHalf := 349/2
WinGetPos, mX, mY, mW, , % "ahk_id " MainhWnd
offset2 := (mw / 2) - ChildWindowWidthHalf
cX := mX + offset2
cY := mY
Gui, show, % "x" cX " y" cY
return
HookProc(hWinEventHook, event, hwnd)
{
global ChildhWnd, ChildWindowWidthHalf
if (hwnd = WinActive("A"))
{
WinGetPos, hX, hY, hW, , % "ahk_id " hwnd
offset1 := (hW / 2) - ChildWindowWidthHalf
X := hX + offset1
Y := hY
WinMove, % "ahk_id " ChildhWnd, , % X, % Y
}
}
SetWinEventHook(eventMin, eventMax, hmodWinEventProc, lpfnWinEventProc, idProcess, idThread, dwFlags)
{
DllCall("CoInitialize", "uint", 0)
return DllCall("SetWinEventHook", "uint", eventMin, "uint", eventMax, "uint", hmodWinEventProc, "uint", lpfnWinEventProc, "uint", idProcess, "uint", idThread, "uint", dwFlags)
}
但它只会在 window 移动时更新 child window 的位置,而不是当您激活另一个 window.[=75= 时]
因此,您还需要 window 获得焦点。
这可以通过 shell 钩子很好地完成。
我将在代码中用注释来解释这一点,这里是相关文档链接:
RegisterShellHookWindow, RegisterWindowMessage, OnMessage
;register our script to receive shell messages
DllCall("RegisterShellHookWindow", UInt, ChildhWnd)
;define a unique message which we'll monitor
MsgId := DllCall("RegisterWindowMessage", Str, "SHELLHOOK")
;monitors the the message and fires the our
;user defined function "MsgMonitor"
OnMessage(MsgId, "MsgMonitor")
MsgMonitor(wParam, lParam) ;wParam = message, lParam = hwnd
{
;HSHELL_WINDOWACTIVATED = 0x04
;HSHELL_RUDEAPPACTIVATED = 0x8004
if (wParam = 0x8004 || wParam = 0x8004)
;we can borrow the HookProc function so we dont
;need to type the same code again
;first two parameters arent needed
HookProc("whatever", "whatever", lParam)
}
And now finally you should arrive at a finished product like this:
SetWinDelay, -1
hookProcAdr := RegisterCallback("HookProc")
hHook := SetWinEventHook(0x800B,0x800B,0,hookProcAdr,0,0,0) ; EVENT_OBJECT_LOCATIONCHANGE
Gui, +hwndChildhWnd +AlwaysOnTop
Gui, add, text, , % "some text in a small gui that will move around with a notepad window"
Gui, add, Button, , Button
;register our script to receive shell messages
DllCall("RegisterShellHookWindow", UInt, ChildhWnd)
;define a unique message which we'll monitor
MsgId := DllCall("RegisterWindowMessage", Str, "SHELLHOOK")
;monitors the the message and fires the our
;user defined function "MsgMonitor"
OnMessage(MsgId, "MsgMonitor")
MainhWnd := WinExist("A")
ChildWindowWidthHalf := 349/2
WinGetPos, mX, mY, mW, , % "ahk_id " MainhWnd
offset2 := (mw / 2) - ChildWindowWidthHalf
cX := mX + offset2
cY := mY
Gui, show, % "x" cX " y" cY
return
MsgMonitor(wParam, lParam) ;wParam = message, lParam = hwnd
{
;HSHELL_WINDOWACTIVATED = 0x04
;HSHELL_RUDEAPPACTIVATED = 0x8004
if (wParam = 0x8004 || wParam = 0x8004)
;we can borrow the HookProc function so we dont
;need to type the same code again
;first two parameters arent needed
HookProc("whatever", "whatever", lParam)
}
HookProc(hWinEventHook, event, hwnd)
{
global ChildhWnd, ChildWindowWidthHalf
if (hwnd = WinActive("A"))
{
WinGetPos, hX, hY, hW, , % "ahk_id " hwnd
offset1 := (hW / 2) - ChildWindowWidthHalf
X := hX + offset1
Y := hY
WinMove, % "ahk_id " ChildhWnd, , % X, % Y
}
}
SetWinEventHook(eventMin, eventMax, hmodWinEventProc, lpfnWinEventProc, idProcess, idThread, dwFlags)
{
DllCall("CoInitialize", "uint", 0)
return DllCall("SetWinEventHook", "uint", eventMin, "uint", eventMax, "uint", hmodWinEventProc, "uint", lpfnWinEventProc, "uint", idProcess, "uint", idThread, "uint", dwFlags)
}