#NoEnv #KeyHistory 0 #MaxThreads 255 #MaxMem 4095 #MaxThreadsBuffer On #MaxHotkeysPerInterval 99000000 #HotkeyInterval 99000000 #SingleInstance Force sendmode,Input CoordMode, ToolTip , Screen ListLines Off Process, Priority, , R SetTitleMatchMode fast SetBatchLines, -1 SetKeyDelay, -1, -1, -1 SetMouseDelay, -1 SetWinDelay, -1 SetControlDelay, -1 SetDefaultMouseSpeed, 0 SetWorkingDir %A_ScriptDir% Run,*RunAs 取色.ahk,% A_ScriptDir ;Menu, Tray, NoStandard ;删除自带托盘菜单 Gui,1: Add, Button, x0 y0 gButtonLoadFolder, 加载配置(ini) Gui,1: Add, Button, y0 gmodifyRange, 选取坐标范围 Gui,1: Add, Button, y0 gscreenshot, 截图 Gui,1: Add, Button, y0 ggetColor, 取色 Gui,1: Add, Button, y0 gModifyConfiguration, 修改取色值 Gui,1: Add, Button, y0 gmodifyHeroChoose, 修改阵容 Gui,1: Add, Button, y0 gloadColor, 重新加载修改取色值及取色范围 Gui,1: Add, Button, y0 gSaveConfiguration, 保存阵容 Gui,1: Add, Button, y0 ghelp, help Gui,1: Add, ListView,x0 r20 w700 -ReadOnly Checked AltSubmit gMyListView vMyListView, 英雄|种族 ; 创建作为上下文菜单的弹出菜单: Menu, MyContextMenu, Add, Clear from ListView, ContextClearRows Menu, MyContextMenu, Add, add, Contextadd global BackgroundColor := 0X101008 global SelectedFile :="" global heroName:={} global TargetScriptTitle := "取色.ahk ahk_class AutoHotkey" global canbuy:=1 global mark_music:=1 global isBuy:=0 loadConfig() MsgBox, 默认支持1080p分辨率,本机分辨率%A_ScreenHeight% gui,1: +AlwaysOnTop +hWndhMainWnd +Resize Gui,1: Show return screenshot: result := Send_WM_COPYDATA("截图", TargetScriptTitle) return getColor: result := Send_WM_COPYDATA("取色", TargetScriptTitle) return modifyRange: MsgBox, 4096,, % GetRange(x,y,w,h) "范围:" x "," y "," w "," h IniWrite,% x,% A_ScriptDir . "configcolor.ini", 通用,X1 IniWrite,% y,% A_ScriptDir . "configcolor.ini", 通用,Y1 IniWrite,% x+w,% A_ScriptDir . "configcolor.ini", 通用,X2 IniWrite,% y+h,% A_ScriptDir . "configcolor.ini", 通用,Y2 return choosehero: ;~0:: ~Numpad0:: canbuy :=1 try { loop { if(ok:=FindText(x1, y1, x2, y2,0, 0, 英雄,1,0)) { StartTime := A_TickCount loop { CoordMode, Mouse x:=ok.1.x y:=ok.1.y SendInput,{Click %x%, %y%,0} sendinput,{LButton down} sleep,100 sendinput,{LButton up} PixelGetColor, color, %X%, %Y%+50 ElapsedTime := A_TickCount - StartTime ToolTip(ElapsedTime) if((color == BackgroundColor) || (ElapsedTime>=1000)) { if(ElapsedTime>=1000) { canbuy := 0 } break } isBuy := 1 } SendInput,{Click 930, 650,0} if(canbuy == 0) { tooltip("买不了") SoundPlay("买不了") break } } else { ToolTip("没有找到") SoundPlay("没找到") ;SoundPlay, %A_ScriptDir%configmusic没找到.mp3 break } } if(isBuy==1) { isBuy:=0 SoundPlay, %A_ScriptDir%configmusic杀鸡取卵.mp3,1 } } return f5:: ExitApp return *f7:: if(WinActive("ahk_id" . hMainWnd)) { Gui,1: Hide } else { Gui,1: Restore } return ;~5:: ~Numpad5:: ;花光模式 canbuy :=1 InputBox, password, 输入次数, (your input will be hidden), Gosub,Hangup loop { Gosub,choosehero if(canbuy == 0) { tooltip("买不了") SoundPlay("买不了") ;SoundPlay, %A_ScriptDir%configmusic买不了.mp3 break } else if(A_Index>=password) { tooltip("花完了") SoundPlay("花完了") SoundPlay, %A_ScriptDir%configmusic花完了.mp3 break } else { SendInput,{d} sleep,500 } } return ~Numpad4:: SoundPlay("鸡霸天下") return ;~8:: ~Numpad7:: SoundPlay("垃圾") return ;~8:: ~Numpad8:: SoundPlay("挂机模式开启") ;SoundPlay, %A_ScriptDir%configmusic挂机模式开启.mp3 tooltip,挂机模式开启 mark_music:=0 guajiFlag:=1 Loop { gosub,choosehero }Until (guajiFlag == 0) mark_music:=1 return Hangup: ;~9:: ~Numpad9:: ;挂机模式 mark_music:=1 guajiFlag:=0 tooltip("挂机模式关闭") SoundPlay, %A_ScriptDir%configmusic挂机模式关闭.mp3,1 ;SoundPlay("挂机模式关闭") return loadColor: loadConfig() return ButtonLoadFolder: LV_Delete() Thread, NoTimers Gui,1: +OwnDialogs ; 强制用户解除此对话框后才可以操作主窗口. FileSelectFile, SelectedFile, , , Open a file, Text Documents (*.ini) Thread, NoTimers, false if SelectedFile = MsgBox, The user didn't select anything. else MsgBox, The user selected the following:`n%SelectedFile% ; 获取所选择文件夹中的文件名列表并添加到 ListView: GuiControl,1: -Redraw, MyListView ; 在加载时禁用重绘来提升性能. myini:=class_EasyIni(SelectedFile) iwant:=myini.通用 For key, value in iwant { LV_Add("Check", key, value) } GuiControl,1: +Redraw, MyListView ; 重新启用重绘 (上面把它禁用了). LV_ModifyCol() ; 根据内容自动调整每列的大小. return MyListView: if(A_GuiEvent == "DoubleClick") { InputBox,putKey,英雄名字,,,,,0,0 InputBox,putvalue,描述,,,,,0,0 if(putKey == "") { return } LV_Modify(A_EventInfo ,, putKey, putvalue) } heroName:={} 英雄:="" RowNumber = 0 ; 这样使得首次循环从列表的顶部开始搜索. Loop { RowNumber := LV_GetNext(RowNumber,"Checked") ; 在前一次找到的位置后继续搜索. if not RowNumber ; 上面返回零, 所以选择的行已经都找到了. break LV_GetText(Text, RowNumber) LV_GetText(Text_value, RowNumber,2) heroName[Text]:=Text_value 英雄.=colorArray[(Text)] } return ContextClearRows: ; 用户在上下文菜单中选择了 "Clear". RowNumber := 0 ; 这会使得首次循环从顶部开始搜索. Loop { ; 由于删除了一行使得此行下面的所有行的行号都减小了, ; 所以把行号减 1, 这样搜索里包含的行号才会与之前找到的行号相一致 ; (以防选择了相邻行): RowNumber := LV_GetNext(RowNumber - 1) if not RowNumber ; 上面返回零, 所以没有更多选择的行了. break LV_Delete(RowNumber) ; 从 ListView 中删除行. } return GuiContextMenu: ; 运行此标签来响应右键点击或按下 Appskey. if (A_GuiControl != "MyListView") ; 仅在 ListView 中点击时才显示菜单. return ; 在提供的坐标处显示菜单, A_GuiX 和 A_GuiY. 应该使用这些 ; 因为即使用户按下 Appskey 它们也会提供正确的坐标: Menu, MyContextMenu, Show, %A_GuiX%, %A_GuiY% return Contextadd: LV_GetText(FileName, FocusedRowNumber, 1) ; 获取首个字段的文本. InputBox,addHeroName,英雄名字,,,,,0,0 LV_Add("Check",addHeroName) Return ModifyConfiguration: run,% A_ScriptDir . "configcolor.ini" return modifyHeroChoose: run,% SelectedFile return SaveConfiguration: Thread, NoTimers Gui,1: +OwnDialogs ; 强制用户解除此对话框后才可以操作主窗口. FileSelectFile, SelectedFile, , , Open a file, Text Documents (*.ini) Thread, NoTimers, false if SelectedFile = MsgBox, The user didn't select anything. else MsgBox, The user selected the following:`n%SelectedFile% RowNumber := 0 ; 这样使得首次循环从列表的顶部开始搜索. IniDelete,% SelectedFile, 通用 for key,value in heroName { IniWrite,% value,% SelectedFile, 通用,% key } Return help: try { helpFile= ( 必须管理员身份运行 1.加载配置,默认配置路径config文件夹下 2.LOL选卡界面,按快捷键F10或者数字小键盘0自动选英雄 3.可以F7勾选和取消相应的英雄 快捷键: f10 0:选英雄 Numpad5 5:花10元 Numpad8 8:挂机模式 Numpad9 9:结束挂机模式 f7: 界面开关 f5:退出 注: 默认1080P分辨率可以用 配置文件可以自己修改,修改第一字段英雄就可以 目前支持的英雄不全,后续会补充 有问题联系qq3261404579 ) ;MsgBox,% helpFile run,% SelectedFile } return loadConfig() { colorini:=class_EasyIni(A_ScriptDir . "configcolor.ini") coordinate:=colorini.通用 global x1:=coordinate["X1"] global y1:=coordinate["Y1"] global x2:=coordinate["X2"] global y2:=coordinate["Y2"] global colorArray:=colorini.取色 Gosub, MyListView } ;============ 脚本结束 ================= ToolTip(label){ ToolTip, %label%, 0,0 SetTimer, RemoveToolTip, 1000 return RemoveToolTip: SetTimer, RemoveToolTip, Off ToolTip Return } ;===== Copy The Following Functions To Your Own Code Just once ===== ;-------------------------------- ; FindText - 屏幕找字函数 ;-------------------------------- ; 返回变量 := FindText( ; X1 --> 查找范围的左上角X坐标 ; , Y1 --> 查找范围的左上角Y坐标 ; , X2 --> 查找范围的右下角X坐标 ; , Y2 --> 查找范围的右下角Y坐标 ; , err1 --> 文字的黑点容错百分率(0.1=10%) ; , err0 --> 背景的白点容错百分率(0.1=10%) ; , Text --> 由工具生成的查找图像的数据,可以一次查找多个,用“|”分隔 ; , ScreenShot --> 是否截屏,为0则使用上一次的截屏数据 ; , FindAll --> 是否搜索所有位置,为0则找到一个位置就返回 ; , JoinText --> 是否组合图像,为1则多个数据组合为一幅图来查找 ; , offsetX --> 组合图像的每个字和前一个字的最大横向间隔 ; , offsetY --> 组合图像的每个字和第一个字的最大高低间隔 ; ) ; 返回变量 --> 如果没找到结果会返回0。否则返回一个二级数组, ; 第一级是每个结果对象,第二级是结果对象的具体信息数组: ; { 1:左上角X, 2:左上角Y, 3:图像宽度W, 4:图像高度H ; , x:中心点X, y:中心点Y, id:图像识别文本 } ; 坐标都是相对于屏幕,颜色使用RGB格式,组合查找必须使用统一的颜色模式 ;-------------------------------- FindText( x1, y1, x2, y2, err1, err0, text, ScreenShot:=1 , FindAll:=1, JoinText:=0, offsetX:=20, offsetY:=10 ) { local ; 避免超级全局变量的影响 bch:=A_BatchLines SetBatchLines, -1 x:=(x1<x2 ? x1:x2), y:=(y1<y2 ? y1:y2) , w:=Abs(x2-x1)+1, h:=Abs(y2-y1)+1 , xywh2xywh(x,y,w,h,x,y,w,h,zx,zy,zw,zh) if (w<1 or h<1) { SetBatchLines, %bch% return, 0 } bits:=GetBitsFromScreen(x,y,w,h,ScreenShot,zx,zy,zw,zh) sx:=x-zx, sy:=y-zy, sw:=w, sh:=h, arr:=[], info:=[] Loop, Parse, text, | if IsObject(j:=PicInfo(A_LoopField)) info.Push(j) if (!(num:=info.MaxIndex()) or !bits.1) { SetBatchLines, %bch% return, 0 } VarSetCapacity(input, num*7*4), k:=0 Loop, % num k+=Round(info[A_Index].2 * info[A_Index].3) VarSetCapacity(s1, k*4), VarSetCapacity(s0, k*4) , VarSetCapacity(gs, sw*sh), VarSetCapacity(ss, sw*sh) , allpos_max:=(FindAll ? 1024 : 1) , VarSetCapacity(allpos, allpos_max*4) Loop, 2 { if (err1=0 and err0=0) and (num>1 or A_Index>1) err1:=0.1, err0:=0.05 if (JoinText) { j:=info[1], mode:=j.8, color:=j.9, n:=j.10 , w1:=-1, h1:=j.3, comment:="", v:="", i:=0 Loop, % num { j:=info[A_Index], w1+=j.2+1, comment.=j.11 Loop, 7 NumPut((A_Index=1 ? StrLen(v) : A_Index=6 and err1 and !j.12 ? Round(j.4*err1) : A_Index=7 and err0 and !j.12 ? Round(j.5*err0) : j[A_Index]), input, 4*(i++), "int") v.=j.1 } ok:=PicFind( mode,color,n,offsetX,offsetY , bits,sx,sy,sw,sh,gs,ss,v,s1,s0 , input,num*7,allpos,allpos_max ) Loop, % ok pos:=NumGet(allpos, 4*(A_Index-1), "uint") , rx:=(pos&0xFFFF)+zx, ry:=(pos>>16)+zy , arr.Push( {1:rx, 2:ry, 3:w1, 4:h1 , x:rx+w1//2, y:ry+h1//2, id:comment} ) } else { For i,j in info { mode:=j.8, color:=j.9, n:=j.10, comment:=j.11 , w1:=j.2, h1:=j.3, v:=j.1 Loop, 7 NumPut((A_Index=1 ? 0 : A_Index=6 and err1 and !j.12 ? Round(j.4*err1) : A_Index=7 and err0 and !j.12 ? Round(j.5*err0) : j[A_Index]), input, 4*(A_Index-1), "int") ok:=PicFind( mode,color,n,offsetX,offsetY , bits,sx,sy,sw,sh,gs,ss,v,s1,s0 , input,7,allpos,allpos_max ) Loop, % ok pos:=NumGet(allpos, 4*(A_Index-1), "uint") , rx:=(pos&0xFFFF)+zx, ry:=(pos>>16)+zy , arr.Push( {1:rx, 2:ry, 3:w1, 4:h1 , x:rx+w1//2, y:ry+h1//2, id:comment} ) if (ok and !FindAll) Break } } if (err1=0 and err0=0 and num=1 and !arr.MaxIndex()) { k:=0 For i,j in info k+=(!j.12) IfEqual, k, 0, Break } else Break } SetBatchLines, %bch% return, arr.MaxIndex() ? arr:0 } ; 绑定窗口从而可以后台查找这个窗口的图像 ; 相当于始终在前台。解绑窗口使用 Bindwindow(0) BindWindow(window_id:=0, set_exstyle:=0, get:=0) { static id, old, Ptr:=A_PtrSize ? "UPtr" : "UInt" if (get) return, id if (window_id) { id:=window_id, old:=0 if (set_exstyle) { WinGet, old, ExStyle, ahk_id %id% WinSet, Transparent, 255, ahk_id %id% Loop, 30 { Sleep, 100 WinGet, i, Transparent, ahk_id %id% } Until (i=255) } } else { if (old) WinSet, ExStyle, %old%, ahk_id %id% id:=old:=0 } } xywh2xywh(x1,y1,w1,h1, ByRef x,ByRef y,ByRef w,ByRef h , ByRef zx:="", ByRef zy:="", ByRef zw:="", ByRef zh:="") { SysGet, zx, 76 SysGet, zy, 77 SysGet, zw, 78 SysGet, zh, 79 left:=x1, right:=x1+w1-1, up:=y1, down:=y1+h1-1 left:=left<zx ? zx:left, right:=right>zx+zw-1 ? zx+zw-1:right up:=up<zy ? zy:up, down:=down>zy+zh-1 ? zy+zh-1:down x:=left, y:=up, w:=right-left+1, h:=down-up+1 } GetBitsFromScreen(x, y, w, h, ScreenShot:=1 , ByRef zx:="", ByRef zy:="", ByRef zw:="", ByRef zh:="") { local ; 避免超级全局变量的影响 static hBM, oldzx, oldzy, oldzw, oldzh, bits:=[] static Ptr:=A_PtrSize ? "UPtr" : "UInt" static init:=!GetBitsFromScreen(0,0,0,0,1) if (!ScreenShot) { zx:=oldzx, zy:=oldzy, zw:=oldzw, zh:=oldzh return, bits } bch:=A_BatchLines, cri:=A_IsCritical Critical if (zw<1 or zh<1) { SysGet, zx, 76 SysGet, zy, 77 SysGet, zw, 78 SysGet, zh, 79 } if (zw>oldzw or zh>oldzh or !hBM) { DllCall("DeleteObject", Ptr,hBM), hBM:="", bpp:=32 VarSetCapacity(bi, 40, 0), NumPut(40, bi, 0, "int") NumPut(zw, bi, 4, "int"), NumPut(-zh, bi, 8, "int") NumPut(1, bi, 12, "short"), NumPut(bpp, bi, 14, "short") hBM:=DllCall("CreateDIBSection", Ptr,0, Ptr,&bi , "int",0, Ptr "*",ppvBits, Ptr,0, "int",0, Ptr) Scan0:=(!hBM ? 0:ppvBits), Stride:=((zw*bpp+31)//32)*4 bits.1:=Scan0, bits.2:=Stride oldzx:=zx, oldzy:=zy, oldzw:=zw, oldzh:=zh x:=zx, y:=zy, w:=zw, h:=zh } if (hBM) and !(w<1 or h<1) { win:=DllCall("GetDesktopWindow", Ptr) hDC:=DllCall("GetWindowDC", Ptr,win, Ptr) mDC:=DllCall("CreateCompatibleDC", Ptr,hDC, Ptr) oBM:=DllCall("SelectObject", Ptr,mDC, Ptr,hBM, Ptr) DllCall("BitBlt",Ptr,mDC,"int",x-zx,"int",y-zy,"int",w,"int",h , Ptr,hDC, "int",x, "int",y, "uint",0x00CC0020|0x40000000) DllCall("ReleaseDC", Ptr,win, Ptr,hDC) if (id:=BindWindow(0,0,1)) WinGet, id, ID, ahk_id %id% if (id) { WinGetPos, wx, wy, ww, wh, ahk_id %id% left:=x, right:=x+w-1, up:=y, down:=y+h-1 left:=left<wx ? wx:left, right:=right>wx+ww-1 ? wx+ww-1:right up:=up<wy ? wy:up, down:=down>wy+wh-1 ? wy+wh-1:down x:=left, y:=up, w:=right-left+1, h:=down-up+1 } if (id) and !(w<1 or h<1) { hDC2:=DllCall("GetDCEx", Ptr,id, Ptr,0, "int",3, Ptr) DllCall("BitBlt",Ptr,mDC,"int",x-zx,"int",y-zy,"int",w,"int",h , Ptr,hDC2, "int",x-wx, "int",y-wy, "uint",0x00CC0020) DllCall("ReleaseDC", Ptr,id, Ptr,hDC2) } DllCall("SelectObject", Ptr,mDC, Ptr,oBM) DllCall("DeleteDC", Ptr,mDC) } Critical, %cri% SetBatchLines, %bch% return, bits } PicInfo(text) { static info:=[] IfNotInString, text, $, return if (info[text]) return, info[text] v:=text, comment:="", e1:=e0:=0, set_e1_e0:=0 ; You Can Add Comment Text within The <> if RegExMatch(v,"<([^>]*)>",r) v:=StrReplace(v,r), comment:=Trim(r1) ; You can Add two fault-tolerant in the [], separated by commas if RegExMatch(v,"[([^]]*)]",r) { v:=StrReplace(v,r), r1.="," StringSplit, r, r1, `, e1:=r1, e0:=r2, set_e1_e0:=1 } StringSplit, r, v, $ color:=r1, v:=r2 "." StringSplit, r, v, . w1:=r1, v:=base64tobit(r2), h1:=StrLen(v)//w1 if (w1<1 or h1<1 or StrLen(v)!=w1*h1) return mode:=InStr(color,"-") ? 4 : InStr(color,"#") ? 3 : InStr(color,"**") ? 2 : InStr(color,"*") ? 1 : 0 if (mode=4) { color:=StrReplace(color,"0x") StringSplit, r, color, - color:="0x" . r1, n:="0x" . r2 } else { color:=RegExReplace(color,"[*#]") . "@" StringSplit, r, color, @ color:=r1, n:=Round(r2,2)+(!r2) , n:=Floor(9*255*255*(1-n)*(1-n)) } StrReplace(v,"1","",len1), len0:=StrLen(v)-len1 , e1:=Round(len1*e1), e0:=Round(len0*e0) return, info[text]:=[v,w1,h1,len1,len0,e1,e0 , mode,color,n,comment,set_e1_e0] } PicFind(mode, color, n, offsetX, offsetY , bits, sx, sy, sw, sh , ByRef gs, ByRef ss, ByRef text, ByRef s1, ByRef s0 , ByRef input, num, ByRef allpos, allpos_max) { static MyFunc, Ptr:=A_PtrSize ? "UPtr" : "UInt" if (!MyFunc) { x32:="5557565383EC788B8424CC0000008BBC24CC000000C7442" . "424000000008B40048B7F148944243C8B8424CC000000897C2" . "42C8BBC24CC0000008B40088B7F18894424388B8424CC00000" . "0897C24308B400C89C6894424288B8424CC0000008B401039C" . "6894424200F4DC68944241C8B8424D000000085C00F8E15010" . "0008BB424CC0000008B44242489F78B0C868B7486048B44870" . "88974241085C0894424180F8ED700000089CD894C2414C7442" . "40C00000000C744240800000000C744240400000000890C248" . "D76008DBC27000000008B5C24108B7424088B4C24148B54240" . "C89DF89F029F101F78BB424C000000001CE85DB7E5E8B0C248" . "9EB893C2489D7EB198BAC24C800000083C70483C00189548D0" . "083C101390424742C83BC248C0000000389FA0F45D0803C063" . "175D48BAC24C400000083C70483C00189549D0083C30139042" . "475D48B7424100174241489DD890C2483442404018BB424B00" . "000008B442404017424088BBC24A4000000017C240C3944241" . "80F8554FFFFFF83442424078B442424398424D00000000F8FE" . "BFEFFFF83BC248C000000030F84A00600008B8424A40000008" . "BB424A80000000FAF8424AC0000008BBC24A40000008D2CB08" . "B8424B0000000F7D88D04878BBC248C0000008944241085FF0" . "F84F702000083BC248C000000010F847F08000083BC248C000" . "000020F84330900008B8424900000008B9C24940000000FB6B" . "C24940000000FB6B42490000000C744241800000000C744242" . "400000000C1E8100FB6DF0FB6D08B84249000000089D10FB6C" . "4894424088B842494000000C1E8100FB6C029C101D08904248" . "B442408894C24408B4C240801D829D9894424088D043E894C2" . "40489F129F9894424148BBC24B40000008B8424B0000000894" . "C240C89E98B6C2440C1E00285FF894424340F8EBA0000008BB" . "424B000000085F60F8E910000008B8424A00000008B5424240" . "39424BC00000001C8034C243489CF894C244003BC24A000000" . "0EB3D8D76008DBC2700000000391C247C3D394C24047F37394" . "C24087C3189F30FB6F33974240C0F9EC3397424140F9DC183C" . "00483C20121D9884AFF39F8741E0FB658020FB648010FB6303" . "9DD7EBE31C983C00483C201884AFF39F875E28BB424B000000" . "0017424248B4C24408344241801034C24108B442418398424B" . "40000000F8546FFFFFF8B8424B00000002B44243C8944240C8" . "B8424B40000002B442438894424600F886D0900008B4424288" . "BBC24C40000008B74243CC744241000000000C744243800000" . "000C7442434000000008D3C8789C583EE01897C246C8974247" . "48B44240C85C00F88E70000008B7C24388B8424AC000000BE0" . "0000000C704240000000001F8C1E0108944246889F82B84249" . "C0000000F49F08B84249C000000897424640FAFB424B000000" . "001F8894424708974245C8DB6000000008B04240344241089C" . "1894424088B442430394424200F84AA0100008B5C241C89C60" . "38C24BC00000031C08B54242C85DB0F8EC8010000897424048" . "B7C2420EB2D39C77E1C8BB424C80000008B1C8601CB803B007" . "40B836C240401782B8D74260083C0013944241C0F849101000" . "039C57ECF8BB424C40000008B1C8601CB803B0174BE83EA017" . "9B9830424018B04243944240C0F8D68FFFFFF83442438018BB" . "424B00000008B44243801742410394424600F8DEFFEFFFF8B4" . "C243483C47889C85B5E5F5DC250008B8424900000008BB424B" . "4000000C744240C00000000C744241400000000C1E8100FB6C" . "08904248B8424900000000FB6C4894424040FB684249000000" . "0894424088B8424B0000000C1E00285F68944242489E88BAC2" . "4940000000F8E24FEFFFF8B9C24B000000085DB7E758B9C24A" . "00000008B7424148BBC24A000000003B424BC00000001C3034" . "424248944241801C78D76008DBC27000000000FB643020FB64" . "B012B04242B4C24040FB6132B5424080FAFC00FAFC98D04400" . "FAFD28D04888D045039C50F930683C30483C60139DF75C98BB" . "C24B0000000017C24148B4424188344240C01034424108B742" . "40C39B424B40000000F8566FFFFFFE985FDFFFF85ED7E358B7" . "424088BBC24BC00000031C08B54242C8D1C378BB424C400000" . "08B0C8601D9803901740983EA010F8890FEFFFF83C00139C57" . "5E683BC24D0000000070F8EAA0100008B442474030424C7442" . "44007000000896C2444894424288B8424CC00000083C020894" . "4243C8B44243C8B9424B00000008B7C24288B0029C28944245" . "08B84249800000001F839C20F4EC289C68944244C39FE0F8C0" . "90100008B44243C8B700C8B78108B6808897424148B7014897" . "C242489C7897424548BB424B40000002B700489F08B7424703" . "9C60F4EC68BB424C4000000894424188B47FC89442404C1E00" . "201C6038424C8000000894424588B4424648B7C2428037C245" . "C3B442418894424040F8F8700000085ED7E268B8C24BC00000" . "08B54242431C08D1C398B0C8601D9803901740583EA01784A8" . "3C00139C575EA8B4424148B4C245439C8747E85C07E7A8B9C2" . "4BC000000896C244831C08B6C245801FBEB0983C0013944241" . "4745C8B54850001DA803A0074EC83E90179E78B6C244890834" . "424040103BC24B00000008B442404394424180F8D79FFFFFF8" . "3442428018B4424283944244C0F8D4CFFFFFF830424018B6C2" . "4448B04243944240C0F8D7EFCFFFFE911FDFFFF8B4424288B7" . "C245083442440078344243C1C8D4438FF894424288B4424403" . "98424D00000000F8F7FFEFFFF8B6C24448B7C24348B0424038" . "424A80000008BB424D40000000B4424688D4F01398C24D8000" . "0008904BE0F8ED8FCFFFF85ED7E278B7424088BBC24BC00000" . "08B8424C40000008D1C378B74246C8B1083C00401DA39F0C60" . "20075F283042401894C24348B04243944240C0F8DDEFBFFFFE" . "971FCFFFF89F68DBC27000000008B74243C8B8424900000003" . "1D2F7F60FAF8424A40000008D0490894424188B8424B000000" . "0038424A800000029F0894424348B8424AC000000038424B40" . "000002B442438398424AC0000008944243C0F8F560400008B8" . "424A40000008BB424A80000000FAF8424AC000000C74424240" . "00000008D04B0034424188BB424A0000000894424388B44243" . "4398424A80000000F8F320100008B8424AC000000C1E010894" . "424408B442438894424148B8424A8000000894424088B44241" . "40FB67C060289C52B6C2418893C240FB67C0601897C24040FB" . "63C068B44241C85C00F8E1E0100008B442430894424108B442" . "42C8944240C31C0EB678D76008DBC2700000000394424207E4" . "A8B9C24C80000008B0C8301E90FB6540E020FB65C0E012B142" . "42B5C24040FB60C0E0FAFD20FAFDB29F98D14520FAFC98D149" . "A8D144A39942494000000720C836C2410017865908D7426008" . "3C0013944241C0F84A3000000394424287E9D8B9C24C400000" . "08B0C8301E90FB6540E020FB65C0E012B14242B5C24040FB60" . "C0E0FAFD20FAFDB29F98D14520FAFC98D149A8D144A3B94249" . "40000000F865BFFFFFF836C240C010F8950FFFFFF834424080" . "183442414048B442408394424340F8DEFFEFFFF838424AC000" . "000018BBC24A40000008B44243C017C24383B8424AC0000000" . "F8D99FEFFFF8B4C242483C4785B5E89C85F5DC250008D74260" . "08B7C24248B4424400B4424088B9C24D40000008D4F013B8C2" . "4D80000008904BB0F8D64FAFFFF894C2424EB848B842490000" . "0008B8C24B4000000C7042400000000C74424040000000083C" . "001C1E00789C68B8424B0000000C1E00285C98944240889E88" . "9F50F8EAFF8FFFF8B9424B000000085D27E5F8B8C24A000000" . "08B5C2404039C24BC00000001C1034424088944240C038424A" . "000000089C70FB651020FB641010FB6316BC04B6BD22601C28" . "9F0C1E00429F001D039C50F970383C10483C30139F975D58BB" . "424B0000000017424048B44240C83042401034424108B34243" . "9B424B40000007582E92CF8FFFF8B8424B0000000C70424000" . "00000C744240400000000C1E002894424088B8424B40000008" . "5C00F8E920000008B8424B000000085C07E6F8B8C24A000000" . "08B5C24048BB424B800000001E9036C240801DE039C24BC000" . "000896C240C03AC24A00000000FB651020FB6410183C1040FB" . "679FC83C60183C3016BC04B6BD22601C289F8C1E00429F801D" . "0C1F8078846FFC643FF0039CD75CC8BBC24B0000000017C240" . "48B6C240C83042401036C24108B0424398424B40000000F856" . "EFFFFFF83BC24B4000000020F8E60F7FFFF8B8424BC0000000" . "38424B00000008BAC24B800000003AC24B0000000C74424040" . "1000000894424088B8424B400000083E8018944240C8B8424B" . "000000083C0018944241083BC24B0000000027E798B4424108" . "9E92B8C24B00000008B5C240889EA8D34288D45FE8904240FB" . "642010FB63A0384249000000039F87C360FB67A0239F87C2E0" . "FB6790139F87C260FB63E39F87C1F0FB63939F87C180FB6790" . "239F87C100FB67EFF39F87C080FB67E0139F87D04C64301018" . "3C20183C30183C10183C6013B0C2475A3834424040103AC24B" . "00000008B4424048BBC24B0000000017C24083944240C0F855" . "8FFFFFFE96FF6FFFF83C47831C95B89C85E5F5DC2500090909" . "090909090" x64:="4157415641554154555756534881EC88000000488B84245" . "0010000488BB42450010000448B94245801000089542428448" . "944240844898C24E80000008B40048B76144C8BBC244001000" . "04C8BB42448010000C74424180000000089442430488B84245" . "00100008974241C488BB424500100008B40088B76188944243" . "C488B842450010000897424388B400C89C789442440488B842" . "4500100008B401039C7894424100F4DC74585D289442454488" . "B84245001000048894424200F8ECB000000488B442420448B0" . "8448B68048B400885C0894424040F8E940000004489CE44890" . "C244531E431FF31ED0F1F8400000000004585ED7E614863142" . "4418D5C3D0089F848039424380100004589E0EB1D0F1F0083C" . "0014D63D94183C0044183C1014883C20139C34789149E74288" . "3F9034589C2440F45D0803A3175D783C0014C63DE4183C0048" . "3C6014883C20139C34789149F75D844012C2483C50103BC241" . "80100004403A42400010000396C24047582834424180748834" . "424201C8B442418398424580100000F8F35FFFFFF83F9030F8" . "43D0600008B8424000100008BBC24080100000FAF842410010" . "0008BB424000100008D3CB88B842418010000F7D885C9448D2" . "C860F841101000083F9010F844108000083F9020F84E008000" . "08B742428C744240400000000C74424180000000089F0440FB" . "6CEC1E8104589CC0FB6D84889F08B7424080FB6D44189DB89F" . "0440FB6C64889F1C1E8100FB6CD89D60FB6C08D2C0A8B94242" . "00100004129C301C3438D040129CE4529C48904248B8424180" . "10000C1E00285D2894424080F8E660100004C89BC244001000" . "0448BBC24180100004585FF0F8E91040000488B8C24F800000" . "04863C74C6354241831D24C03942430010000488D440102EB3" . "A0F1F80000000004439C37C4039CE7F3C39CD7C384539CC410" . "F9EC044390C240F9DC14421C141880C124883C2014883C0044" . "139D70F8E2D040000440FB6000FB648FF440FB648FE4539C37" . "EBB31C9EBD58B5C2428448B8C242001000031ED4531E44889D" . "84189DB0FB6DB0FB6F48B84241801000041C1EB10450FB6DBC" . "1E0024585C98904240F8EA10000004C89BC24400100004C89B" . "42448010000448B7C2408448BB424180100004585F67E60488" . "B8C24F80000004D63D44C039424300100004863C74531C94C8" . "D440102410FB600410FB648FF410FB650FE4429D829F10FAFC" . "029DA0FAFC98D04400FAFD28D04888D04504139C7430F93040" . "A4983C1014983C0044539CE7FC4033C244501F483C5014401E" . "F39AC2420010000758C4C8BBC24400100004C8BB4244801000" . "08B8424180100002B4424308904248B8424200100002B44243" . "C894424680F88750800008B7C24404D89F5488BAC243001000" . "0448B7424104C89FEC74424040000000048C74424280000000" . "0C74424200000000089F883E801498D4487044189FF4889442" . "4088B44243083E801894424788B042485C00F88D9000000488" . "B5C24288B8424100100004D89EC448B6C245401D8C1E010894" . "4247089D82B8424F000000089C7B8000000000F49C731FF894" . "4246C0FAF842418010000894424648B8424F000000001D8894" . "42474908B442404897C24188D1C388B4424384139C60F84AB0" . "000004189C131C04585ED448B44241C7F36E9C30000000F1F4" . "0004139CE7E1B418B148401DA4863D2807C150000740B4183E" . "901782E0F1F4400004883C0014139C50F8E920000004139C78" . "9C17ECC8B148601DA4863D2807C15000174BD4183E80179B74" . "883C701393C240F8D7AFFFFFF4D89E54883442428018B9C241" . "8010000488B442428015C2404394424680F8DFCFEFFFF8B4C2" . "42089C84881C4880000005B5E5F5D415C415D415E415FC3458" . "5FF7E278B4C241C4C8B4424084889F28B0201D84898807C050" . "001740583E90178934883C2044939D075E583BC24580100000" . "70F8EE60100008B442478488B8C24500100000344241844896" . "C2450448BAC241801000044897C24404883C1204889742410C" . "744243C07000000448974244448897C24484989CF895C247C8" . "9C64C89642430418B074489EA29C28944245C8B8424E800000" . "001F039C20F4EC239F0894424580F8CD0000000418B47148BB" . "C2420010000412B7F0449635FFC458B4F08458B670C8944246" . "08B442474458B771039C70F4FF8488B44241048C1E3024C8D1" . "41848035C24308B442464448D04068B44246C39F84189C37F7" . "2904585C97E234489F131D2418B04924401C04898807C05000" . "1740583E90178464883C2014139D17FE28B4424604139C40F8" . "4AA0000004585E40F8EA100000089C131D2EB0D4883C201413" . "9D40F8E8E0000008B04934401C04898807C05000074E483E90" . "179DF4183C3014501E84439DF7D8F83C601397424580F8D6EF" . "FFFFF488B7C2448448B7C2440448B742444448B6C2450488B7" . "424104C8B6424304883C701393C240F8D97FDFFFFE918FEFFF" . "F6690037C240844017C241883442404014401EF8B442404398" . "424200100000F854DFBFFFF4C8BBC2440010000E996FCFFFF8" . "B44245C8344243C074983C71C8D7406FF8B44243C398424580" . "100000F8F87FEFFFF448B7C2440448B742444448B6C2450488" . "B7C24488B5C247C488B7424104C8B64243048634424208B542" . "418039424080100004C8B9C24600100000B5424708D4801398" . "C2468010000418914830F8E9AFDFFFF4585FF7E1D4C8B44240" . "84889F08B104883C00401DA4C39C04863D2C64415000075EB4" . "883C701393C24894C24200F8DBAFCFFFFE93BFDFFFF0F1F440" . "0008B7C24308B44242831D2F7F70FAF8424000100008D04908" . "94424208B8424180100000384240801000029F8894424308B8" . "42410010000038424200100002B44243C39842410010000894" . "424440F8F2B0400008B8424000100008BBC24080100000FAF8" . "42410010000448B642440448B6C24544C8B8C24F8000000C74" . "42428000000008D04B8034424208944243C8B4424303984240" . "80100000F8F360100008B8424100100008B6C243CC1E010894" . "424408B8424080100008904248D450289EF2B7C24204585ED4" . "898450FB61C018D45014898410FB61C014863C5410FB634010" . "F8E1C0100008B442438894424188B44241C8944240431C0EB6" . "90F1F800000000044395424107E4E418B0C8601F98D5102448" . "D41014863C9410FB60C094863D24D63C0410FB61411470FB60" . "40129F10FAFC94429DA4129D80FAFD2450FAFC08D1452428D1" . "4828D144A395424087207836C241801786B4883C0014139C50" . "F8E9F0000004139C44189C27E96418B0C8701F98D5102448D4" . "1014863C9410FB60C094863D24D63C0410FB61411470FB6040" . "129F10FAFC94429DA4129D80FAFD2450FAFC08D1452428D148" . "28D144A3B5424080F864BFFFFFF836C2404010F8940FFFFFF8" . "304240183C5048B0424394424300F8DE6FEFFFF83842410010" . "000018BBC24000100008B442444017C243C3B8424100100000" . "F8D95FEFFFF8B4C2428E95CFBFFFF48634424288B5424400B1" . "424488BBC24600100008D48013B8C24680100008914870F8D3" . "5FBFFFF8304240183C504894C24288B0424394424300F8D7AF" . "EFFFFEB92448B5C2428448B84242001000031DB8B842418010" . "00031F6448B9424180100004183C30141C1E3074585C08D2C8" . "5000000000F8E6BF9FFFF4585D27E57488B8C24F80000004C6" . "3CE4C038C24300100004863C74531C0488D4C01020FB6110FB" . "641FF440FB661FE6BC04B6BD22601C24489E0C1E0044429E00" . "1D04139C3430F9704014983C0014883C1044539C27FCC01EF4" . "401D683C3014401EF399C24200100007595E9FBF8FFFF8B8C2" . "4200100008B84241801000031DB31F6448B8C241801000085C" . "98D2C85000000007E7D4585C97E694C63C6488B8C24F800000" . "04863C74D89C24C038424300100004C0394242801000031D24" . "88D4C0102440FB6190FB641FF4883C104440FB661FA6BC04B4" . "56BDB264101C34489E0C1E0044429E04401D8C1F8074188041" . "241C60410004883C2014139D17FC401EF4401CE83C3014401E" . "F399C2420010000758383BC2420010000020F8E4BF8FFFF486" . "3B424180100008B9C24180100008BBC2420010000488D56014" . "48D67FFBF010000004889D0480394243001000048038424280" . "100004889D58D53FD4C8D6A0183BC241801000002488D1C067" . "E7E4989C04D8D5C05004989D94929F04889E90FB610440FB65" . "0FF035424284439D27C44440FB650014439D27C3A450FB6104" . "439D27C31450FB6114439D27C28450FB650FF4439D27C1E450" . "FB650014439D27C14450FB651FF4439D27C0A450FB65101443" . "9D27D03C601014883C0014983C1014883C1014983C0014C39D" . "8759383C7014801F54889D84139FC0F8562FFFFFFE968F7FFF" . "F31C9E9D9F8FFFF909090909090909090909090" MCode(MyFunc, A_PtrSize=8 ? x64:x32) } return, !bits.1 ? 0:DllCall(&MyFunc, "int",mode, "uint",color , "uint",n, "int",offsetX, "int",offsetY, Ptr,bits.1 , "int",bits.2, "int",sx, "int",sy, "int",sw, "int",sh , Ptr,&gs, Ptr,&ss, "AStr",text, Ptr,&s1, Ptr,&s0 , Ptr,&input, "int",num, Ptr,&allpos, "int",allpos_max) } MCode(ByRef code, hex) { bch:=A_BatchLines SetBatchLines, -1 VarSetCapacity(code, len:=StrLen(hex)//2) lls:=A_ListLines=0 ? "Off" : "On" ListLines, Off Loop, % len NumPut("0x" SubStr(hex,2*A_Index-1,2),code,A_Index-1,"uchar") ListLines, %lls% Ptr:=A_PtrSize ? "UPtr" : "UInt", PtrP:=Ptr "*" DllCall("VirtualProtect",Ptr,&code, Ptr,len,"uint",0x40,PtrP,0) SetBatchLines, %bch% } base64tobit(s) { Chars:="0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZ" . "abcdefghijklmnopqrstuvwxyz" SetFormat, IntegerFast, d StringCaseSense, On lls:=A_ListLines=0 ? "Off" : "On" ListLines, Off Loop, Parse, Chars { i:=A_Index-1, v:=(i>>5&1) . (i>>4&1) . (i>>3&1) . (i>>2&1) . (i>>1&1) . (i&1) s:=StrReplace(s,A_LoopField,v) } ListLines, %lls% StringCaseSense, Off s:=SubStr(s,1,InStr(s,"1",0,0)-1) s:=RegExReplace(s,"[^01]+") return, s } bit2base64(s) { s:=RegExReplace(s,"[^01]+") s.=SubStr("100000",1,6-Mod(StrLen(s),6)) s:=RegExReplace(s,".{6}","|$0") Chars:="0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZ" . "abcdefghijklmnopqrstuvwxyz" SetFormat, IntegerFast, d lls:=A_ListLines=0 ? "Off" : "On" ListLines, Off Loop, Parse, Chars { i:=A_Index-1, v:="|" . (i>>5&1) . (i>>4&1) . (i>>3&1) . (i>>2&1) . (i>>1&1) . (i&1) s:=StrReplace(s,v,A_LoopField) } ListLines, %lls% return, s } ASCII(s) { if RegExMatch(s,"$(d+).([w+/]+)",r) { s:=RegExReplace(base64tobit(r2),".{" r1 "}","$0`n") s:=StrReplace(StrReplace(s,"0","_"),"1","0") } else s= return, s } ; 可以在脚本的开头用 PicLib(Text,1) 导入字库, ; 然后使用 PicLib("说明文字1|说明文字2|...") 获取字库中的数据 PicLib(comments, add_to_Lib:=0, index:=1) { static Lib:=[] SetFormat, IntegerFast, d if (add_to_Lib) { re:="<([^>]*)>[^$]+$d+.[w+/]+" Loop, Parse, comments, | if RegExMatch(A_LoopField,re,r) { s1:=Trim(r1), s2:="" Loop, Parse, s1 s2.="_" . Ord(A_LoopField) Lib[index,s2]:=r } Lib[index,""]:="" } else { Text:="" Loop, Parse, comments, | { s1:=Trim(A_LoopField), s2:="" Loop, Parse, s1 s2.="_" . Ord(A_LoopField) Text.="|" . Lib[index,s2] } return, Text } } PicN(Number, index:=1) { return, PicLib(RegExReplace(Number,".","|$0"), 0, index) } ; 使用 PicX(Text) 可以将文字分割成多个单字的组合,从而适应间隔变化 ; 但是不能用于“颜色位置二值化”模式, 因为位置是与整体图像相关的 PicX(Text) { if !RegExMatch(Text,"|([^$]+)$(d+).([w+/]+)",r) return, Text w:=r2, v:=base64tobit(r3), Text:="" c:=StrLen(StrReplace(v,"0"))<=StrLen(v)//2 ? "1":"0" wz:=RegExReplace(v,".{" w "}","$0`n") SetFormat, IntegerFast, d While InStr(wz,c) { While !(wz~="m`n)^" c) wz:=RegExReplace(wz,"m`n)^.") i:=0 While (wz~="m`n)^.{" i "}" c) i++ v:=RegExReplace(wz,"m`n)^(.{" i "}).*","$1") wz:=RegExReplace(wz,"m`n)^.{" i "}") if (v!="") Text.="|" r1 "$" i "." bit2base64(v) } return, Text } ; 截屏,作为后续操作要用的“上一次的截屏” ScreenShot(x1:="", y1:="", x2:="", y2:="") { if (x1+y1+x2+y2="") n:=150000, x:=y:=-n, w:=h:=2*n else x:=(x1<x2 ? x1:x2), y:=(y1<y2 ? y1:y2) , w:=Abs(x2-x1)+1, h:=Abs(y2-y1)+1 xywh2xywh(x,y,w,h,x,y,w,h,zx,zy,zw,zh) GetBitsFromScreen(x,y,w,h,1,zx,zy,zw,zh) } ; 从“上一次的截屏”中快速获取指定坐标的RGB颜色 ; 如果坐标超出了屏幕范围,将返回白色 ScreenShot_GetColor(x,y) { bits:=GetBitsFromScreen(0,0,0,0,0,zx,zy,zw,zh) return, (x<zx or x>zx+zw-1 or y<zy or y>zy+zh-1 or !bits.1) ? "0xFFFFFF" : Format("0x{:06X}",NumGet(bits.1 +(y-zy)*bits.2+(x-zx)*4,"uint")&0xFFFFFF) } ; 根据 FindText() 的结果识别一行文字或验证码 ; offsetX 为两个文字的最大间隔,超过会插入*号 ; offsetY 为后续文字与第一个文字的最大高度差 ; 最后返回数组:{ocr:识别结果, x:结果左上角X, y:结果左上角Y} OcrOK(ok, offsetX:=20, offsetY:=20) { ocr_Text:=ocr_X:=ocr_Y:=min_X:="" For k,v in ok x:=v.1 , min_X:=(A_Index=1 or x<min_X ? x : min_X) , max_X:=(A_Index=1 or x>max_X ? x : max_X) While (min_X!="" and min_X<=max_X) { LeftX:="" For k,v in ok { x:=v.1, y:=v.2, w:=v.3, h:=v.4 if (x<min_X) or Abs(y-ocr_Y)>offsetY Continue ; Get the leftmost X coordinates if (LeftX="" or x<LeftX) LeftX:=x, LeftY:=y, LeftW:=w, LeftH:=h, LeftOCR:=v.id else if (x=LeftX) { Loop, 100 { err:=(A_Index-1)/100+0.000001 if FindText(LeftX,LeftY,LeftX+LeftW-1,LeftY+LeftH-1,err,err,Text,0) Break if FindText(x, y, x+w-1, y+h-1, err, err, Text, 0) { LeftX:=x, LeftY:=y, LeftW:=w, LeftH:=h, LeftOCR:=v.id Break } } } } if (ocr_X="") ocr_X:=LeftX, ocr_Y:=LeftY ; If the interval exceeds the set value, add "*" to the result ocr_Text.=(ocr_Text!="" and LeftX-min_X>offsetX ? "*":"") . LeftOCR ; Update min_X for next search min_X:=LeftX+LeftW } return, {ocr:ocr_Text, x:ocr_X, y:ocr_Y} } ; 按照从左到右、从上到下的顺序排序FindText()的结果 ; 忽略轻微的Y坐标差距,返回排序后的数组对象 SortOK(ok, dy:=10) { if !IsObject(ok) return, ok SetFormat, IntegerFast, d ypos:=[] For k,v in ok { x:=v.x, y:=v.y, add:=1 For k2,v2 in ypos if Abs(y-v2)<=dy { y:=v2, add:=0 Break } if (add) ypos.Push(y) n:=(y*150000+x) "." k, s:=A_Index=1 ? n : s "-" n } Sort, s, N D- ok2:=[] Loop, Parse, s, - ok2.Push( ok[(StrSplit(A_LoopField,".")[2])] ) return, ok2 } ; 以指定点为中心,按从近到远排序FindText()的结果 ; 返回排序后的数组对象 SortOK2(ok, px, py) { if !IsObject(ok) return, ok SetFormat, IntegerFast, d For k,v in ok { x:=v.1+v.3//2, y:=v.2+v.4//2 n:=((x-px)**2+(y-py)**2) "." k s:=A_Index=1 ? n : s "-" n } Sort, s, N D- ok2:=[] Loop, Parse, s, - ok2.Push( ok[(StrSplit(A_LoopField,".")[2])] ) return, ok2 } ; 提示某个坐标的位置,或远程控制中当前鼠标的位置 MouseTip(x:="", y:="") { if (x="") { VarSetCapacity(pt,16,0), DllCall("GetCursorPos","ptr",&pt) x:=NumGet(pt,0,"uint"), y:=NumGet(pt,4,"uint") } x:=Round(x-10), y:=Round(y-10), w:=h:=2*10+1 ;------------------------- Gui, _MouseTip_: +AlwaysOnTop -Caption +ToolWindow +Hwndmyid +E0x08000000 Gui, _MouseTip_: Show, Hide w%w% h%h% ;------------------------- dhw:=A_DetectHiddenWindows DetectHiddenWindows, On d:=4, i:=w-d, j:=h-d s=0-0 %w%-0 %w%-%h% 0-%h% 0-0 s=%s% %d%-%d% %i%-%d% %i%-%j% %d%-%j% %d%-%d% WinSet, Region, %s%, ahk_id %myid% DetectHiddenWindows, %dhw% ;------------------------- Gui, _MouseTip_: Show, NA x%x% y%y% Loop, 4 { Gui, _MouseTip_: Color, % A_Index & 1 ? "Red" : "Blue" Sleep, 500 } Gui, _MouseTip_: Destroy } /***** 机器码的 C语言 源代码 ***** int __attribute__((__stdcall__)) PicFind( int mode, unsigned int c, unsigned int n , int offsetX, int offsetY, unsigned char * Bmp , int Stride, int sx, int sy, int sw, int sh , unsigned char * gs, char * ss, char * text , int * s1, int * s0, int * input, int num , unsigned int * allpos, int allpos_max) { int o, i, j, x, y, r, g, b, rr, gg, bb, max, e1, e0, ok; int o1, x1, y1, w1, h1, sx1, sy1, len1, len0, err1, err0; int o2, x2, y2, w2, h2, sx2, sy2, len21, len20, err21, err20; int r_min, r_max, g_min, g_max, b_min, b_max; //---------------------- ok=0; w1=input[1]; h1=input[2]; len1=input[3]; len0=input[4]; err1=input[5]; err0=input[6]; max=len1>len0 ? len1 : len0; //---------------------- // 生成查表需要的表格 for (j=0; j<num; j+=7) { o=o1=o2=input[j]; w2=input[j+1]; h2=input[j+2]; for (y=0; y<h2; y++) { for (x=0; x<w2; x++) { i=(mode==3) ? y*Stride+x*4 : y*sw+x; if (text[o++]=='1') s1[o1++]=i; else s0[o2++]=i; } } } // 颜色位置模式 // 此模式不支持文字组合查找,仅用于多色验证码的识别 if (mode==3) { c=(c/w1)*Stride+(c%w1)*4; sx1=sx+sw-w1; sy1=sy+sh-h1; for (y=sy; y<=sy1; y++) { for (x=sx; x<=sx1; x++) { o=y*Stride+x*4; e1=err1; e0=err0; j=o+c; rr=Bmp[2+j]; gg=Bmp[1+j]; bb=Bmp[j]; for (i=0; i<max; i++) { if (i<len1) { j=o+s1[i]; r=Bmp[2+j]-rr; g=Bmp[1+j]-gg; b=Bmp[j]-bb; if (3*r*r+4*g*g+2*b*b>n && (--e1)<0) goto NoMatch3; } if (i<len0) { j=o+s0[i]; r=Bmp[2+j]-rr; g=Bmp[1+j]-gg; b=Bmp[j]-bb; if (3*r*r+4*g*g+2*b*b<=n && (--e0)<0) goto NoMatch3; } } allpos[ok++]=y<<16|x; if (ok>=allpos_max) goto Return1; NoMatch3: continue; } } goto Return1; } // 生成二值化图像 o=sy*Stride+sx*4; j=Stride-4*sw; i=0; if (mode==0) // 颜色相似二值化 { rr=(c>>16)&0xFF; gg=(c>>8)&0xFF; bb=c&0xFF; for (y=0; y<sh; y++, o+=j) for (x=0; x<sw; x++, o+=4, i++) { r=Bmp[2+o]-rr; g=Bmp[1+o]-gg; b=Bmp[o]-bb; ss[i]=(3*r*r+4*g*g+2*b*b<=n) ? 1:0; } } else if (mode==1) // 灰度阈值二值化 { c=(c+1)*128; for (y=0; y<sh; y++, o+=j) for (x=0; x<sw; x++, o+=4, i++) ss[i]=(Bmp[2+o]*38+Bmp[1+o]*75+Bmp[o]*15<c) ? 1:0; } else if (mode==2) // 灰度差值二值化 { for (y=0; y<sh; y++, o+=j) { for (x=0; x<sw; x++, o+=4, i++) { gs[i]=(Bmp[2+o]*38+Bmp[1+o]*75+Bmp[o]*15)>>7; ss[i]=0; } } sx1=sw-2; sy1=sh-2; for (y=1; y<=sy1; y++) for (x=1; x<=sx1; x++) { i=y*sw+x; j=gs[i]+c; if ( gs[i-1]>j || gs[i+1]>j || gs[i-sw]>j || gs[i+sw]>j || gs[i-sw-1]>j || gs[i-sw+1]>j || gs[i+sw-1]>j || gs[i+sw+1]>j ) ss[i]=1; } } else // (mode==4) 颜色分量二值化 { r=(c>>16)&0xFF; g=(c>>8)&0xFF; b=c&0xFF; rr=(n>>16)&0xFF; gg=(n>>8)&0xFF; bb=n&0xFF; r_min=r-rr; g_min=g-gg; b_min=b-bb; r_max=r+rr; g_max=g+gg; b_max=b+bb; for (y=0; y<sh; y++, o+=j) for (x=0; x<sw; x++, o+=4, i++) { r=Bmp[2+o]; g=Bmp[1+o]; b=Bmp[o]; ss[i]=(r>=r_min && r<=r_max && g>=g_min && g<=g_max && b>=b_min && b<=b_max) ? 1:0; } } // 开始查找 sx1=sw-w1; sy1=sh-h1; for (y=0; y<=sy1; y++) { for (x=0; x<=sx1; x++) { o=y*sw+x; e1=err1; e0=err0; if (e0==len0) { for (i=0; i<len1; i++) if (ss[o+s1[i]]!=1 && (--e1)<0) goto NoMatch1; } else { for (i=0; i<max; i++) { if (i<len1 && ss[o+s1[i]]!=1 && (--e1)<0) goto NoMatch1; if (i<len0 && ss[o+s0[i]]!=0 && (--e0)<0) goto NoMatch1; } } //------------------ // 组合查找 if (num>7) { x1=x+w1-1; y1=y-offsetY; if (y1<0) y1=0; for (j=7; j<num; j+=7) { o2=input[j]; w2=input[j+1]; h2=input[j+2]; len21=input[j+3]; len20=input[j+4]; err21=input[j+5]; err20=input[j+6]; sx2=sw-w2; i=x1+offsetX; if (i<sx2) sx2=i; sy2=sh-h2; i=y+offsetY; if (i<sy2) sy2=i; for (x2=x1; x2<=sx2; x2++) { for (y2=y1; y2<=sy2; y2++) { o1=y2*sw+x2; e1=err21; e0=err20; for (i=0; i<len21; i++) { if (ss[o1+s1[o2+i]]!=1 && (--e1)<0) goto NoMatch2; } if (e0!=len20) { for (i=0; i<len20; i++) if (ss[o1+s0[o2+i]]!=0 && (--e0)<0) goto NoMatch2; } goto MatchOK; NoMatch2: continue; } } goto NoMatch1; MatchOK: x1=x2+w2-1; } } //------------------ allpos[ok++]=(sy+y)<<16|(sx+x); if (ok>=allpos_max) goto Return1; // 清空已经找到的图像 for (i=0; i<len1; i++) ss[o+s1[i]]=0; NoMatch1: continue; } } Return1: return ok; } */ ;================= The End ================= ; ; ############################################################################################################# ; # Verdlin's INI library ; # Original thread: https://autohotkey.com/board/topic/91578-class-easyini-native-syntax-inisectionkey-val-formatting-retained/ ; # Original source code: https://github.com/Aatoz/AutoHotKey/blob/master/Lib/class_EasyIni.ahk ; ############################################################################################################# ; ############################################################################################################# ; # Modified by dein0s ; # Source code: https://github.com/dein0s/AHK_Snippets/blob/master/EasyIni.ahk ; # List of all functions and class methods at the end of this file ; # ; # Github: https://github.com/dein0s ; # Twitter: https://twitter.com/dein0s ; # Discord: dein0s#2248 ; # ; # Modified parts marked as following: ; # --- MODIFICATION START (dein0s) --- ; # --- MODIFICATION END (dein0s) ---; ; ############################################################################################################# class_EasyIni(sFile="", sLoadFromStr="") { return new EasyIni(sFile, sLoadFromStr) } class EasyIni { __New(sFile="", sLoadFromStr="") ; Loads ths file into memory. { this := this.CreateIniObj("EasyIni_ReservedFor_m_sFile", sFile , "EasyIni_ReservedFor_TopComments", Object()) ; Top comments can be stored in linear array because order will simply be numeric if (sFile == A_Blank && sLoadFromStr == A_Blank) return this ; Append ".ini" if it is not already there. if (SubStr(sFile, StrLen(sFile)-3, 4) != ".ini") this.EasyIni_ReservedFor_m_sFile := sFile := (sFile . ".ini") sIni := sLoadFromStr if (sIni == A_Blank) FileRead, sIni, %sFile% /* Current design (not fully implemented): --------------------------------------------------------------------------------------------------------------------------------------------------- Comments at the top of the section apply to the file as a whole. They are keyed off an internal section called "EasyIni_ReservedFor_TopComments." Comments above section headers apply to the the last key of the previous section. If a comment appears between two keys, then it will apply to the key above it -- this is consistent with the solution for comments above section headers. Newlines will be stored in similar fashion to comments. --------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------- If full-support for comments needs to be added, then the design below should supersede the design above. By saying, "Full-support" I mean a way to directly access these comments based upon sections and keys. --------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------- Comments at the top of the section apply to the file as a whole. They are keyed off an internal section called "EasyIni_ReservedFor_TopComments." Comments above section headers apply to the section header. If people dislike this, I may instead chose to make the comments apply to the last key of the previous section if there a newline in-between the comment in question and the next section. I may come up with some decent solution as I experiment If a comment appears between two keys, then it will apply to the key below it -- this is consistent with the solution for comments above section headers. Newlines will be stored in similar fashion to comments. --------------------------------------------------------------------------------------------------------------------------------------------------- */ Loop, Parse, sIni, `n, `r { sTrimmedLine := Trim(A_LoopField) ; Comments or newlines within the ini if (SubStr(sTrimmedLine, 1, 1) == ";" || sTrimmedLine == A_Blank) ; A_Blank would be a newline { ; Chr(14) is just the magical char to indicate that this line should only be a newline "`n" LoopField := A_LoopField == A_Blank ? Chr(14) : A_LoopField if (sCurSec == A_Blank) this.EasyIni_ReservedFor_TopComments.Insert(A_Index, LoopField) ; not using sTrimmedLine so as to keep comment formatting else { if (sPrevKeyForThisSec == A_Blank) ; This happens when there is a comment in the section before the first key, if any sPrevKeyForThisSec := "SectionComment" if (IsObject(this[sCurSec].EasyIni_ReservedFor_Comments)) { if (this[sCurSec].EasyIni_ReservedFor_Comments.HasKey(sPrevKeyForThisSec)) this[sCurSec].EasyIni_ReservedFor_Comments[sPrevKeyForThisSec] .= "`n" LoopField else this[sCurSec].EasyIni_ReservedFor_Comments.Insert(sPrevKeyForThisSec, LoopField) } else { if (IsObject(this[sCurSec])) this[sCurSec].EasyIni_ReservedFor_Comments := {(sPrevKeyForThisSec):LoopField} else this[sCurSec, "EasyIni_ReservedFor_Comments"] := {(sPrevKeyForThisSec):LoopField} } } continue } ; [Section] if (SubStr(sTrimmedLine, 1, 1) = "[" && InStr(sTrimmedLine, "]")) ; need to be sure that this isn't just a key starting with "[" { if (sCurSec != A_Blank && !this.HasKey(sCurSec)) this[sCurSec] := EasyIni_CreateBaseObj() sCurSec := SubStr(sTrimmedLine, 2, InStr(sTrimmedLine, "]", false, 0) - 2) ; 0 search right to left. We want to trim the *last* occurence of "]" sPrevKeyForThisSec := "" continue } ; key=val iPosOfEquals := InStr(sTrimmedLine, "=") if (iPosOfEquals) { sPrevKeyForThisSec := SubStr(sTrimmedLine, 1, iPosOfEquals - 1) ; so it's not the previous key yet...but it will be on next iteration :P val := SubStr(sTrimmedLine, iPosOfEquals + 1) StringReplace, val, val , `%A_ScriptDir`%, %A_ScriptDir%, All StringReplace, val, val , `%A_WorkingDir`%, %A_ScriptDir%, All this[sCurSec, sPrevKeyForThisSec] := val } else ; at this point, we know it isn't a comment, or newline, it isn't a section, and it isn't a conventional key-val pair. Treat this line as a key with no val { sPrevKeyForThisSec := sTrimmedLine this[sCurSec, sPrevKeyForThisSec] := "" } } ; if there is a section with no keys and it is at the bottom of the file, then we missed it if (sCurSec != A_Blank && !this.HasKey(sCurSec)) this[sCurSec] := EasyIni_CreateBaseObj() return this } CreateIniObj(parms*) { ; Define prototype object for ini arrays: ; --- MODIFICATION START (dein0s) --- static base := {__Set: "EasyIni_Set", _NewEnum: "EasyIni_NewEnum", Delete: "Delete", Remove: "EasyIni_Remove", Insert: "EasyIni_Insert", InsertBefore: "EasyIni_InsertBefore", AddSection: "EasyIni.AddSection", RenameSection: "EasyIni.RenameSection", DeleteSection: "EasyIni.DeleteSection", GetSections: "EasyIni.GetSections", FindSecs: "EasyIni.FindSecs", AddKey: "EasyIni.AddKey", RenameKey: "EasyIni.RenameKey", DeleteKey: "EasyIni.DeleteKey", RemoveKey: "EasyIni.RemoveKey", GetKeys: "EasyIni.GetKeys", FindKeys: "EasyIni.FindKeys", GetVals: "EasyIni.GetVals", FindVals: "EasyIni.FindVals", HasVal: "EasyIni.HasVal", SetKeyVal: "EasyIni.SetKeyVal", GetCommentContent: "EasyIni.GetCommentContent", GetTopComments: "EasyIni.GetTopComments", GetSectionComments: "EasyIni.GetSectionComments", GetKeyComments: "EasyIni.GetKeyComments", AddComment: "EasyIni.AddComment", AddTopComment: "EasyIni.AddTopComment", AddSectionComment: "EasyIni.AddSectionComment", AddKeyComment: "EasyIni.AddKeyComment", DeleteComment: "EasyIni.DeleteComment", Update: "EasyIni.Update", Compare: "EasyIni.Compare", Copy: "EasyIni.Copy", Merge: "EasyIni.Merge", GetFileName: "EasyIni.GetFileName", GetOnlyIniFileName:"EasyIni.GetOnlyIniFileName", IsEmpty:"EasyIni.IsEmpty", Reload: "EasyIni.Reload", GetIsSaved: "EasyIni.GetIsSaved", Save: "EasyIni.Save", ToVar: "EasyIni.ToVar", GetValue: "EasyIni.GetValue"} ; --- MODIFICATION END (dein0s) --- ; Create and return new object: return Object("_keys", Object(), "base", base, parms*) } AddSection(sec, key="", val="", ByRef rsError="") { if (this.HasKey(sec)) { rsError := "Error! Cannot add new section [" sec "], because it already exists." MsgBox, %rsError% return false } if (key == A_Blank) this[sec] := EasyIni_CreateBaseObj() else this[sec, key] := val return true } RenameSection(sOldSec, sNewSec, ByRef rsError="") { if (!this.HasKey(sOldSec)) { rsError := "Error! Could not rename section [" sOldSec "], because it does not exist." MsgBox, %rsError% return false } if (sOldSec = sNewSec) ; EasyIni is case-insensitve. return true ; true because the rename is harmless. this[sNewSec] := this[sOldSec] this.DeleteSection(sOldSec) return true } DeleteSection(sec) { r := this.Remove(sec) return r } GetSections(sDelim="`n", sSort="") { for sec in this secs .= (A_Index == 1 ? sec : sDelim sec) if (sSort) Sort, secs, D%sDelim% %sSort% return secs } FindSecs(sExp, iMaxSecs="") { aSecs := [] for sec in this { if (RegExMatch(sec, sExp)) { aSecs.Insert(sec) if (iMaxSecs&& aSecs.MaxIndex() == iMaxSecs) return aSecs } } return aSecs } AddKey(sec, key, val="", ByRef rsError="") { if (this.HasKey(sec)) { if (this[sec].HasKey(key)) { rsError := "Error! Could not add key, " key " because there is a key in the same section:`nSection: " sec "`nKey: " key MsgBox, %rsError% return false } } else { rsError := "Error! Could not add key, " key " because Section, " sec " does not exist." MsgBox, %rsError% return false } this[sec, key] := val return true } RenameKey(sec, OldKey, NewKey, ByRef rsError="") { if (!this[sec].HasKey(OldKey)) { rsError := "Error! The specified key " OldKey " could not be modified because it does not exist." MsgBox, %rsError% return false } ValCopy := this[sec][OldKey] ; --- MODIFICATION START (dein0s) --- CommentCopy := this.GetKeyComments(sec, OldKey) this.RemoveKey(sec, OldKey) this.AddKey(sec, NewKey) if (!IsStringEmpty(CommentCopy)) { this.AddKeyComment(sec, NewKey, CommentCopy) } ; --- MODIFICATION END (dein0s) --- this[sec][NewKey] := ValCopy return true } DeleteKey(sec, key) { this[sec].Delete(key) return } ; --- MODIFICATION START (dein0s) --- ; DeleteKey() provides some inconsistency (key is still saved into the file, but with empty value) RemoveKey(sec, key) { this[sec].Remove(key) return } ; --- MODIFICATION END (dein0s) --- GetKeys(sec, sDelim="`n", sSort="") { for key in this[sec] keys .= A_Index == 1 ? key : sDelim key if (sSort) Sort, keys, D%sDelim% %sSort% return keys } FindKeys(sec, sExp, iMaxKeys="") { aKeys := [] for key in this[sec] { if (RegExMatch(key, sExp)) { aKeys.Insert(key) if (iMaxKeys && aKeys.MaxIndex() == iMaxKeys) return aKeys } } return aKeys } ; Non-regex, exact match on key ; returns key(s) and their assocationed section(s) FindExactKeys(key, iMaxKeys="") { aKeys := {} for sec, aData in this { if (aData.HasKey(key)) { aKeys.Insert(sec, key) if (iMaxKeys && aKeys.MaxIndex() == iMaxKeys) return aKeys } } return aKeys } GetVals(sec, sDelim="`n", sSort="") { for key, val in this[sec] vals .= A_Index == 1 ? val : sDelim val if (sSort) Sort, vals, D%sDelim% %sSort% return vals } FindVals(sec, sExp, iMaxVals="") { aVals := [] for key, val in this[sec] { if (RegExMatch(val, sExp)) { aVals.Insert(val) if (iMaxVals && aVals.MaxIndex() == iMaxVals) break } } return aVals } HasVal(sec, FindVal) { for k, val in this[sec] if (FindVal = val) return true return false } ; --- MODIFICATION START (dein0s) --- SetKeyVal(sec, key, val, ByRef rsError="") { if (!this.HasKey(sec)) { rsError := "Error! Could not set value '" val "' for key '" key "' because Section [" sec "] does not exist." MsgBox, %rsError% return false } if (!this[sec].HasKey(key)) { rsError := "Error! Could not set value '" val "' for key '" key "' because key does not exist in Section [" sec "]." MsgBox, %rsError% return false } this[sec, key] := val return true } GetCommentContent(sec="", key="", topComment=false) { if (topComment) { commentsObj := this.EasyIni_ReservedFor_TopComments } else { commentsObj := StrSplit(this[sec].EasyIni_ReservedFor_Comments[key], "`n") } for commentIndex, commentContent in commentsObj { if (!IsStringEmpty(commentContent)) { sComments .= commentContent "`n" } } return sComments } GetTopComments() { return this.GetCommentContent( , , true) } GetSectionComments(sec) { return this.GetCommentContent(sec, "SectionComment") } GetKeyComments(sec, key) { return this.GetCommentContent(sec, key) } AddComment(sec="", key="", comment="", topComment=false, ByRef rsError="") { for commentIndex, commentContent in StrSplit(comment, "`n") { if (!IsStringEmpty(commentContent)) { if (InStr(commentContent, ";") != 1) { commentContent := "; " commentContent } if (topComment) { if (!this.HasKey("EasyIni_ReservedFor_TopComments")) { this.Insert("EasyIni_ReservedFor_TopComments", []) } this.EasyIni_ReservedFor_TopComments.Insert(commentContent) } else { if (!this.HasKey(sec)) { if (key == "SectionComment") { rsError := "Error! Could not add comment to Section [" sec "] because it does not exist." } else { rsError := "Error! Could not add comment to key '" key "' because Section [" sec "] does not exist." } MsgBox, %rsError% return false } if (key != "SectionComment" and !this[sec].HasKey(key)) { rsError := "Error! Could not add comment to key '" key "' because this key does not exist in Section [" sec "]." MsgBox, %rsError% return false } if (!IsObject(this[sec].EasyIni_ReservedFor_Comments)) { this[sec].EasyIni_ReservedFor_Comments := {} } commentCurrent := this[sec].EasyIni_ReservedFor_Comments[key] if (IsStringEmpty(commentCurrent)) { this[sec].EasyIni_ReservedFor_Comments.Insert(key, commentContent) } else { this[sec].EasyIni_ReservedFor_Comments.Insert(key, commentCurrent "`n" commentContent) } } } } return true } AddTopComment(comment, ByRef rsError="") { return this.AddComment( , , comment, true, rsError) } AddSectionComment(sec, comment, ByRef rsError="") { return this.AddComment(sec, "SectionComment", comment, , rsError) } AddKeyComment(sec, key, comment, ByRef rsError="") { return this.AddComment(sec, key, comment, , rsError) } DeleteComment(sec="", key="", comment="", topComment=false, ByRef rsError="") { for commentIndex, commentContent in StrSplit(comment, "`n") { if (!IsStringEmpty(commentContent)) { if (topComment) { for commentIndexTop, commentContentTop in this.EasyIni_ReservedFor_TopComments { if (commentContentTop ~= commentContent) { this.EasyIni_ReservedFor_TopComments.Delete(commentIndexTop) } } } else { if (!this.HasKey(sec)) { if (key == "SectionComment") { rsError := "Error! Could not delete comment from Section [" sec "] because it does not exist." } else { rsError := "Error! Could not remove comment from key '" key "' because Section [" sec "] does not exist." } MsgBox, %rsError% return false } else { if (key != "SectionComment" and !this[sec].HasKey(key)) { rsError := "Error! Could not delete comment from key '" key "' because this key does not exist in Section [" sec "]." MsgBox, %rsError% return false } for commentIndexCurrent, commentContentCurrent in StrSplit(this[sec].EasyIni_ReservedFor_Comments[key], "`n") { if (commentContentCurrent ~= commentContent) { continue } else { commentCurrentStr .= commentContentCurrent "`n" } } if (!IsStringEmpty(commentCurrentStr)) { this[sec].EasyIni_ReservedFor_Comments.Insert(key, commentCurrentStr) } else { this[sec].EasyIni_ReservedFor_Comments.Delete(key) } } } } } return true } DeleteTopComment(comment, ByRef rsError="") { return this.DeleteComment( , , comment, true, rsError) } DeleteSectionComment(sec, comment, ByRef rsError="") { return this.DeleteComment(sec, "SectionComment", comment, , rsError) } DeleteKeyComment(sec, key, comment, ByRef rsError="") { return this.DeleteComment(sec, key, comment, , rsError) } Update(SourceIni, sections=true, keys=true, values=false, top_comments=false, section_comments=true, key_comments=true, repeatedRecursions=0) ; TODO: add docstring { if (!IsObject(SourceIni)) { SourceIni := class_EasyIni(SourceIni) } if (SourceIni.IsEmpty()) { return } ; Add new items from SourceIni object if (top_comments) { for commentIndex, commentContent in StrSplit(SourceIni.GetTopComments(), "`n") { ; Add new top comment if (!InStr(this.GetTopComments(), commentContent)) { this.AddTopComment(commentContent) } } } for sectionName, sectionKeys in SourceIni { ; Add new section if (sections and !this.HasKey(sectionName)) { this.AddSection(sectionName) } ; Add new section comment if (section_comments and this.HasKey(sectionName)) { for commentIndex, commentContent in StrSplit(SourceIni.GetSectionComments(sectionName), "`n") { if (!InStr(this.GetSectionComments(sectionName), commentContent)) { this.AddSectionComment(sectionName, commentContent) } } } for keyName, keyVal in sectionKeys { ; Add new key if (keys and !this[sectionName].HasKey(keyName)) { this.AddKey(sectionName, keyName, keyVal) } if (this[sectionName].HasKey(keyName)) { ; Set new key value if (values) { this.SetKeyVal(sectionName, keyName, keyVal) } ; Add new key comment if (key_comments) { for commentIndex, commentContent in StrSplit(SourceIni.GetKeyComments(sectionName, keyName), "`n") { if (!InStr(this.GetKeyComments(sectionName, keyName), commentContent)) { this.AddKeyComment(sectionName, keyName, commentContent) } } } } } } ; Remove old items from current EasyIni object if (top_comments) { for commentIndex, commentContent in StrSplit(this.GetTopComments(), "`n") { ; Remove old top comment if (!InStr(SourceIni.GetTopComments(), commentContent)) { this.DeleteTopComment(commentContent) } } } removeSectionsList := [] for sectionName, sectionKeys in this { ; Remove old section, remember Section, remove later to not mess up the object index. if (sections and !SourceIni.HasKey(sectionName)) { removeSectionsList.push(sectionName) } ; Remove old section comment if (section_comments and SourceIni.HasKey(sectionName)) { for commentIndex, commentContent in StrSplit(this.GetSectionComments(sectionName), "`n") { if (!InStr(SourceIni.GetSectionComments(sectionName), commentContent)) { this.DeleteSection(sectionName, commentContent) } } } for keyName, keyVal in sectionKeys { ; Remove old key if (keys and !SourceIni[sectionName].HasKey(keyName)) { this.RemoveKey(sectionName, keyName) } ; Remove old key comment if (key_comments and SourceIni[sectionName].HasKey(keyName)){ for commentIndex, commentContent in StrSplit(this.GetKeyComments(sectionName, keyName), "`n") { if (!InStr(SourceIni.GetKeyComments(sectionName, keyName), commentContent)) { this.DeleteKeyComment(sectionName, keyName, commentContent) } } } } } Loop, % removeSectionsList.MaxIndex() { this.DeleteSection(removeSectionsList[A_Index]) } return } Compare(SourceIni, sections=true, keys=true, values=false, comments=false) ; TODO: add docstring { if (!IsObject(SourceIni)) { SourceIni := class_EasyIni(SourceIni) } if (sections) { if (this.GetSections("|", "C") != SourceIni.GetSections("|", "C")) { return false } } if (keys) { for sectionIndex, sectionName in StrSplit(this.GetSections("|", "C"), "|") { if (this.GetKeys(sectionName, "|", "C") != SourceIni.GetKeys(sectionName, "|", "C")) { return false } } } if (comments) { for commentIndex, commentContent in this.EasyIni_ReservedFor_TopComments { sTopComments .= (A_Index == 1) ? commentContent : "|" commentContent Sort, sTopComments, "|" "C" } for commentIndex, commentContent in SourceIni.EasyIni_ReservedFor_TopComments { sTopCommentsSource .= (A_Index == 1) ? commentContent : "|" commentContent Sort, sTopCommentsSource, "|" "C" } if (sTopComments != sTopCommentsSource) { return false } for sectionIndex, sectionName in StrSplit(this.GetSections("|", "C"), "|") { for commentKey, commentContent in this[sectionName].EasyIni_ReservedFor_Comments { sAllSectionComments .= (A_Index == 1) ? commentContent : "|" commentContent Sort, sAllSectionComments, "|" "C" } for commentKey, commentContent in SourceIni[sectionName].EasyIni_ReservedFor_Comments { sAllSectionCommentsSource .= (A_Index == 1) ? commentContent : "|" commentContent Sort, sAllSectionCommentsSource, "|" "C" } if (sAllSectionComments != sAllSectionCommentsSource) { return false } } } return true } ; --- MODIFICATION END (dein0s) --- ; SourceIni: May be EasyIni object or simply a path to an ini file. ; bCopyFileName = true: Allow copying of data without copying the file name. Copy(SourceIni, bCopyFileName = true) { ; Get ini as string. if (IsObject(SourceIni)) sIniString := SourceIni.ToVar() else FileRead, sIniString, %SourceIni% ; Effectively make this function static by allowing calls via EasyIni.Copy. if (IsObject(this)) { if (bCopyFileName) sOldFileName := this.GetFileName() this := A_Blank ; avoid any copy constructor issues. ; ObjClone doesn't work consistently. It's likely a problem with the meta-function overrides, ; but this is a nice, quick hack. this := class_EasyIni(SourceIni.GetFileName(), sIniString) ; Restore file name. this.EasyIni_ReservedFor_m_sFile := sOldFileName } else return class_EasyIni(bCopyFileName ? SourceIni.GetFileName() : "", sIniString) return this } ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; /* Author: Verdlin STILL UNDER CONSTRUCTION. Need to handle comments, and then this will be complete. Function: Merge Purpose: Merge two EasyIni objects. Parameters vOtherIni: Other EasyIni object to merge with this. bRemoveNonMatching: If true, removes sections and keys that do not exist in both inis. bOverwriteMatching: If true, any key that exists in both objects will use the val from vOtherIni. vExceptionsIni: class_Easy ini object full of exceptions keys for secs. Any matching key will remain unchanged. */ Merge(vOtherIni, bRemoveNonMatching = false, bOverwriteMatching = false, vExceptionsIni = "") { ; TODO: Perhaps just save one ini, read it back in, and then perform merging? I think this would help with formatting. ; [Sections] for sec, aKeysToVals in vOtherIni { if (!this.HasKey(sec)) if (bRemoveNonMatching) this.DeleteSection(sec) else this.AddSection(sec) ; key=val for key, val in aKeysToVals { bMakeException := vExceptionsIni[sec].HasKey(key) if (this[sec].HasKey(key)) { if (bOverwriteMatching && !bMakeException) this[sec, key] := val } else { if (bRemoveNonMatching && !bMakeException) this.DeleteKey(sec, key) else if (!bRemoveNonMatching) this.AddKey(sec, key, val) } } } return } ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; /* Author: Verdlin Function: GetFileName Purpose: Wrapper to return the extremely long named member var, EasyIni_ReservedFor_m_sFile Parameters None */ GetFileName() { return this.EasyIni_ReservedFor_m_sFile } ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; /* Author: Verdlin Function: GetFileName Purpose: Wrapper to return just the .ini name without the path. Parameters None */ GetOnlyIniFileName() { return SubStr(this.EasyIni_ReservedFor_m_sFile, InStr(this.EasyIni_ReservedFor_m_sFile,"", false, -1)+1) } ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; /* Author: Verdlin Function: IsEmpty Purpose: To indicate whether or not this ini has data Parameters None */ IsEmpty() { return (this.GetSections() == A_Blank ; No sections. && !this.EasyIni_ReservedFor_TopComments.HasKey(1)) ; and no comments. } ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; /* Author: Verdlin Function: Reload Purpose: Reloads object from ini file. This is necessary when other routines may be modifying the same ini file. Parameters None */ Reload() { if (FileExist(this.GetFileName())) this := class_EasyIni(this.GetFileName()) ; else nothing to reload. return this } ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; TODO: Add option to store load and save times in comment at bottom of ini? Save(sSaveAs="", bWarnIfExist=false) { if (sSaveAs == A_Blank) sFile := this.GetFileName() else { sFile := sSaveAs ; Append ".ini" if it is not already there. if (SubStr(sFile, StrLen(sFile)-3, 4) != ".ini") sFile .= ".ini" if (bWarnIfExist && FileExist(sFile)) { MsgBox, 4,, The file "%sFile%" already exists.`n`nAre you sure that you want to overwrite it? IfMsgBox, No return false } } ; Formatting is preserved in ini object. FileDelete, %sFile% bIsFirstLine := true for k, v in this.EasyIni_ReservedFor_TopComments { ; --- MODIFICATION START (dein0s) --- sLastAddedLine := (A_Index == 1 ? "" : "`n") (v == Chr(14) ? "" : v) FileAppend, %sLastAddedLine%, %sFile% bIsFirstLine := false } for section, aKeysToVals in this { sLastAddedLine := (bIsFirstLine ? "[" : "`n[") section "]" FileAppend, %sLastAddedLine%, %sFile% bIsFirstLine := false ; Add the comment(s) for this section sComments := this[section].EasyIni_ReservedFor_Comments["SectionComment"] Loop, Parse, sComments, `n { sLastAddedLine := "`n" (A_LoopField == Chr(14) ? "" : A_LoopField) FileAppend, %sLastAddedLine%, %sFile% } bEmptySection := true for key, val in aKeysToVals { bEmptySection := false sLastAddedLine := "`n" key "=" val FileAppend, %sLastAddedLine%, %sFile% ; Add the comment(s) for this key sComments := this[section].EasyIni_ReservedFor_Comments[key] Loop, Parse, sComments, `n { sLastAddedLine := "`n" (A_LoopField == Chr(14) ? "" : A_LoopField) FileAppend, %sLastAddedLine%, %sFile% } } if (bEmptySection) { ; An empy section may contain comments... sComments := this[section].EasyIni_ReservedFor_Comments["SectionComment"] Loop, Parse, sComments, `n { sLastAddedLine := "`n" (A_LoopField == Chr(14) ? "" : A_LoopField) FileAppend, %sLastAddedLine%, %sFile% } } if (!IsStringEmpty(sLastAddedLine)) { FileAppend, % "`n", %sFile% ; NB: add new line at the end of the section } ; --- MODIFICATION END (dein0s) --- } return true } ToVar() { sTmpFile := "$$$EasyIni_Temp.ini" this.Save(sTmpFile, !A_IsCompiled) FileRead, sIniAsVar, %sTmpFile% FileDelete, %sTmpFile% return sIniAsVar } } ; For all of the EasyIni_* functions below, much credit is due to Lexikos and Rbrtryn for their work with ordered arrays ; See http://www.autohotkey.com/board/topic/61792-ahk-l-for-loop-in-order-of-key-value-pair-creation/?p=389662 for Lexikos's initial work with ordered arrays ; See http://www.autohotkey.com/board/topic/94043-ordered-array/#entry592333 for Rbrtryn's OrderedArray lib EasyIni_CreateBaseObj(parms*) { ; Define prototype object for ordered arrays: static base := {__Set: "EasyIni_Set", _NewEnum: "EasyIni_NewEnum", Delete: "Delete", Remove: "EasyIni_Remove", Insert: "EasyIni_Insert", InsertBefore: "EasyIni_InsertBefore"} ; Create and return new base object: return Object("_keys", Object(), "base", base, parms*) } EasyIni_Set(obj, parms*) { ; If this function is called, the key must not already exist. ; Sub-class array if necessary then add this new key to the key list, if it doesn't begin with "EasyIni_ReservedFor_" if parms.maxindex() > 2 ObjInsert(obj, parms[1], EasyIni_CreateBaseObj()) ; Skip over member variables if (SubStr(parms[1], 1, 20) <> "EasyIni_ReservedFor_") ObjInsert(obj._keys, parms[1]) ; Since we don't return a value, the default behaviour takes effect. ; That is, a new key-value pair is created and stored in the object. } EasyIni_NewEnum(obj) { ; Define prototype object for custom enumerator: static base := Object("Next", "EasyIni_EnumNext") ; Return an enumerator wrapping our _keys array's enumerator: return Object("obj", obj, "enum", obj._keys._NewEnum(), "base", base) } EasyIni_EnumNext(e, ByRef k, ByRef v="") { ; If Enum.Next() returns a "true" value, it has stored a key and ; value in the provided variables. In this case, "i" receives the ; current index in the _keys array and "k" receives the value at ; that index, which is a key in the original object: if r := e.enum.Next(i,k) ; We want it to appear as though the user is simply enumerating ; the key-value pairs of the original object, so store the value ; associated with this key in the second output variable: v := e.obj[k] return r } EasyIni_Remove(obj, parms*) { r := ObjRemove(obj, parms*) ; Remove keys from main object Removed := [] for k, v in obj._keys ; Get each index key pair if not ObjHasKey(obj, v) ; if key is not in main object Removed.Insert(k) ; Store that keys index to be removed later for k, v in Removed ; For each key to be removed ObjRemove(obj._keys, v, "") ; remove that key from key list return r } EasyIni_Insert(obj, parms*) { r := ObjInsert(obj, parms*) ; Insert keys into main object enum := ObjNewEnum(obj) ; Can't use for-loop because it would invoke EasyIni_NewEnum while enum[k] { ; For each key in main object for i, kv in obj._keys ; Search for key in obj._keys if (k = "_keys" || k = kv || SubStr(k, 1, 20) = "EasyIni_ReservedFor_" || SubStr(kv, 1, 20) = "EasyIni_ReservedFor_") ; If found... continue 2 ; Get next key in main object ObjInsert(obj._keys, k) ; Else insert key into obj._keys } return r } EasyIni_InsertBefore(obj, key, parms*) { OldKeys := obj._keys ; Save key list obj._keys := [] ; Clear key list for idx, k in OldKeys { ; Put the keys before key if (k = key) ; back into key list break obj._keys.Insert(k) } r := ObjInsert(obj, parms*) ; Insert keys into main object enum := ObjNewEnum(obj) ; Can't use for-loop because it would invoke EasyIni_NewEnum while enum[k] { ; For each key in main object for i, kv in OldKeys ; Search for key in OldKeys if (k = "_keys" || k = kv) ; If found... continue 2 ; Get next key in main object ObjInsert(obj._keys, k) ; Else insert key into obj._keys } for i, k in OldKeys { ; Put the keys after key if (i < idx) ; back into key list continue obj._keys.Insert(k) } return r } ; --- MODIFICATION START (dein0s) --- IsStringEmpty(string) { if (string == Chr(14) or string == "`n" or string == "`r" or string == "`r`n" or string == "`n`r" or string == "") { return true } return false } CalcStringMD5(string, case=true) { static MD5_DIGEST_LENGTH := 16 hModule := DllCall("LoadLibrary", "Str", "advapi32.dll", "Ptr") VarSetCapacity(MD5_CTX, 104, 0) DllCall("advapi32MD5Init", "Ptr", &MD5_CTX) DllCall("advapi32MD5Update", "Ptr", &MD5_CTX, "AStr", string, "UInt", StrLen(string)) DllCall("advapi32MD5Final", "Ptr", &MD5_CTX) Loop % MD5_DIGEST_LENGTH outStr .= Format("{:02" (case ? "X" : "x") "}", NumGet(MD5_CTX, 87 + A_Index, "UChar")) return outStr, DllCall("FreeLibrary", "Ptr", hModule) } ;觉得原函数不是很好用,进行改造,实现设置默认值的功能 GetValue(sec, key,default) { IniVal:=this[sec][ key] if !IniVal { this[sec][ key]:=default IniVal := default } return IniVal } /* List of functions: class_EasyIni(sFile="", sLoadFromStr="") EasyIni_CreateBaseObj(parms*) EasyIni_Set(obj, parms*) EasyIni_NewEnum(obj) EasyIni_EnumNext(e, ByRef k, ByRef v="") EasyIni_Remove(obj, parms*) EasyIni_Insert(obj, parms*) EasyIni_InsertBefore(obj, key, parms*) IsStringEmpty(string) CalcStringMD5(string, case=true) GetValue(sec, key,default) List of EasyIni class methods: __New(sFile="", sLoadFromStr="") CreateIniObj(parms*) AddSection(sec, key="", val="", ByRef rsError="") RenameSection(sOldSec, sNewSec, ByRef rsError="") DeleteSection(sec) GetSections(sDelim="`n", sSort="") FindSecs(sExp, iMaxSecs="") AddKey(sec, key, val="", ByRef rsError="") RenameKey(sec, OldKey, NewKey, ByRef rsError="") DeleteKey(sec, key) RemoveKey(sec, key) GetKeys(sec, sDelim="`n", sSort="") FindKeys(sec, sExp, iMaxKeys="") FindExactKeys(key, iMaxKeys="") GetVals(sec, sDelim="`n", sSort="") FindVals(sec, sExp, iMaxVals="") HasVal(sec, FindVal) SetKeyVal(sec, key, val, ByRef rsError="") GetCommentContent(sec="", key="", topComment=false) GetTopComments() GetSectionComments(sec) GetKeyComments(sec, key) AddComment(sec="", key="", comment="", topComment=false, ByRef rsError="") AddTopComment(comment, ByRef rsError="") AddSectionComment(sec, comment, ByRef rsError="") AddKeyComment(sec, key, comment, ByRef rsError="") DeleteComment(sec="", key="", comment="", topComment=false, ByRef rsError="") DeleteTopComment(comment, ByRef rsError="") DeleteSectionComment(sec, comment, ByRef rsError="") DeleteKeyComment(sec, key, comment, ByRef rsError="") Update(SourceIni, sections=true, keys=true, values=false, top_comments=false, section_comments=true, key_comments=true) Compare(SourceIni, sections=true, keys=true, comments=false) Copy(SourceIni, bCopyFileName = true) Merge(vOtherIni, bRemoveNonMatching = false, bOverwriteMatching = false, vExceptionsIni = "") GetFileName() GetOnlyIniFileName() IsEmpty() Reload() Save(sSaveAs="", bWarnIfExist=false) ToVar() */ ; --- MODIFICATION END (dein0s) --- /* ------------------------------------------ 左键拖动选择屏幕范围 v1.5 By FeiYue 说明: 按热键“F1”开始左键拖动选择范围, 然后点击范围确定,点其他位置取消 ------------------------------------------ */ GetRange(ByRef x="",ByRef y="",ByRef w="",ByRef h="") { Hotkey, *LButton, LButton_Return, On CoordMode, Mouse Ptr:=A_PtrSize ? "UPtr":"UInt", int:="int" nW:=A_ScreenWidth, nH:=A_ScreenHeight ;-- 生成画布窗口和选框窗口 Gui, Canvas:New, +AlWaysOnTop +ToolWindow -Caption Gui, Canvas:Add, Picture, x0 y0 w%nW% h%nH% +0xE HwndpicID Gui, Range:+LastFound +AlWaysOnTop -Caption +OwnerCanvas WinSet, Transparent, 50 Gui, Range:Color, Yellow range_create("Red","Canvas") ;-- 截屏到内存图像并关联到画布窗口的图片控件 hDC:=DllCall("GetDC", Ptr,0, Ptr) mDC:=DllCall("CreateCompatibleDC", Ptr,hDC, Ptr) hBM:=DllCall("CreateCompatibleBitmap", Ptr,hDC, int,nW, int,nH, Ptr) oBM:=DllCall("SelectObject", Ptr,mDC, Ptr,hBM, Ptr) DllCall("BitBlt", Ptr,mDC, int,0, int,0, int,nW, int,nH , Ptr,hDC, int,0, int,0, int,0x00CC0020|0x40000000) SendMessage, 0x172, 0, hBM,, ahk_id %picID% if ( E:=ErrorLevel ) DllCall("DeleteObject", Ptr,E) DllCall("SelectObject", Ptr,mDC, Ptr,oBM) DllCall("DeleteDC", Ptr,mDC) DllCall("ReleaseDC", Ptr,0, Ptr,hDC) ;-- 显示画布窗口,开始等待选择范围 Gui, Canvas:Show, NA x0 y0 w%nW% h%nH% ok:=w:=h:=0 ListLines, Off Loop { Sleep, 100 MouseGetPos, x2, y2 if GetkeyState("LButton","P")!=ok { ToolTip ok:=!ok, x1:=x2, y1:=y2 if (ok and x2>x and y2>y and x2<x+w-1 and y2<y+h-1) Break } if (ok=0) { ToolTip, % w+h>5 ? "点击范围确定,点其他位置取消" : "请按下鼠标左键并拖动选择范围" Continue } w:=Abs(x1-x2), h:=Abs(y1-y2) x:=(x1+x2-w)//2, y:=(y1+y2-h)//2 if (w+h>5) { Gui, Range:Show, NA x%x% y%y% w%w% h%h% range_show(x,y,w,h) } else { Gui, Range:Hide range_hide() } } ListLines, On range_delete() Gui, Range:Destroy Gui, Canvas:Destroy ;-- 不需要了才清除内存图像 DllCall("DeleteObject", Ptr,hBM) KeyWait, LButton Hotkey, *LButton, LButton_Return, Off LButton_Return: Return } range_create(color="Red", Owner="") { j:=Owner ? "+Owner" Owner : "+ToolWindow" Loop 4 { i:=A_Index Gui,range_%i%:+AlWaysOnTop -Caption %j% +E0x08000000 Gui,range_%i%:Color, %color% } } range_show(x,y,w,h,r=2) { Loop 4 { i:=A_Index , x1:=i=3 ? x+w : x-r , y1:=i=4 ? y+h : y-r , w1:=i=1 or i=3 ? r : w+r+r , h1:=i=2 or i=4 ? r : h+r+r Gui, range_%i%:Show, NA x%x1% y%y1% w%w1% h%h1% } } range_hide() { Loop 4 Gui, range_%A_Index%:Hide } range_delete() { Loop 4 Gui, range_%A_Index%:Destroy } Send_WM_COPYDATA(ByRef StringToSend, ByRef TargetScriptTitle) ; 在这种情况中使用 ByRef 能节约一些内存. ; 此函数发送指定的字符串到指定的窗口然后返回收到的回复. ; 如果目标窗口处理了消息则回复为 1, 而消息被忽略了则为 0. { VarSetCapacity(CopyDataStruct, 3*A_PtrSize, 0) ; 分配结构的内存区域. ; 首先设置结构的 cbData 成员为字符串的大小, 包括它的零终止符: SizeInBytes := (StrLen(StringToSend) + 1) * (A_IsUnicode ? 2 : 1) NumPut(SizeInBytes, CopyDataStruct, A_PtrSize) ; 操作系统要求这个需要完成. NumPut(&StringToSend, CopyDataStruct, 2*A_PtrSize) ; 设置 lpData 为到字符串自身的指针. Prev_DetectHiddenWindows := A_DetectHiddenWindows Prev_TitleMatchMode := A_TitleMatchMode DetectHiddenWindows On SetTitleMatchMode 2 TimeOutTime := 4000 ; Optional. Milliseconds to wait for response from receiver.ahk. Default is 5000 ; 必须使用发送 SendMessage 而不是投递 PostMessage. SendMessage, 0x4a, 0, &CopyDataStruct,, %TargetScriptTitle% ; 0x4a 为 WM_COPYDAT DetectHiddenWindows %Prev_DetectHiddenWindows% ; 恢复调用者原来的设置. SetTitleMatchMode %Prev_TitleMatchMode% ; 同样. return ErrorLevel ; 返回 SendMessage 的回复给我们的调用者. } SoundPlay(sound) { if(mark_music==1) { SoundPlay, %A_ScriptDir%configmusic%sound%.mp3 } }
链接:https://pan.baidu.com/s/1DMkaKKceQn9rL47rPjeIrQ
提取码:xibt
复制这段内容后打开百度网盘手机App,操作更方便哦
提取码:xibt
复制这段内容后打开百度网盘手机App,操作更方便哦
✗棒棒的✗
太牛了