• SysListView321控件查找并定位


    SysListView321控件在windows里相当普遍,比如【程序和功能】页面的已安装软件列表,
    因为这种控件无法搜索,如果内容一多,给定位带来困难,于是有了如下脚本。
    具体逻辑是:

    1. 提取控件的所有内容
    2. 用gui界面写个查找功能,并确定要查找的内容
    3. 用AutoHotkey定位到内容所在行。

    运行脚本后,激活控件页面,按F9即可
    附上 AutoHotkey v2-beta 代码

    #SingleInstance force
    persistent
    
    F9::_ListView("SysListView321", "A").selectByInput()
    
    class _ListView {
    
        __new(ctl, winTitle:="") {
            this.ctl := ctl
            this.hwnd := WinExist(winTitle)
            this.hCtl := ControlGetHwnd(ctl, "A")
            ; msgbox(ctl . "`n" . this.hwnd . "`n" . this.hCtl . "`n" . WinExist())
            this.pid := WinGetPID()
        }
    
        getIndexByText(str, idx:=1) {
            loop parse, ListViewGetContent(, this.hCtl), "`n", "`r" {
                ;msgbox(str . "`n" . json.stringify(StrSplit(A_LoopField,A_Tab), 4))
                if (StrSplit(A_LoopField,A_Tab)[idx] == str) {
                    return A_Index
                }
            }
            return 0
        }
    
        selectByInput() {
            arr := []
            loop parse, ListViewGetContent(, this.hCtl), "`n", "`r"
                arr.push(RegExReplace(A_LoopField, "`t.*"))
            arrRes := searchArr(arr)
            if (arrRes.length)
                this.selectByText(arrRes[1])
        }
        selectByText(str) {
            idx := this.getIndexByText(str)
            if (!idx)
                throw ValueError(format('not found "{1}" in ListView', str))
            this.selectByIndex(idx)
        }
        selectByIndex(idx:=1) {
            bufLvItem := buffer(52+(2*A_PtrSize))
            numput("UPtr", state:=3, bufLvItem, 12)
            numput("UPtr", stateMask:=2, bufLvItem, 16)
            oRB := RemoteBuffer(this.pid, bufLvItem.size)
            oRB.write(bufLvItem)
            SendMessage(LVM_ENSUREVISIBLE:=0x1013, idx-1,,, "ahk_id" . this.hCtl)
            SendMessage(LVM_SETITEMSTATE:=0x102B, idx-1, oRB.arrBuffer[1],, "ahk_id" . this.hCtl)
            ;PostMessage(0x1043,, 2,, "ahk_id" . ControlGetHwnd(ctl, winTitle))
        }
    
    }
    
    class RemoteBuffer {
        ;size 一般多大
        ; https://docs.microsoft.com/en-us/windows/win32/procthread/process-security-and-access-rights
        ;PROCESS_VM_OPERATION:=0x8 PROCESS_VM_READ:=0x10 PROCESS_VM_WRITE:=0x20 PROCESS_QUERY_INFORMATION:=0x400
        __new(pid, size:=0, DesiredAccess:=56) { ; 0x8|0x10|0x20==56
            if !(this.hProcess := dllcall("OpenProcess", "UInt",DesiredAccess, "Int",0, "UInt",pid, "Ptr"))
                return ""
            this.arrBuffer := [] ;NOTE 可申请多个内存,所以用数组
            this.arrSize := []
            if (size)
                this.addBuffer(size)
        }
    
        __delete(idx:=0) {
            if idx {
                pBuffer := this.arrBuffer.RemoveAt(idx)
                this.arrSize.RemoveAt(idx)
                dllcall("VirtualFreeEx", "Ptr",this.hProcess, "Ptr",pBuffer, "UInt",0, "UInt",MEM_RELEASE:=0x8000)
                dllcall("CloseHandle", "Ptr",this.hProcess)
            } else { ;删除全部
                for k, pBuffer in this.arrBuffer
                    dllcall("VirtualFreeEx", "Ptr",this.hProcess, "Ptr",pBuffer, "UInt",0, "UInt",MEM_RELEASE:=0x8000)
                dllcall("CloseHandle", "Ptr",this.hProcess)
            }
        }
    
        ;比如给 SysTabControl321 的某一项申请空间(文字内容不定长,所以不能直接用 SendMessage 获取)
        ;文字内容往往在 pBuffer + size 的后面
        addBuffer(size) {
            if !(pBuffer := dllcall("VirtualAllocEx", "UInt",this.hProcess, "UInt",0, "UInt",size, "UInt",MEM_COMMIT:=0x1000, "UInt",PAGE_READWRITE:=4, "Ptr"))
                return ""
            this.arrBuffer.push(pBuffer)
            this.arrSize.push(size)
            return pBuffer
        }
    
        ;NOTE size 可能和 arrSize 不同
        write(pLocalBuff, size:=0, idx:=1, offset:=0) {
            size := size ? size : this.arrSize[idx] ;TODO size是否恒等于 this.arrSize[idx],是则可省略参数
            return dllcall("WriteProcessMemory", "Ptr",this.hProcess, "Ptr",this.arrBuffer[idx]+offset, "Ptr",pLocalBuff, "UInt",size, "UInt",0)
        }
    
        ;如果是单值,可直接设置 bVal=1
        read(idx:=1, bVal:=0, offset:=0, size:=0) {
            static bufLocal ;NOTE 不能少
            if !size
                size := this.arrSize[idx] - offset
            else
                size := min(size, this.arrSize[idx] - offset)
            bufLocal := buffer(size, 0)
            ;从 arrBuffer[idx]+offset 地址读取 size 长度内容,存到 &bufLocal 地址
            dllcall("ReadProcessMemory", "Ptr",this.hProcess, "Ptr",this.arrBuffer[idx]+offset, "Ptr",bufLocal, "UInt",size, "UInt",0)
            ;bufLocal := buffer(-1)
            return bVal ? bufLocal : bufLocal.ptr
        }
    
    }
    
    searchArr(arr, bAddPy:=false, bDistinct:=false) {
        ;NOTE 转成二维 顺带 push 序号(以第1项为主,用来生成拼音什么的,所以用 push)
        if !arr.length
            return []
        if !isobject(arr[1]) { ;一维转成[hot, item]
            for k, v in arr
                arr[k] := [v, k]
        }
        arrNew := []
        ;去重(根据 subArr[1])
        if (bDistinct) { ;去重,并过滤空值
            obj := map()
            for subArr in arr {
                v := subArr[1]
                if (strlen(v) && !obj.has(v)) {
                    arrNew.push(subArr)
                }
                obj[v] := ""
            }
        } else {
            arrNew := arr
        }
        ;添加标题
        arrField := ["序号"]
        for v in arrNew[1]
            arrField.push("v" . A_Index)
        ;添加拼音
        if (bAddPy) {
            arrField.push("拼音")
            for k, v in arrNew
                arrNew[k].push(v[1].shouzimus())
        }
        ;msgbox(json.stringify(arrField, 4))
        ;msgbox(json.stringify(arrNew, 4))
        ;添加到 Gui
        oGui := gui("+resize")
        oGui.OnEvent("escape",doEscape)
        oGui.OnEvent("close",doEscape)
        oGui.SetFont("s13")
        oGui.add("Text",,"按 F1-F12 或【双击】可直接确定对应条目")
        oEdit := oGui.add("Edit", "Lowercase section")
        oEdit.OnEvent("change", loadLV)
        oCB1 := oGui.Add("Checkbox", "yp checked", arrField[2])
        oCB2 := oGui.Add("Checkbox", "yp", arrField[3])
        ;添加按键显示结果(点击复制)
        oButton1 := oGui.add("button", "w200 xs cRed")
        oButton1.OnEvent("click", (ctl, p*)=>A_Clipboard := ctl.text)
        if (arrNew[1].length > 2) {
            oButton2 := oGui.add("button", "w500 yp xp+300 cRed")
            oButton2.OnEvent("click", (ctl, p*)=>A_Clipboard := ctl.text)
        }
        ;ListView 标题名
        ;field := 65
        oLv := oGui.AddListView("vlv1 xs r20 cRed w1400", arrField) ;NOTE selectN 要用 lv1 获取控件,不要用 oLv(影响释放)
        oLv.OnEvent("DoubleClick", do)
        oLv.OnEvent("ItemFocus", tips)
        tooltip("加载数据...")
        timeSave := A_TickCount
        obj := ""
        nLoad := A_TickCount - timeSave
        tooltip("添加到Gui...")
        loadLV(oEdit)
        nGui := A_TickCount - timeSave - nLoad
        tooltip
        oGui.title := format("读取耗时 {1} 加载到Gui耗时 {2}", nLoad,nGui)
        oGui.show()
        resGui := []
        OnMessage(WM_KEYDOWN:=0x100, selectN)
        WinWaitClose("ahk_id " . oGui.hwnd)
        return resGui
        doEscape(oGui, p*) {
            oGui.destroy()
            OnMessage(WM_KEYDOWN, selectN, 0)
        }
        loadLV(ctl, p*) { ;中文则搜索第1个内容,否则搜索第2个内容
            oLv.delete()
            oLv.opt("-Redraw")
            ;获取匹配项
            arrKeysMatch := []
            if oCB1.value
                arrKeysMatch.push(1)
            if oCB2.value
                arrKeysMatch.push(2)
            if !arrKeysMatch.length
                return
            sInput := ctl.text
            arrKeysMatch[1] := (bAddPy && sInput ~= "[[:ascii:]]") ? arrNew[1].length : 1
            i := 1
            for subArr in arrNew {
                for idx in arrKeysMatch {
                    if (sInput=="" || instr(subArr[idx], sInput)) {
                        oLv.add(, i++, subArr*)
                        break
                    }
                }
            }
            ;搜网址有用没结果且只搜索标题,则搜索网址
            ;if (oLv.GetCount() == 0) {
            ;    for subArr in arrNew {
            ;        if instr(subArr[2], sInput)
            ;            oLv.add(, i++, subArr[1], subArr[2], subArr[3])
            ;    }
            ;}
            oLv.ModifyCol(, "+AutoHdr +center")
            oLv.ModifyCol(1, "48")
            oLv.opt("+Redraw")
            if (oLv.GetCount() == 1) { ;单结果
                do(oLv, 1)
            } else if (oLv.GetCount() > 1) {
                oLv.modify(1, "+select")
                tips(oLv, 1)
            }
        }
        selectN(wParam, lParam, msg, hwnd) { ;NOTE 由于这个函数不传入oGui或oControl,要用 hwnd获取oGui,用oGui[ctlNmae]获取控件
            try
                oLv := GuiFromHwnd(hwnd, 1)["lv1"] ;NOTE
            catch
                return
            if (wParam == 13) { ;enter
                do(oLv, oLv.GetNext())
            } else if (wParam == 40) { ;down
                n := oLv.GetNext()
                if (!n)
                    oLv.modify(1, "+select")
                else {
                    oLv.modify(n, "-select")
                    oLv.modify(n+1, "+select")
                }
            } else if (wParam == 38) { ;up
                n := oLv.GetNext()
                if (n>1) {
                    oLv.modify(n, "-select")
                    oLv.modify(n-1, "+select")
                }
            } else {
                r := wParam-111
                if (r >= 1 && r <= 12) ;F1-F12
                    do(oLv, r)
            }
        }
        tips(oLv, r, p*) {
            ;获取当前行整行内容
            arrRes := []
            loop(arrNew.length)
                arrRes.push(oLv.GetText(r, A_Index))
            oButton1.text := arrRes[2]
            try
                oButton2.text := arrRes[3]
        }
        do(oLv, r, p*) { ;NOTE 要做的事
            ;获取当前行整行内容
            arrRes := []
            loop(arrNew[1].length)
                arrRes.push(oLv.GetText(r, A_Index+1))
            ;做任何事
            ;设置返回值
            resGui := arrRes
            doEscape(oLv.gui)
        }
    }
    
  • 相关阅读:
    免费第三方API平台整合
    接口使用数据库缓存考虑的不周到之处
    找了两个小时的错误,net.sf.json.JSONException: JSON keys cannot be null.
    jsp动态页面访问报错:HTTP Status 500
    JAVA中json转换为集合(对象)之间的相互转换
    听头条
    使用DataOutputStream输出流的read方法出现读取字节不一致解决办法,本地和测试环境不一致
    ibatis中的xml配置文件
    poj 1325 Machine Schedule 题解
    poj 1469 COURSES 题解
  • 原文地址:https://www.cnblogs.com/hyaray/p/16099360.html
Copyright © 2020-2023  润新知