MoveDitto | 让 Ditto 出现在输入光标附近

源起:Ditto 不能出现在输入光标附近,有什么解决方法吗?,当时没有方法准确获取输入光标的位置,所以一直无法实现,直到最近转机( 通过hook资源管理器获取文本插入点坐标 - AutoAHK )出现,于是根据 @dog代码 合并下,就有了这软件。

MoveDitto

  • 功能:移动 Ditto 剪贴板到输入光标附近
  • 注意:退出需要到任务管理器结束
  • 下载链接: 百度网盘 请输入提取码 提取码: xmks

源码

  • 需要 AHKV2.0-beta.6+
  • 需要移动其它剪贴板的,可自行修改源码中的 if WinExist("ahk_class QPasteClass") WinMove lastX, lastY,,,"ahk_class QPasteClass" 中的窗口识别信息
#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)
    }

; 移动 ditto 窗口
    SetWinDelay 0    
    DetectHiddenWindows True
    Persistent
    OnMessage(RegisterCaretMessageHook(true), CaretMsgHandler2)
    CaretMsgHandler2(leftTop, rightBottom, msg, hwnd){
        static lastHasCaret := 0, showToolTip := 0, lastX, lastY
        hasCaret := leftTop || rightBottom
        if lastHasCaret && !hasCaret {
            if WinExist("ahk_class QPasteClass") 
                WinMove lastX, lastY,,,"ahk_class QPasteClass"
        }
        lastHasCaret := hasCaret
        if hasCaret {
            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
            }
            x := rightBottom & 0xffffffff
            y := rightBottom >> 32
            if x > 0x7fffffff
                x -= 0x100000000
            if y > 0x7fffffff
                y -= 0x100000000
            lastX  := x, lastY := y
        }
        return 0
    }     


3 个赞

有V1版的吗

Sorry 没有,获取位置那个文章只给了 v2 版的,而且我也看不懂具体的操作:rofl:,帮不了你

v2 马上正式版了,各位v1众是时候考虑升级了。

你好,反馈一个问题。
如果光标位于屏幕下方的话,打开Ditto会出现如图的情况。
image

现在这个用了非 hook 方法,只是根据我自己的屏幕情况调整了下,你可以试下,链接: 百度网盘 请输入提取码 提取码: h4g6

源码

  • 需要 ahk v2
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 {
        if WinExist("ahk_class QPasteClass") {
            DPIfresh()
            WinGetPos &firstX, &firstY,,&firstH, "ahk_class QPasteClass"
            if ( firstY >= 0 and lastY < 0 ) {
                WinMove lastX, lastY - firstH,,,"ahk_class QPasteClass"
            }
            DPIfresh()
            WinGetPos &OutX, &OutY,, &wHeight, "ahk_class QPasteClass"
            maxHeight := SysGet(79) + SysGet(77)
            if ( lastY > 0 - wHeight and lastY < 0 ) or ( lastY > maxHeight - wHeight ) {
                newY := lastY - wHeight
            } else {
                newY := lastY  + caretH 
            }
            WinMove lastX, newY,,,"ahk_class QPasteClass"
        }
    }
    lastHasCaret := hasCaret
    if hasCaret {
        DPIfresh()
        topleftY := SysGet(79)
        if ( caretY < topleftY ) {
            caretY := Mod( caretY, Abs(topleftY))
        }
             
        lastX := caretX , lastY := caretY
    }
    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")
}
1 个赞

目前看来是解决问题了,非常感谢!

Ditto本身就有选择出现位置的选项,可以选择出现在鼠标所在位置。

鼠标所在跟我这个不一个东西。虽然官方也有在输入光标显示,但不是所有窗口都可以,你没遇到那应该不需要,我这个工具也只是通过其他方法让他支持更多类型的窗口而已。同时我也反馈给官方了但还没有下文。:joy: