Ditto 不能出现在输入光标附近,有什么解决方法吗?

如下图,已设置为“插入符号处理”,是还要设置其它地方吗?我记得之前是正常的,已重装过。


不能出现在什么软件的输入光标处呢?
你试试 系统自带的 记事本 也不行么?

自带的笔记本可以,现在我在火狐浏览器上就不行,这还挑软件? :joy:

挑软件的,现代软件基本上都不标准,光标位置需要通过其他方法获取,用Windows的默认方案获取不到

好吧 :melting_face:

话虽这么说,但还是有能适配绝大多数软件的东西:输入法。基本上光标的位置都能获取到,所以我觉得应该还是有什么方法的吧(难道是一个个程序去适配吗)。

如何获取Windows各类软件的“插入/输入光标【caret】”的x、y坐标? - 问题求助 - 小众软件官方论坛 (appinn.net)

我也想知道

确实,windows 11 自带的剪贴板就可以做到出现在那附近

这段ahk代码是可以适配相当一部分软件的。比如 edge、firefox浏览器,还有一些编辑器,但是没有输入法适配范围那么广。

比如 苍月浏览器 输入法就能适配,这段代码就handle不了

我对这个已经放弃了,ditto、copyq、clibor这几个软件当中,ditto对光标的捕捉还算比较好的,但也常常不成功。我现在已经改成单击选择历史条目后手动复制到光标,其实也没多耽误事…

难道不是选这个嘛?

这个是鼠标在屏幕上的位置,不是输入文字的位置

我能想到的比较简单但是最有效的方法是:

通过ahk代码,每次调出ditto之前,先假装打一个字,然后跳出来了输入法的候选栏,获取输入法候选栏的坐标,再把ditto显示在这个坐标。

我做了个搜狗输入法的版本,效果和代码如下,只要搜狗输入法能显示准确的地方,用了这段代码,ditto就能显示准确

(其他输入法同理,修改ahk_class SoPY_Comp为对应的输入法窗口名就行。不过系统自带的微软输入法不太行,没有窗口名字、进程名还总变,只能每次开机时手动获取一次,太麻烦。):

6

666

哈哈,我实际操作了一下,代码有问题,不能用WinActivate,会丢失输入窗口,我再改一下。

改了一下,这下能用了

需要给ditto增加一个快捷键用来中介
image

#Persistent
#SingleInstance Force
DetectHiddenWindows, On

#IfWinNotActive ahk_class Notepad

$!`::
{
sendinput,v
sleep,200
WinGetPos , X, Y, , , ahk_class SoPY_Comp
send,{backspace}
send,!+1
WinMove,ahk_class QPasteClass, , X, Y
return
}
1 个赞

谷歌了下,发现貌似有实现的方法 How to get caret position in ANY application from C#? - Microsoft Q&A

把你这个和 通过hook资源管理器获取文本插入点坐标 - AutoAHK 缝合下,好像还行,有时需要触发 ditto 快捷键两次位置才正确,不知什么原因

;#NoTrayIcon
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 窗口    
    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 {
            WinWait("ahk_class QPasteClass",,1)
            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
            try
                lastX  := x, lastY := y
        }
        return 0
    }