最近论坛上有人发个获取输入光标的新方法,想起了之前论坛里火热的输入法状态提示工具,于是上网找了些 AHK 代码进行缝合。
功能
测试环境:AHKV2.0-beta.6+,微软五笔
, shift
中英切换,其它没测试,有需要自己改代码就行。新方法基本支持所有窗口。
- 在输入光标附近显示当前中英状态(Win 10 1607及以上支持多屏不同缩放)
源码
- 需要 AHKV2.0-beta.6+
- exe x64
- 新版下载链接: 百度网盘 请输入提取码 提取码: jyg1
- hook 版下载链接: 百度网盘 请输入提取码 提取码: 5fb3
- 部分窗口需要管理员权限
新版
GetCaret(&X?, &Y?, &W?, &H?) {
; UIA2 caret
static IUIA := ComObject("{e22ad333-b25f-460c-83d0-0581107395c9}", "{34723aff-0c9d-49d0-9896-7ab52df8cd8a}")
try {
ComCall(8, IUIA, "ptr*", &FocusedEl:=0) ; GetFocusedElement
ComCall(16, FocusedEl, "int", 10024, "ptr*", &patternObject:=0), ObjRelease(FocusedEl) ; GetCurrentPattern. TextPatternElement2 = 10024
if patternObject {
ComCall(10, patternObject, "int*", &IsActive:=1, "ptr*", &caretRange:=0), ObjRelease(patternObject) ; GetCaretRange
ComCall(10, caretRange, "ptr*", &boundingRects:=0), ObjRelease(caretRange) ; GetBoundingRectangles
if (Rect := ComValue(0x2005, boundingRects)).MaxIndex() = 3 { ; VT_ARRAY | VT_R8
X:=Round(Rect[0]), Y:=Round(Rect[1]), W:=Round(Rect[2]), H:=Round(Rect[3])
return
}
}
}
; Acc caret
static _ := DllCall("LoadLibrary", "Str","oleacc", "Ptr")
try {
idObject := 0xFFFFFFF8 ; OBJID_CARET
if DllCall("oleacc\AccessibleObjectFromWindow", "ptr", WinExist("A"), "uint",idObject &= 0xFFFFFFFF
, "ptr",-16 + NumPut("int64", idObject == 0xFFFFFFF0 ? 0x46000000000000C0 : 0x719B3800AA000C81, NumPut("int64", idObject == 0xFFFFFFF0 ? 0x0000000000020400 : 0x11CF3C3D618736E0, IID := Buffer(16)))
, "ptr*", oAcc := ComValue(9,0)) = 0 {
x:=Buffer(4), y:=Buffer(4), w:=Buffer(4), h:=Buffer(4)
oAcc.accLocation(ComValue(0x4003, x.ptr, 1), ComValue(0x4003, y.ptr, 1), ComValue(0x4003, w.ptr, 1), ComValue(0x4003, h.ptr, 1), 0)
X:=NumGet(x,0,"int"), Y:=NumGet(y,0,"int"), W:=NumGet(w,0,"int"), H:=NumGet(h,0,"int")
if (X | Y) != 0
return
}
}
; Default caret
savedCaret := A_CoordModeCaret, W := 4, H := 20
;DPIfresh()
CoordMode "Caret", "Screen"
CaretGetPos(&X, &Y)
CoordMode "Caret", savedCaret
}
#NoTrayIcon
SetWinDelay 0
DetectHiddenWindows True
Persistent
loop {
main()
}
main() {
static lastHasCaret := 0, lastX := 0, lastY := 0, caretX := 0, caretY := 0, caretW := 0, caretH := 0
DPIfresh()
GetCaret(&caretX, &caretY, &caretW, &caretH )
hasCaret := caretX and caretY
if lastHasCaret and !hasCaret {
ToolTip()
}
lastHasCaret := hasCaret
if hasCaret {
DPIfresh()
oldCoordMode := A_CoordModeToolTip
DPIfresh()
A_CoordModeToolTip := "Screen"
topleftY := SysGet(79)
if ( caretY < topleftY ) {
caretY := Mod( caretY, Abs(topleftY))
}
Capstatus := GetKeyState("CapsLock", "T") ? "A" : ""
ToolTip GetIMEStatus() Capstatus, caretX - caretW, caretY + caretH
lastX := caretX , lastY := caretY
A_CoordModeToolTip := oldCoordMode
}
sleep 10
return 0
}
; DPI 感知
DPIfresh() {
Version := RegExReplace(A_OSVersion,"\.")
if Version >= 10014393 and Version < 10015063
DllCall("SetThreadDpiAwarenessContext", "ptr", -3, "ptr")
if Version >= 10015063
DllCall("SetThreadDpiAwarenessContext", "ptr", -4, "ptr")
}
; 中英状态获取
DetectHiddenWindows True
GetIMEStatus() {
fgWin := WinExist("A")
if WinActive("ahk_class vguiPopupWindow") or WinActive("ahk_class ApplicationFrameWindow") or WinActive("ahk_class CabinetWClass") or WinActive("ahk_class CASCADIA_HOSTING_WINDOW_CLASS") or WinActive("ahk_class Windows.UI.Core.CoreWindow") {
Focused := ControlGetFocus("A")
if Focused {
CtrlID := ControlGetHwnd(Focused, "A")
fgWin := CtrlID
}
}
return SendMessage(0x283, 0x001, 0,, "ahk_id " DllCall("imm32\ImmGetDefaultIMEWnd", "Uint", fgWin, "Uint")) ? "中 " : ""
}
Hook 版
;#NoTrayIcon
; DPI 感知
Version := RegExReplace(A_OSVersion,"\.")
if Version >= 10014393 and Version < 10015063
DllCall("SetThreadDpiAwarenessContext", "ptr", -3, "ptr")
if Version >= 10015063
DllCall("SetThreadDpiAwarenessContext", "ptr", -4, "ptr")
; 位置获取
RegisterCaretMessageHook(flag) {
static pShellCode := 0
hProcess := hMsvcrt := 0
if !pid := ProcessExist("explorer.exe")
throw Error('"explorer.exe" not found')
if !hProcess := DllCall("OpenProcess", "uint", 0x1fffff, "int", false, "uint", pid, "ptr")
throw OSError()
try {
if !hMsvcrt := DllCall("LoadLibraryW", "str", "msvcrt.dll", "ptr")
throw OSError()
pLocalMemcpy := DllCall("GetProcAddress", "ptr", hMsvcrt, "astr", "memcpy", "ptr")
if !msvcrtEntry := ToolHelpFindModuleByName("msvcrt.dll", pid)
throw Error('"msvcrt.dll" not found')
pMemcpy := msvcrtEntry.modBaseAddr + pLocalMemcpy - hMsvcrt
msg := DllCall("RegisterWindowMessage", "str", "WM_CARETPOSCHANGED")
if !flag || pShellCode {
if !flag {
OnExit(unregister, 0)
DllCall("ChangeWindowMessageFilterEx", "ptr", A_ScriptHwnd, "uint", msg, "uint", 0, "ptr", 0)
}
if !DllCall("WriteProcessMemory", "ptr", hProcess, "ptr", pMemcpy, "ptr", pLocalMemcpy, "uptr", 16, "uptr*", 0)
throw OSError()
if pShellCode {
DllCall("VirtualFreeEx", "ptr", hProcess, "ptr", pShellCode, "uptr", 0, "uint", 0x8000)
pShellCode := 0
}
return
}
memcpyBytes := DllCall("GetProcAddress", "ptr", hMsvcrt, "astr", "memset", "ptr") - pLocalMemcpy
if !user32Entry := ToolHelpFindModuleByName("user32.dll", pid)
throw Error('"User32.dll" not found')
hUser32 := DllCall("GetModuleHandle", "str", "user32", "ptr")
pPostMessageW := user32Entry.modBaseAddr + DllCall("GetProcAddress", "ptr", hUser32, "astr", "PostMessageW", "ptr") - hUser32
if !coreUiComponentsEntry := ToolHelpFindModuleByName("CoreUIComponents.dll", pid)
throw Error('"CoreUIComponents.dll" not found')
CryptHexStringToBinary("00000000000000000000000000000000000000000000000090909090909090904983F8100F85970000004C8B54240849BB00000000000000004D29DA4981FA000000000F8378000000837914090F840A000000837914070F856400000083791C000F855A0000004C8B124C8B5A084C3B158BFFFFFF0F850D0000004C3B1D86FFFFFF0F84390000004C891571FFFFFF4C891D72FFFFFF415052514883EC404D8BCB4D8BC2BA0000000048B90000000000000000FF1557FFFFFF4883C440595A4158", &shellCodeBuf)
NumPut("ptr", pPostMessageW, shellCodeBuf, 0x10)
NumPut("ptr", coreUiComponentsEntry.modBaseAddr, shellCodeBuf, 0x31)
NumPut("uint", coreUiComponentsEntry.modBaseSize, shellCodeBuf, 0x3F)
NumPut("uint", msg, shellCodeBuf, 0xA5)
NumPut("ptr", A_ScriptHwnd, shellCodeBuf, 0xAB)
if !pShellCode := DllCall("VirtualAllocEx", "ptr", hProcess, "ptr", 0, "uptr", shellCodeBuf.Size + memcpyBytes, "uint", 0x1000, "uint", 0x40, "ptr")
throw OSError()
CryptHexStringToBinary("48B80000000000000000FFD0C3", &hookBuf)
NumPut("ptr", pShellCode + 32, hookBuf, 2)
if !DllCall("WriteProcessMemory", "ptr", hProcess, "ptr", pShellCode, "ptr", shellCodeBuf, "uptr", shellCodeBuf.Size, "uptr*", 0)
throw OSError()
if !DllCall("WriteProcessMemory", "ptr", hProcess, "ptr", pShellCode + shellCodeBuf.Size, "ptr", pLocalMemcpy, "uptr", memcpyBytes, "uptr*", 0)
throw OSError()
if !DllCall("WriteProcessMemory", "ptr", hProcess, "ptr", pMemcpy, "ptr", hookBuf, "uptr", hookBuf.Size, "uptr*", 0)
throw OSError()
DllCall("ChangeWindowMessageFilterEx", "ptr", A_ScriptHwnd, "uint", msg, "uint", 1, "ptr", 0)
OnExit(unregister)
}
catch as e {
if pShellCode {
DllCall("VirtualFreeEx", "ptr", hProcess, "ptr", pShellCode, "uptr", 0, "uint", 0x8000)
pShellCode := 0
}
throw e
}
finally {
if hMsvcrt
DllCall("FreeLibrary", "ptr", hMsvcrt)
DllCall("CloseHandle", "ptr", hProcess)
}
return msg
static unregister(*) => RegisterCaretMessageHook(false)
}
/*
alloc(data, 2048)
label(begin)
label(ready)
label(memcpy)
label(L1)
data:
dq 0,0,0
dq 9090909090909090
begin:
cmp r8,10
jne memcpy
mov r10,[rsp+08]
mov r11,0000000000000000 // uicorecomponents address
sub r10,r11
cmp r10,00000000 // uicorecomponents size
jae memcpy
cmp dword ptr[rcx+14],9
je L1
cmp dword ptr[rcx+14],7
jne memcpy
L1:
cmp dword ptr[rcx+1C],0
jne memcpy
mov r10,[rdx]
mov r11,[rdx+08]
cmp r10,[data]
jne ready
cmp r11,[data+08]
je memcpy
ready:
mov [data],r10
mov [data+08],r11
push r8
push rdx
push rcx
sub rsp,40
mov r9,r11
mov r8,r10
mov edx,0 // msg
mov rcx,0 // hwnd
call [data+10]
add rsp,40
pop rcx
pop rdx
pop r8
memcpy:
*/
CryptHexStringToBinary(hexString, &binary){
DllCall("crypt32\CryptStringToBinaryW", "str", hexString, "uint", len := StrLen(hexString), "uint", 4, "ptr", 0, "uint*", &bytes := 0, "ptr", 0, "ptr", 0)
return DllCall("crypt32\CryptStringToBinaryW", "str", hexString, "uint", len, "uint", 4, "ptr", binary := binary ?? Buffer(bytes), "uint*", bytes, "ptr", 0, "ptr", 0)
}
ToolHelpFindModuleByName(moduleName, pid := 0) {
if !snapshot := DllCall("CreateToolhelp32Snapshot", "uint", 0x18, "uint", pid, "ptr")
return
entry := tagMODULEENTRY32W()
entry.dwSize := entry.Size
next := DllCall("GetProcAddress", "Ptr", DllCall("GetModuleHandle", "str", "kernel32", "ptr"), "astr", "Module32NextW", "ptr")
res := DllCall("Module32FirstW", "ptr", snapshot, "ptr", entry)
while res {
if entry.szModule = moduleName {
res := entry
break
}
res := DllCall(next, "ptr", snapshot, "ptr", entry)
}
DllCall("CloseHandle", "ptr", snapshot)
return res
}
class tagMODULEENTRY32W {
Size := 1080
__New() => this.Ptr := (this._ := Buffer(this.Size)).Ptr
dwSize {
get => NumGet(this, "uint")
set => NumPut("uint", Value, this)
}
th32ModuleID => NumGet(this, 4, "uint")
th32ProcessID => NumGet(this, 8, "uint")
GlblcntUsage => NumGet(this, 12, "uint")
ProccntUsage => NumGet(this, 16, "uint")
modBaseAddr => NumGet(this, 24, "ptr")
modBaseSize => NumGet(this, 32, "uint")
hModule => NumGet(this, 40, "ptr")
szModule => StrGet(this.Ptr + 48)
szExePath => StrGet(this.Ptr + 560)
}
; 中英状态提示
DetectHiddenWindows True
Persistent
OnMessage(RegisterCaretMessageHook(true), CaretMsgHandler2)
CaretMsgHandler2(leftTop, rightBottom, msg, hwnd){
static showToolTip := 0
if rightBottom == leftTop + 1 {
DllCall("SystemParametersInfo", "uint", 48, "uint", 0, "ptr", rect := Buffer(16), "uint", 0)
if NumGet(rect, 8, "int64") == rightBottom + 0x100000000
return 0
}
oldCoordMode := A_CoordModeToolTip
A_CoordModeToolTip := "Screen"
x := rightBottom & 0xffffffff
y := rightBottom >> 32
if x > 0x7fffffff
x -= 0x100000000
if y > 0x7fffffff
y -= 0x100000000
try
Capstatus := GetKeyState("CapsLock", "T") ? " A" : ""
ToolTip GetIMEStatus() Capstatus, x, y
SetTimer(StopShowToolTip, -500)
A_CoordModeToolTip := oldCoordMode
static StopShowToolTip() => (ToolTip(), showToolTip := 0)
return 0
}
; 中英状态获取
DetectHiddenWindows True
GetIMEStatus() {
fgWin := WinExist("A")
if WinActive("ahk_class vguiPopupWindow") or WinActive("ahk_class ApplicationFrameWindow") or WinActive("ahk_class CabinetWClass") or WinActive("ahk_class CASCADIA_HOSTING_WINDOW_CLASS") or WinActive("ahk_class Windows.UI.Core.CoreWindow") {
Focused := ControlGetFocus("A")
if Focused
{
CtrlID := ControlGetHwnd(Focused, "A")
fgWin := CtrlID
}
}
return SendMessage(0x283, 0x001, 0,, "ahk_id " DllCall("imm32\ImmGetDefaultIMEWnd", "Uint", fgWin, "Uint")) ? "中" : "英"
}
参考
以下是缝合时参考的文章