以前做的效果视频展示:
让电脑向iOS发送剪贴板信息达到同步剪贴板的目的,无需再复制粘贴到QQ,再发送到手机的步骤。
iOSBark推送安装教程:
脚本界面:
免安装AHK的版本链接:https://pan.baidu.com/s/1wKuPAOQhcTOHbDZwAbxYQg 提取码:6666
AHK脚本代码:
#SingleInstance Force SetWorkingDir %A_ScriptDir% if FileExist( A_ScriptDir "\删掉此文件可恢复托盘图标.ini") Menu, Tray, NoIcon Menu Tray, Icon, shell32.dll, 147 Gui开机启动显示 := FileExist( A_StartMenu "\Programs\Startup\Bark推送开机自启.vbs")="" ? "0" : "1" Gui隐藏托盘图标显示 := FileExist( A_ScriptDir "\删掉此文件可恢复托盘图标.ini")="" ? "0" : "1" RegRead, 服务端口注册表保存, HKCU\Software\dbgba, iOStoPCServerPath 服务端口注册表保存 := 服务端口注册表保存="" ? "33222" : 服务端口注册表保存 RegRead, 网页接力注册表保存和显示, HKCU\Software\dbgba, iOStoPCHandoff Global 网页接力注册表保存和显示 := 网页接力注册表保存和显示="" ? "1" : 网页接力注册表保存和显示 RegRead, 推送链接注册表保存, HKCU\Software\dbgba, iOStoPCBarkSckeyURL 推送链接注册表保存 := 推送链接注册表保存="" ? "https://api.day.app/xxxxxxxxxxxxxxxxx/" : 推送链接注册表保存 RegRead, 推送热键注册表保存, HKCU\Software\dbgba, iOStoPCTriggerHotkey 推送热键注册表保存 := 推送热键注册表保存="" ? "+v" : 推送热键注册表保存 Hotkey, %推送热键注册表保存%, 自定义热键启用 ; 以下是创建独立的HTTP服务,用于接收iOS向PC同步的信息 paths := {"/iOStoPC" : Func("iOStoPC")} Server := new HttpServer() Server.SetPaths(paths) Server.Serve(服务端口注册表保存) Gosub 加载托盘菜单 Return ; 以下是收到推送自动保存剪贴板的示例,有其它需求可以自行修改链接格式 自定义热键启用: PCtoiOS := ComObjCreate("WinHttp.WinHttpRequest.5.1") PCtoiOS.Open("GET", 推送链接注册表保存 . EncodeDecodeURI(Clipboard) "?automaticallyCopy=1", true) PCtoiOS.Send() return 加载托盘菜单: Menu, Tray, NoStandard Menu, Tray, DeleteAll Menu, Tray, UseErrorLevel Menu, Tray, Add, 推送设置(&S), 推送设置 Menu, Tray, Icon, 推送设置(&S), shell32.dll, 18, 16 Menu, Tray, Add, Menu, Tray, Add, 编辑脚本(&E), 编辑脚本 Menu, Tray, Icon, 编辑脚本(&E), C:\Windows\System32\notepad.exe, 1, 16 Menu, Tray, Add, Menu, Tray, Add, 关于脚本(&A), HTTP关于界面 Menu, Tray, Icon, 关于脚本(&A), shell32.dll, 222, 16 Menu, Tray, Add, Menu, Tray, Add, 关闭HTTP(&X), 关闭脚本 Menu, Tray, Icon, 关闭HTTP(&X), shell32.dll, 132, 16 Menu, Tray, Color, ffffff Menu, Tray, Default, 关闭HTTP(&X) Return 编辑脚本: Run "C:\Windows\System32\notepad.exe" "%A_ScriptFullPath%" Return 关闭脚本: ExitApp 推送设置: Gui New, -MaximizeBox -MinimizeBox Gui Add, Text, x16 y108 w54 h22, 测试推送 Gui Add, Edit, x72 y104 w288 h20 vGui测试推送内容 Gui Add, Text, x16 y12 w54 h22, 推送热键 Gui Add, Hotkey, x72 y8 w106 h20 vGui推送热键更新显示 g推送热键更新, %推送热键注册表保存% Gui Add, Text, x16 y44 w54 h22, 服务端口 Gui Add, Edit, x72 y40 w106 h20 Limit5 Number vGui服务端口更新显示 g服务端口更新, %服务端口注册表保存% Gui Add, Text, x16 y76 w54 h22, 推送链接 Gui Add, Edit, x72 y72 w288 h20 vGui推送链接更新显示 g推送链接更新, %推送链接注册表保存% Gui Add, Button, x110 y136 w180 h23 g测试推送发送, 测试推送 Gui Add, Text, x221 y13 w97 h21, 开机启动 Gui Add, CheckBox, x204 y8 w17 h22 Checked%Gui开机启动显示% g开机启动更新 Gui Add, Text, x306 y13 w97 h21, 网页接力 Gui Add, CheckBox, x289 y8 w17 h22 Checked%网页接力注册表保存和显示% g网页接力更新 Gui Add, Text, x221 y45 w150 h21, 启动后,隐藏托盘图标 Gui Add, CheckBox, x204 y40 w17 h22 Checked%Gui隐藏托盘图标显示% g隐藏托盘图标 if (SubStr(A_IPAddress1, -1)!=".1") && (SubStr(A_IPAddress1, -1)!=".0") 本机IP确认 := A_IPAddress1 else if (SubStr(A_IPAddress2, -1)!=".1") && (SubStr(A_IPAddress2, -1)!=".0") 本机IP确认 := A_IPAddress2 else if (SubStr(A_IPAddress3, -1)!=".1") && (SubStr(A_IPAddress3, -1)!=".0") 本机IP确认 := A_IPAddress3 else if (SubStr(A_IPAddress4, -1)!=".1") && (SubStr(A_IPAddress4, -1)!=".0") 本机IP确认 := A_IPAddress4 else 本机IP确认 := A_IPAddress1 Gui Show, w374 h180, 本机IP:%本机IP确认%:%服务端口注册表保存% 【关闭窗口后设置生效】 Return 推送热键更新: Hotkey, %推送热键注册表保存%, off GuiControlGet, 推送热键注册表保存,, Gui推送热键更新显示 RegWrite, REG_SZ, HKCU\Software\dbgba,iOStoPCTriggerHotkey, %推送热键注册表保存% if (推送热键注册表保存!="") Hotkey, %推送热键注册表保存%, 自定义热键启用 Return 服务端口更新: GuiControlGet, 服务端口注册表保存,, Gui服务端口更新显示 if (服务端口注册表保存<65535) RegWrite, REG_SZ, HKCU\Software\dbgba,iOStoPCServerPath, %服务端口注册表保存% else { ToolTip 服务端口设置不能大于65535! SetTimer 弹窗关闭, -2000 } Return 弹窗关闭: ToolTip Return 推送链接更新: GuiControlGet, 推送链接注册表保存,, Gui推送链接更新显示 if (SubStr(推送链接注册表保存, -0)!="/") && (SubStr(推送链接注册表保存, -0)!="") 推送链接注册表保存 := 推送链接注册表保存 "/" RegWrite, REG_SZ, HKCU\Software\dbgba,iOStoPCBarkSckeyURL, %推送链接注册表保存% Return 测试推送发送: GuiControlGet, 测试推送内容,, Gui测试推送内容 PCtoiOS := ComObjCreate("WinHttp.WinHttpRequest.5.1") PCtoiOS.Open("GET", 推送链接注册表保存 . EncodeDecodeURI(测试推送内容) "?automaticallyCopy=1", true) PCtoiOS.Send() Return 网页接力更新: 网页接力注册表保存和显示 := !网页接力注册表保存和显示 RegWrite, REG_SZ, HKCU\Software\dbgba,iOStoPCHandoff, %网页接力注册表保存和显示% Return 开机启动更新: if FileExist( A_StartMenu "\Programs\Startup\Bark推送开机自启.vbs") FileDelete, %A_StartMenu%\Programs\Startup\Bark推送开机自启.vbs else PowerBoot() Gui开机启动显示 := FileExist( A_StartMenu "\Programs\Startup\Bark推送开机自启.vbs")="" ? "0" : "1" Return 隐藏托盘图标: if FileExist( A_ScriptDir "\删掉此文件可恢复托盘图标.ini") FileDelete, %A_ScriptDir%\删掉此文件可恢复托盘图标.ini else FileAppend, 删掉此文件可恢复托盘图标`n, %A_ScriptDir%\删掉此文件可恢复托盘图标.ini Gui隐藏托盘图标显示 := FileExist( A_ScriptDir "\删掉此文件可恢复托盘图标.ini")="" ? "0" : "1" Return GuiEscape: GuiClose: Gui, Destroy Reload Return HTTP关于界面: Gui, HTTP_About:New, -MaximizeBox -MinimizeBox Gui, HTTP_About:Margin, , 16 Gui, HTTP_About:Add, Picture, w48 h-1 Icon18, shell32.dll Gui, HTTP_About:Font, S12 Bold Gui, HTTP_About:Add, Text, x+10 yp+1 vTAppName Section, HTTPBark v1.0 Gui, HTTP_About:Font NetIPAPI:=GetNetIPAPI() Gui, HTTP_About:Add, Text, xs+4 y+10 g点击复制外网IP到剪贴板, % "外网IP:" NetIPAPI[1] " 点击复制" Gui, HTTP_About:Add, Text, xs+8 y+10, 感谢:AutoHotkey|中文社区 Gui, HTTP_About:Add, Button, x116 y123 w70 Default gHTTP_AboutGuiClose, 确定 Gui, Add, Link, x78 y81, <a href="tencent://groupwpa/?subcmd=all¶m=7B22457874506172616D223A7B226170704964223A223231227D2C2267726F757055696E223A3731373934373634372C2276697369746F72223A317D">QQ群:717947647</a> Gui, HTTP_About:Add, Text, x78 y101, Copyright (C) 2021 dbgba GuiControlGet, rcCtrl, HTTP_About:Pos, TAppName Gui, HTTP_About:Show, Autosize Return 点击复制外网IP到剪贴板: RegRead, 服务端口注册表读取, HKCU\Software\dbgba, iOStoPCServerPath Clipboard := "http://" NetIPAPI[1] ":" 服务端口注册表读取 "/iOStoPC" Return HTTP_AboutGuiClose: HTTP_AboutGuiEscape: Gui, HTTP_About:Destroy Return ; 接收iOS推送的函数 iOStoPC(ByRef req, ByRef res) { HttpReqBodyArray:="", HttpReqBodyArray := Object() For each, Pair in StrSplit(req.body,"&") Part := StrSplit(Pair, "="), HttpReqBodyArray.Push([Part[1], Part[2]]) 一次解码 := EncodeDecodeURI(HttpReqBodyArray[1,2], false) Clipboard := EncodeDecodeURI(一次解码, false) if (SubStr(Clipboard, 1, 4)="http") && (网页接力注册表保存和显示=1) Run %Clipboard% SoundPlay *-1 ; 接收提示音【不喜欢可删掉这句】 res.SetBodyText(""), res.status := 200 } ; URL编解码函数 EncodeDecodeURI(str, encode := true, component := true) { ; By teadrinker static Doc, JS ; https://www.autohotkey.com/boards/viewtopic.php?f=76&t=84825 if !Doc { Doc := ComObjCreate("htmlfile") Doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">") JS := Doc.parentWindow ( Doc.documentMode < 9 && JS.execScript() ) } Return JS[ (encode ? "en" : "de") . "codeURI" . (component ? "Component" : "") ](str) } ; 添加开机自启动 PowerBoot() { FileDelete, %A_StartMenu%\Programs\Startup\Bark推送开机自启.vbs StartupPath := InStr(A_AhkPath, "\AutoHotkey.exe")=0 ? A_AhkPath : A_ScriptFullPath FileAppend, Set shell=Wscript.CreateObject("Wscript.Shell")`nSet fs=Wscript.CreateObject("Scripting.FileSystemObject")`nif fs.FileExists("%StartupPath%") then`na=shell.run("""%StartupPath%"""`,0)`nend if, %A_StartMenu%\Programs\Startup\Bark推送开机自启.vbs } ; 获取外网IP函数 GetNetIPAPI(url:="http://whois.pconline.com.cn/ipJson.jsp?json=true") { if ipobj:=UrlDownloadToVars(url,,,,,,,,,3){ ;设定超时时长 iJson:= Json_toObj(ipobj) ipLocal:= iJson["pro"] iJson["city"] "-" iJson["addr"] return [ iJson["ip"], ipLocal, ipLocal] } } UrlDownloadToVars(URL,Charset="",URLCodePage="",Proxy="",ProxyBypassList="",Cookie="",Referer="",UserAgent="",EnableRedirects="",Timeout=-1) { ComObjError(0) ;禁用 COM 错误通告。禁用后,检查 A_LastError 的值,脚本可以实现自己的错误处理 WebRequest := ComObjCreate("WinHttp.WinHttpRequest.5.1") if (URLCodePage<>"") ;设置URL的编码 WebRequest.Option(2):=URLCodePage if (EnableRedirects<>"") WebRequest.Option(6):=EnableRedirects if (Proxy<>"") ;设置代理服务器。微软的代码 SetProxy() 是放在 Open() 之前的,所以我也放前面设置,以免无效 WebRequest.SetProxy(2,Proxy,ProxyBypassList) WebRequest.Open("GET", URL, true) ;true为异步获取,默认是false。龟速的根源!!!卡顿的根源!!! if (Cookie<>"") ;设置Cookie。SetRequestHeader() 必须 Open() 之后才有效 { WebRequest.SetRequestHeader("Cookie","tuzi") ;先设置一个cookie,防止出错,见官方文档 WebRequest.SetRequestHeader("Cookie",Cookie) } if (Referer<>"") ;设置Referer WebRequest.SetRequestHeader("Referer",Referer) if (UserAgent<>"") ;设置User-Agent WebRequest.SetRequestHeader("User-Agent",UserAgent) WebRequest.Send() WebRequest.WaitForResponse(Timeout) ;WaitForResponse方法确保获取的是完整的响应 if (Charset="") ;设置字符集 return,RegExReplace(WebRequest.ResponseText(),"^\s+|\s+$") else { ADO:=ComObjCreate("adodb.stream") ;使用 adodb.stream 编码返回值。参考 http://bbs.howtoadmin.com/ThRead-814-1-1.html ADO.Type:=1 ;以二进制方式操作 ADO.Mode:=3 ;可同时进行读写 ADO.Open() ;开启物件 ADO.Write(WebRequest.ResponseBody()) ;写入物件。注意 WebRequest.ResponseBody() 获取到的是无符号的bytes,通过 adodb.stream 转换成字符串string ADO.Position:=0 ;从头开始 ADO.Type:=2 ;以文字模式操作 ADO.Charset:=Charset ;设定编码方式 return,RegExReplace(ADO.ReadText(),"^\s+|\s+$") ;将物件内的文字读出 } } Json_toObj(ByRef src, args*) { static q := Chr(34) key := "", is_key := false stack := [ tree := [] ] is_arr := { (tree): 1 } next := q . "{[01234567890-tfn" pos := 0 while ( (ch := SubStr(src, ++pos, 1)) != "" ) { if InStr(" `t`n`r", ch) continue if !InStr(next, ch, true) { ln := ObjLength(StrSplit(SubStr(src, 1, pos), "`n")) col := pos - InStr(src, "`n",, -(StrLen(src)-pos+1)) msg := Format("{}: line {} col {} (char {})" , (next == "") ? ["Extra data", ch := SubStr(src, pos)][1] : (next == "'") ? "Unterminated string starting at" : (next == "\") ? "Invalid \escape" : (next == ":") ? "Expecting ':' delimiter" : (next == q) ? "Expecting object key enclosed in double quotes" : (next == q . "}") ? "Expecting object key enclosed in double quotes or object closing '}'" : (next == ",}") ? "Expecting ',' delimiter or object closing '}'" : (next == ",]") ? "Expecting ',' delimiter or array closing ']'" : [ "Expecting JSON value(string, number, [true, false, null], object or array)" , ch := SubStr(src, pos, (SubStr(src, pos)~="[\]\},\s]|$")-1) ][1] , ln, col, pos) throw Exception(msg, -1, ch) } is_array := is_arr[obj := stack[1]] if i := InStr("{[", ch) { val := (proto := args[i]) ? new proto : {} is_array? ObjPush(obj, val) : obj[key] := val ObjInsertAt(stack, 1, val) is_arr[val] := !(is_key := ch == "{") next := q . (is_key ? "}" : "{[]0123456789-tfn") } else if InStr("}]", ch) { ObjRemoveAt(stack, 1) next := stack[1]==tree ? "" : is_arr[stack[1]] ? ",]" : ",}" } else if InStr(",:", ch) { is_key := (!is_array && ch == ",") next := is_key ? q : q . "{[0123456789-tfn" } else { if (ch == q) { i := pos while i := InStr(src, q,, i+1) { val := StrReplace(SubStr(src, pos+1, i-pos-1), "\\", "\u005C") static end := A_AhkVersion<"2" ? 0 : -1 if (SubStr(val, end) != "\") break } if !i ? (pos--, next := "'") : 0 continue pos := i ; update pos val := StrReplace(val, "\/", "/") , val := StrReplace(val, "\" . q, q) , val := StrReplace(val, "\b", "`b") , val := StrReplace(val, "\f", "`f") , val := StrReplace(val, "\n", "`n") , val := StrReplace(val, "\r", "`r") , val := StrReplace(val, "\t", "`t") i := 0 while i := InStr(val, "\",, i+1) { if (SubStr(val, i+1, 1) != "u") ? (pos -= StrLen(SubStr(val, i)), next := "\") : 0 continue 2 xxxx := Abs("0x" . SubStr(val, i+2, 4)) if (A_IsUnicode || xxxx < 0x100) val := SubStr(val, 1, i-1) . Chr(xxxx) . SubStr(val, i+6) } if is_key { key := val, next := ":" continue } } else { val := SubStr(src, pos, i := RegExMatch(src, "[\]\},\s]|$",, pos)-pos) static null := "" ; for #Warn if InStr(",true,false,null,", "," . val . ",", true) ; if var in val := %val% else if (Abs(val) == "") ? (pos--, next := "#") : 0 continue val := val + 0, pos += i-1 } is_array? ObjPush(obj, val) : obj[key] := val next := obj==tree ? "" : is_array ? ",]" : ",}" } } return tree[1] } ;========== AHKhttp.ahk https://github.com/Skiouros/AHKhttp/blob/master/AHKhttp.ahk ========== class Uri { Decode(str) { Loop If RegExMatch(str, "i)(?<=%)[\da-f]{1,2}", hex) StringReplace, str, str, `%%hex%, % Chr("0x" . hex), All Else Break Return, str } Encode(str) { f = %A_FormatInteger% SetFormat, Integer, Hex If RegExMatch(str, "^\w+:/{0,2}", pr) StringTrimLeft, str, str, StrLen(pr) StringReplace, str, str, `%, `%25, All Loop If RegExMatch(str, "i)[^\w\.~%]", char) StringReplace, str, str, %char%, % "%" . Asc(char), All Else Break SetFormat, Integer, %f% Return, pr . str } } class HttpServer { static servers := {} LoadMimes(file) { if (!FileExist(file)) return false FileRead, data, % file types := StrSplit(data, "`n") this.mimes := {} for i, data in types { info := StrSplit(data, " ") type := info.Remove(1) ; Seperates type of content and file types info := StrSplit(LTrim(SubStr(data, StrLen(type) + 1)), " ") for i, ext in info { this.mimes[ext] := type } } return true } GetMimeType(file) { default := "text/plain" if (!this.mimes) return default SplitPath, file,,, ext type := this.mimes[ext] if (!type) return default return type } ServeFile(ByRef response, file) { f := FileOpen(file, "r") length := f.RawRead(data, f.Length) f.Close() response.SetBody(data, length) res.headers["Content-Type"] := this.GetMimeType(file) } SetPaths(paths) { this.paths := paths } Handle(ByRef request) { response := new HttpResponse() if (!this.paths[request.path]) { func := this.paths["404"] response.status := 404 if (func) func.(request, response, this) return response } else { this.paths[request.path].(request, response, this) } return response } Serve(port) { this.port := port HttpServer.servers[port] := this AHKsock_Listen(port, "HttpHandler") } } HttpHandler(sEvent, iSocket = 0, sName = 0, sAddr = 0, sPort = 0, ByRef bData = 0, bDataLength = 0) { static sockets := {} if (!sockets[iSocket]) { sockets[iSocket] := new Socket(iSocket) AHKsock_SockOpt(iSocket, "SO_KEEPALIVE", true) } socket := sockets[iSocket] if (sEvent == "DISCONNECTED") { socket.request := false sockets[iSocket] := false } else if (sEvent == "SEND") { if (socket.TrySend()) { socket.Close() } } else if (sEvent == "RECEIVED") { server := HttpServer.servers[sPort] text := StrGet(&bData, "UTF-8") ; New request or old? if (socket.request) { ; Get data and append it to the existing request body socket.request.bytesLeft -= StrLen(text) socket.request.body := socket.request.body . text request := socket.request } else { ; Parse new request request := new HttpRequest(text) length := request.headers["Content-Length"] request.bytesLeft := length + 0 if (request.body) { request.bytesLeft -= StrLen(request.body) } } if (request.bytesLeft <= 0) { request.done := true } else { socket.request := request } if (request.done || request.IsMultipart()) { response := server.Handle(request) if (response.status) { socket.SetData(response.Generate()) } } if (socket.TrySend()) { if (!request.IsMultipart() || request.done) { socket.Close() } } } } class HttpRequest { __New(data = "") { if (data) this.Parse(data) } GetPathInfo(top) { results := [] while (pos := InStr(top, " ")) { results.Insert(SubStr(top, 1, pos - 1)) top := SubStr(top, pos + 1) } this.method := results[1] this.path := Uri.Decode(results[2]) this.protocol := top } GetQuery() { pos := InStr(this.path, "?") query := StrSplit(SubStr(this.path, pos + 1), "&") if (pos) this.path := SubStr(this.path, 1, pos - 1) this.queries := {} for i, value in query { pos := InStr(value, "=") key := SubStr(value, 1, pos - 1) val := SubStr(value, pos + 1) this.queries[key] := val } } Parse(data) { this.raw := data data := StrSplit(data, "`n`r") headers := StrSplit(data[1], "`n") this.body := LTrim(data[2], "`n") this.GetPathInfo(headers.Remove(1)) this.GetQuery() this.headers := {} for i, line in headers { pos := InStr(line, ":") key := SubStr(line, 1, pos - 1) val := Trim(SubStr(line, pos + 1), "`n`r ") this.headers[key] := val } } IsMultipart() { length := this.headers["Content-Length"] expect := this.headers["Expect"] if (expect = "100-continue" && length > 0) return true return false } } class HttpResponse { __New() { this.headers := {} this.status := 0 this.protocol := "HTTP/1.1" this.SetBodyText("") } Generate() { FormatTime, date,, ddd, d MMM yyyy HH:mm:ss this.headers["Date"] := date headers := this.protocol . " " . this.status . "`r`n" for key, value in this.headers { headers := headers . key . ": " . value . "`r`n" } headers := headers . "`r`n" length := this.headers["Content-Length"] buffer := new Buffer((StrLen(headers) * 2) + length) buffer.WriteStr(headers) buffer.Append(this.body) buffer.Done() return buffer } SetBody(ByRef body, length) { this.body := new Buffer(length) this.body.Write(&body, length) this.headers["Content-Length"] := length } SetBodyText(text) { this.body := Buffer.FromString(text) this.headers["Content-Length"] := this.body.length } } class Socket { __New(socket) { this.socket := socket } Close(timeout = 5000) { AHKsock_Close(this.socket, timeout) } SetData(data) { this.data := data } TrySend() { if (!this.data || this.data == "") return false p := this.data.GetPointer() length := this.data.length this.dataSent := 0 loop { if ((i := AHKsock_Send(this.socket, p, length - this.dataSent)) < 0) { if (i == -2) { return } else { ; Failed to send return } } if (i < length - this.dataSent) { this.dataSent += i } else { break } } this.dataSent := 0 this.data := "" return true } } class Buffer { __New(len) { this.SetCapacity("buffer", len) this.length := 0 } FromString(str, encoding = "UTF-8") { length := Buffer.GetStrSize(str, encoding) buffer := new Buffer(length) buffer.WriteStr(str) return buffer } GetStrSize(str, encoding = "UTF-8") { encodingSize := ((encoding="utf-16" || encoding="cp1200") ? 2 : 1) ; length of string, minus null char return StrPut(str, encoding) * encodingSize - encodingSize } WriteStr(str, encoding = "UTF-8") { length := this.GetStrSize(str, encoding) VarSetCapacity(text, length) StrPut(str, &text, encoding) this.Write(&text, length) return length } ; data is a pointer to the data Write(data, length) { p := this.GetPointer() DllCall("RtlMoveMemory", "uint", p + this.length, "uint", data, "uint", length) this.length += length } Append(ByRef buffer) { destP := this.GetPointer() sourceP := buffer.GetPointer() DllCall("RtlMoveMemory", "uint", destP + this.length, "uint", sourceP, "uint", buffer.length) this.length += buffer.length } GetPointer() { return this.GetAddress("buffer") } Done() { this.SetCapacity("buffer", this.length) } } ;========== AHKsock.ahk https://github.com/jleb/AHKsock/blob/master/AHKsock.ahk ========== AHKsock_Listen(sPort, sFunction = False) { ;Check if there is already a socket listening on this port If (sktListen := AHKsock_Sockets("GetSocketFromNamePort", A_Space, sPort)) { ;Check if we're stopping the listening If Not sFunction { AHKsock_Close(sktListen) ;Close the socket ;Check if we're retrieving the current function } Else If (sFunction = "()") { Return AHKsock_Sockets("GetFunction", sktListen) ;Check if it's a different function } Else If (sFunction <> AHKsock_Sockets("GetFunction", sktListen)) AHKsock_Sockets("SetFunction", sktListen, sFunction) ;Update it Return ;We're done } ;Make sure we even have a function If Not IsFunc(sFunction) Return 2 ;sFunction is not a valid function. ;Make sure Winsock has been started up If (i := AHKsock_Startup()) Return (i = 1) ? 3 ;The WSAStartup() call failed. The error is in ErrorLevel. : 4 ;The Winsock DLL does not support version 2.2. ;Resolve the local address and port to be used by the server VarSetCapacity(aiHints, 16 + 4 * A_PtrSize, 0) NumPut(1, aiHints, 0, "Int") ;ai_flags = AI_PASSIVE NumPut(2, aiHints, 4, "Int") ;ai_family = AF_INET NumPut(1, aiHints, 8, "Int") ;ai_socktype = SOCK_STREAM NumPut(6, aiHints, 12, "Int") ;ai_protocol = IPPROTO_TCP iResult := DllCall("Ws2_32\GetAddrInfo", "Ptr", 0, "Ptr", &sPort, "Ptr", &aiHints, "Ptr*", aiResult) If (iResult != 0) Or ErrorLevel { ;Check for error ErrorLevel := ErrorLevel ? ErrorLevel : iResult Return 5 ;The getaddrinfo() call failed. The error is in ErrorLevel. } sktListen := -1 ;INVALID_SOCKET sktListen := DllCall("Ws2_32\socket", "Int", NumGet(aiResult+0, 04, "Int") , "Int", NumGet(aiResult+0, 08, "Int") , "Int", NumGet(aiResult+0, 12, "Int"), "Ptr") If (sktListen = -1) Or ErrorLevel { ;Check for INVALID_SOCKET sErrorLevel := ErrorLevel ? ErrorLevel : AHKsock_LastError() DllCall("Ws2_32\FreeAddrInfo", "Ptr", aiResult) ErrorLevel := sErrorLevel Return 6 ;The socket() call failed. The error is in ErrorLevel. } ;Setup the TCP listening socket iResult := DllCall("Ws2_32\bind", "Ptr", sktListen, "Ptr", NumGet(aiResult+0, 16 + 2 * A_PtrSize), "Int", NumGet(aiResult+0, 16, "Ptr")) If (iResult = -1) Or ErrorLevel { ;Check for SOCKET_ERROR sErrorLevel := ErrorLevel ? ErrorLevel : AHKsock_LastError() DllCall("Ws2_32\closesocket", "Ptr", sktListen) DllCall("Ws2_32\FreeAddrInfo", "Ptr", aiResult) ErrorLevel := sErrorLevel Return 7 ;The bind() call failed. The error is in ErrorLevel. } DllCall("Ws2_32\FreeAddrInfo", "Ptr", aiResult) ;Add socket to array with A_Space for Name and IP to indicate that it's a listening socket AHKsock_Sockets("Add", sktListen, A_Space, A_Space, sPort, sFunction) ;We must now actually register the socket If AHKsock_RegisterAsyncSelect(sktListen) { sErrorLevel := ErrorLevel DllCall("Ws2_32\closesocket", "Ptr", sktListen) AHKsock_Sockets("Delete", sktListen) ;Remove from array ErrorLevel := sErrorLevel Return 8 ;The WSAAsyncSelect() call failed. The error is in ErrorLevel. } ;Start listening for incoming connections iResult := DllCall("Ws2_32\listen", "Ptr", sktListen, "Int", 0x7FFFFFFF) ;SOMAXCONN If (iResult = -1) Or ErrorLevel { ;Check for SOCKET_ERROR sErrorLevel := ErrorLevel ? ErrorLevel : AHKsock_LastError() DllCall("Ws2_32\closesocket", "Ptr", sktListen) AHKsock_Sockets("Delete", sktListen) ;Remove from array ErrorLevel := sErrorLevel Return 9 ;The listen() call failed. The error is in ErrorLevel. } } AHKsock_Connect(sName, sPort, sFunction) { Static aiResult, iPointer, bProcessing, iMessage Static sCurName, sCurPort, sCurFunction, sktConnect ;Check if it's just to inquire whether or not a call is possible If (Not sName And Not sPort And Not sFunction) Return bProcessing ;Check if we're busy If bProcessing And (sFunction != iMessage) { ErrorLevel := sCurName A_Tab sCurPort Return 1 ;AHKsock_Connect is still processing a connection attempt. ErrorLevel contains the name and the port, ;delimited by a tab. } Else If bProcessing { ;sFunction = iMessage. The connect operation has finished. ;Check if it was successful If (i := sPort >> 16) { ;Close the socket that failed DllCall("Ws2_32\closesocket", "Ptr", sktConnect) ;Get the next pointer. ai_next iPointer := NumGet(iPointer+0, 16 + 3 * A_PtrSize) ;Check if we reached the end of the linked structs If (iPointer = 0) { ;We can now free the chain of addrinfo structs DllCall("Ws2_32\FreeAddrInfo", "Ptr", aiResult) ;This is to ensure that the user can call AHKsock_Connect() right away upon receiving the message. bProcessing := False ;Raise an error (can't use Return 1 because we were called asynchronously) ErrorLevel := i AHKsock_RaiseError(1) ;The connect() call failed. The error is in ErrorLevel. ;Call the function to signal that connection failed If IsFunc(sCurFunction) %sCurFunction%("CONNECTED", -1, sCurName, 0, sCurPort) Return } } Else { ;Successful connection! ;Get the IP we successfully connected to sIP := DllCall("Ws2_32\inet_ntoa", "UInt", NumGet(NumGet(iPointer+0, 16 + 2 * A_PtrSize)+4, 0, "UInt"), "AStr") ;We can now free the chain of ADDRINFO structs DllCall("Ws2_32\FreeAddrInfo", "Ptr", aiResult) ;Add socket to array AHKsock_Sockets("Add", sktConnect, sCurName, sIP, sCurPort, sCurFunction) ;This is to ensure that the user can call AHKsock_Connect() right away upon receiving the message. bProcessing := False ;Do this small bit in Critical so that AHKsock_AsyncSelect doesn't receive ;any FD messages before we call the user function Critical ;We must now actually register the socket If AHKsock_RegisterAsyncSelect(sktConnect) { sErrorLevel := ErrorLevel ? ErrorLevel : AHKsock_LastError() DllCall("Ws2_32\closesocket", "Ptr", sktConnect) AHKsock_Sockets("Delete", sktConnect) ;Remove from array ErrorLevel := sErrorLevel AHKsock_RaiseError(2) ;The WSAAsyncSelect() call failed. The error is in ErrorLevel. If IsFunc(sCurFunction) ;Call the function to signal that connection failed %sCurFunction%("CONNECTED", -1, sCurName, 0, sCurPort) } Else If IsFunc(sCurFunction) ;Call the function to signal that connection was successful %sCurFunction%("CONNECTED", sktConnect, sCurName, sIP, sCurPort) Return } } Else { ;We were called ;Make sure we even have a function If Not IsFunc(sFunction) Return 2 ;sFunction is not a valid function. bProcessing := True ;Block future calls to AHKsock_Connect() until we're done ;Keep the values sCurName := sName sCurPort := sPort sCurFunction := sFunction ;Make sure Winsock has been started up If (i := AHKsock_Startup()) { bProcessing := False Return (i = 1) ? 3 ;The WSAStartup() call failed. The error is in ErrorLevel. : 4 ;The Winsock DLL does not support version 2.2. } ;Resolve the server address and port VarSetCapacity(aiHints, 16 + 4 * A_PtrSize, 0) NumPut(2, aiHints, 4, "Int") ;ai_family = AF_INET NumPut(1, aiHints, 8, "Int") ;ai_socktype = SOCK_STREAM NumPut(6, aiHints, 12, "Int") ;ai_protocol = IPPROTO_TCP iResult := DllCall("Ws2_32\GetAddrInfo", "Ptr", &sName, "Ptr", &sPort, "Ptr", &aiHints, "Ptr*", aiResult) If (iResult != 0) Or ErrorLevel { ;Check for error ErrorLevel := ErrorLevel ? ErrorLevel : iResult bProcessing := False Return 5 ;The getaddrinfo() call failed. The error is in ErrorLevel. } ;Start with the first struct iPointer := aiResult } ;Create a SOCKET for connecting to server sktConnect := DllCall("Ws2_32\socket", "Int", NumGet(iPointer+0, 04, "Int") , "Int", NumGet(iPointer+0, 08, "Int") , "Int", NumGet(iPointer+0, 12, "Int"), "Ptr") If (sktConnect = 0xFFFFFFFF) Or ErrorLevel { ;Check for INVALID_SOCKET sErrorLevel := ErrorLevel ? ErrorLevel : AHKsock_LastError() DllCall("Ws2_32\FreeAddrInfo", "Ptr", aiResult) bProcessing := False ErrorLevel := sErrorLevel If (sFunction = iMessage) { ;Check if we were called asynchronously AHKsock_RaiseError(3) ;The socket() call failed. The error is in ErrorLevel. ;Call the function to signal that connection failed If IsFunc(sCurFunction) %sCurFunction%("CONNECTED", -1) } Return 6 ;The socket() call failed. The error is in ErrorLevel. } ;Register the socket to know when the connect() function is done. FD_CONNECT = 16 iMessage := AHKsock_Settings("Message") + 1 If AHKsock_RegisterAsyncSelect(sktConnect, 16, "AHKsock_Connect", iMessage) { sErrorLevel := ErrorLevel DllCall("Ws2_32\FreeAddrInfo", "Ptr", aiResult) DllCall("Ws2_32\closesocket", "Ptr", sktConnect) bProcessing := False ErrorLevel := sErrorLevel If (sFunction = iMessage) { ;Check if we were called asynchronously AHKsock_RaiseError(4) ;The WSAAsyncSelect() call failed. The error is in ErrorLevel. ;Call the function to signal that connection failed If IsFunc(sCurFunction) %sCurFunction%("CONNECTED", -1) } Return 7 ;The WSAAsyncSelect() call failed. The error is in ErrorLevel. } ;Connect to server (the connect() call also implicitly binds the socket to any host address and any port) iResult := DllCall("Ws2_32\connect", "Ptr", sktConnect, "Ptr", NumGet(iPointer+0, 16 + 2 * A_PtrSize), "Int", NumGet(iPointer+0, 16)) If ErrorLevel Or ((iResult = -1) And (AHKsock_LastError() != 10035)) { ;Check for any error other than WSAEWOULDBLOCK sErrorLevel := ErrorLevel ? ErrorLevel : AHKsock_LastError() DllCall("Ws2_32\FreeAddrInfo", "Ptr", aiResult) DllCall("Ws2_32\closesocket", "Ptr", sktConnect) bProcessing := False ErrorLevel := sErrorLevel If (sFunction = iMessage) { ;Check if we were called asynchronously AHKsock_RaiseError(5) ;The connect() call failed. The error is in ErrorLevel. ;Call the function to signal that connection failed If IsFunc(sCurFunction) %sCurFunction%("CONNECTED", -1) } Return 8 ;The connect() call failed. The error is in ErrorLevel. } } AHKsock_Send(iSocket, ptrData = 0, iLength = 0) { ;Make sure the socket is on record. Fail-safe If Not AHKsock_Sockets("Index", iSocket) Return -4 ;The socket specified in iSocket is not a recognized socket. ;Make sure Winsock has been started up If Not AHKsock_Startup(1) Return -1 ;WSAStartup hasn't been called yet. ;Make sure the socket is cleared for sending If Not AHKsock_Sockets("GetSend", iSocket) Return -5 ;The socket specified in iSocket is not cleared for sending. /*! Uncomment this block to simulate the possibility of an incomplete send() Random, iRand, 1, 100 If (iRand <= 30) { ;Probability of failure of 30% Random, iRand, 1, iLength - 1 ;Randomize how much of the data will not be sent iLength -= iRand } */ iSendResult := DllCall("Ws2_32\send", "Ptr", iSocket, "Ptr", ptrData, "Int", iLength, "Int", 0) If (iSendResult = -1) And ((iErr := AHKsock_LastError()) = 10035) { ;Check specifically for WSAEWOULDBLOCK AHKsock_Sockets("SetSend", iSocket, False) ;Update socket's send status Return -2 ;Calling send() would have blocked the thread. Try again once you get the proper update. } Else If (iSendResult = -1) Or ErrorLevel { ErrorLevel := ErrorLevel ? ErrorLevel : iErr Return -3 ;The send() call failed. The error is in ErrorLevel. } Else Return iSendResult ;The send() operation was successful } AHKsock_ForceSend(iSocket, ptrData, iLength) { ;Make sure Winsock has been started up If Not AHKsock_Startup(1) Return -1 ;WSAStartup hasn't been called yet ;Make sure the socket is on record. Fail-safe If Not AHKsock_Sockets("Index", iSocket) Return -4 ;Make sure that we're not in Critical, or we won't be able to wait for FD_WRITE messages If A_IsCritical Return -5 ;Extra precaution to make sure FD_WRITE messages can make it Thread, Priority, 0 ;We need to make sure not to fill up the send buffer in one call, or we'll get a performance hit. ;http://support.microsoft.com/kb/823764 ;Get the socket's send buffer size If ((iMaxChunk := AHKsock_SockOpt(iSocket, "SO_SNDBUF")) = -1) Return -6 ;Check if we'll be sending in chunks or not If (iMaxChunk <= 1) { ;We'll be sending as much as possible everytime! Loop { ;Keep sending the data until we're done or until an error occurs ;Wait until we can send data (ie. when FD_WRITE arrives) While Not AHKsock_Sockets("GetSend", iSocket) Sleep -1 Loop { ;Keep sending the data until we get WSAEWOULDBLOCK or until an error occurs If ((iSendResult := AHKsock_Send(iSocket, ptrData, iLength)) < 0) { If (iSendResult = -2) ;Check specifically for WSAEWOULDBLOCK Break ;Calling send() would have blocked the thread. Break the loop and we'll try again after we ;receive FD_WRITE Else Return iSendResult ;Something bad happened with AHKsock_Send. Return the same value we got. } Else { ;AHKsock_Send was able to send bytes. Let's check if it sent only part of what we requested If (iSendResult < iLength) ;Move the offset up by what we were able to send ptrData += iSendResult, iLength -= iSendResult Else Return ;We're done sending all the data } } } } Else { ;We'll be sending in chunks of just under the send buffer size to avoid the performance hit iMaxChunk -= 1 ;Reduce by 1 to be smaller than the send buffer Loop { ;Keep sending the data until we're done or until an error occurs ;Wait until we can send data (ie. when FD_WRITE arrives) While Not AHKsock_Sockets("GetSend", iSocket) Sleep -1 ;Check if we have less than the max chunk to send If (iLength < iMaxChunk) { Loop { ;Keep sending the data until we get WSAEWOULDBLOCK or until an error occurs ;Send using the traditional offset method If ((iSendResult := AHKsock_Send(iSocket, ptrData, iLength)) < 0) { If (iSendResult = -2) ;Check specifically for WSAEWOULDBLOCK Break ;Calling send() would have blocked the thread. Break the loop and we'll try again after we ;receive FD_WRITE Else Return iSendResult ;Something bad happened with AHKsock_Send. Return the same value we got. } Else { ;AHKsock_Send was able to send bytes. Let's check if it sent only part of what we requested If (iSendResult < iLength) ;Move the offset up by what we were able to send ptrData += iSendResult, iLength -= iSendResult Else Return ;We're done sending all the data } } } Else { ;Send up to max chunk If ((iSendResult := AHKsock_Send(iSocket, ptrData, iMaxChunk)) < 0) { If (iSendResult = -2) ;Check specifically for WSAEWOULDBLOCK Continue ;Calling send() would have blocked the thread. Continue the loop and we'll try again after ;we receive FD_WRITE Else Return iSendResult ;Something bad happened with AHKsock_Send. Return the same value we got. } Else ptrData += iSendResult, iLength -= iSendResult ;Move up offset by updating the pointer and length } } } } AHKsock_Close(iSocket = -1, iTimeout = 5000) { ;Make sure Winsock has been started up If Not AHKsock_Startup(1) Return ;There's nothing to close If (iSocket = -1) { ;We need to close all the sockets ;Check if we even have sockets to close If Not AHKsock_Sockets() { DllCall("Ws2_32\WSACleanup") AHKsock_Startup(2) ;Reset the value to show that we've turned off Winsock Return ;We're done! } ;Take the current time (needed for time-outing) iStartClose := A_TickCount Loop % AHKsock_Sockets() ;Close all sockets and cleanup AHKsock_ShutdownSocket(AHKsock_Sockets("GetSocketFromIndex", A_Index)) ;Check if we're in the OnExit subroutine If Not A_ExitReason { A_IsCriticalOld := A_IsCritical ;Make sure we can still receive FD_CLOSE msgs Critical, Off Thread, Priority, 0 ;We can try a graceful shutdown or wait for a timeout While (AHKsock_Sockets()) And (A_TickCount - iStartClose < iTimeout) Sleep, -1 ;Restore previous Critical Critical, %A_IsCriticalOld% } /*! Used for debugging purposes only If (i := AHKsock_Sockets()) { If (i = 1) OutputDebug, % "Cleaning up now, with the socket " AHKsock_Sockets("GetSocketFromIndex", 1) " remaining..." Else { OutputDebug, % "Cleaning up now, with the following sockets remaining:" Loop % AHKsock_Sockets() { OutputDebug, % AHKsock_Sockets("GetSocketFromIndex", A_Index) } } } */ DllCall("Ws2_32\WSACleanup") AHKsock_Startup(2) ;Reset the value to show that we've turned off Winsock ;Close only one socket } Else If AHKsock_ShutdownSocket(iSocket) ;Error-checking Return 1 ;The shutdown() call failed. The error is in ErrorLevel. } AHKsock_GetAddrInfo(sHostName, ByRef sIPList, bOne = False) { ;Make sure Winsock has been started up If (i := AHKsock_Startup()) Return i ;Return the same error (error 1 and 2) ;Resolve the address and port VarSetCapacity(aiHints, 16 + 4 * A_PtrSize, 0) NumPut(2, aiHints, 4, "Int") ;ai_family = AF_INET NumPut(1, aiHints, 8, "Int") ;ai_socktype = SOCK_STREAM NumPut(6, aiHints, 12, "Int") ;ai_protocol = IPPROTO_TCP iResult := DllCall("Ws2_32\GetAddrInfo", "Ptr", &sHostName, "Ptr", 0, "Ptr", &aiHints, "Ptr*", aiResult) If (iResult = 11001) ;Check specifically for WSAHOST_NOT_FOUND since it's the most common error Return 3 ;Received WSAHOST_NOT_FOUND. No such host is known. Else If (iResult != 0) Or ErrorLevel { ;Check for any other error ErrorLevel := ErrorLevel ? ErrorLevel : iResult Return 4 ;The getaddrinfo() call failed. The error is in ErrorLevel. } If bOne sIPList := DllCall("Ws2_32\inet_ntoa", "UInt", NumGet(NumGet(aiResult+0, 16 + 2 * A_PtrSize)+4, 0, "UInt"), "AStr") Else { ;Start with the first addrinfo struct iPointer := aiResult, sIPList := "" While iPointer { s := DllCall("Ws2_32\inet_ntoa", "UInt", NumGet(NumGet(iPointer+0, 16 + 2 * A_PtrSize)+4, 0, "UInt"), "AStr") iPointer := NumGet(iPointer+0, 16 + 3 * A_PtrSize) ;Go to the next addrinfo struct sIPList .= s (iPointer ? "`n" : "") ;Add newline only if it's not the last one } } ;We're done DllCall("Ws2_32\FreeAddrInfo", "Ptr", aiResult) } AHKsock_GetNameInfo(sIP, ByRef sHostName, sPort = 0, ByRef sService = "") { ;Make sure Winsock has been started up If (i := AHKsock_Startup()) Return i ;Return the same error (error 1 and 2) ;Translate to IN_ADDR iIP := DllCall("Ws2_32\inet_addr", "AStr", sIP, "UInt") If (iIP = 0 Or iIP = 0xFFFFFFFF) ;Check for INADDR_NONE or INADDR_ANY Return 3 ;The IP address supplied in sIP is invalid. ;Construct a sockaddr struct VarSetCapacity(tSockAddr, 16, 0) NumPut(2, tSockAddr, 0, "Short") ;ai_family = AF_INET NumPut(iIP, tSockAddr, 4, "UInt") ;Put in the IN_ADDR ;Fill in the port field if we're also looking up the service name If sPort ;Translate to network byte order NumPut(DllCall("Ws2_32\htons", "UShort", sPort, "UShort"), tSockAddr, 2, "UShort") ;Prep vars VarSetCapacity(sHostName, 1025 * 2, 0) ;NI_MAXHOST If sPort VarSetCapacity(sService, 32 * 2, 0) ;NI_MAXSERV iResult := DllCall("Ws2_32\GetNameInfoW", "Ptr", &tSockAddr, "Int", 16, "Str", sHostName, "UInt", 1025 * 2 , sPort ? "Str" : "UInt", sPort ? sService : 0, "UInt", 32 * 2, "Int", 0) If (iResult != 0) Or ErrorLevel { ErrorLevel := ErrorLevel ? ErrorLevel : DllCall("Ws2_32\WSAGetLastError") Return 4 ;The getnameinfo() call failed. The error is in ErrorLevel. } } AHKsock_SockOpt(iSocket, sOption, iValue = -1) { ;Prep variable VarSetCapacity(iOptVal, iOptValLength := 4, 0) If (iValue <> -1) NumPut(iValue, iOptVal, 0, "UInt") If (sOption = "SO_KEEPALIVE") { intLevel := 0xFFFF ;SOL_SOCKET intOptName := 0x0008 ;SO_KEEPALIVE } Else If (sOption = "SO_SNDBUF") { intLevel := 0xFFFF ;SOL_SOCKET intOptName := 0x1001 ;SO_SNDBUF } Else If (sOption = "SO_RCVBUF") { intLevel := 0xFFFF ;SOL_SOCKET intOptName := 0x1002 ;SO_SNDBUF } Else If (sOption = "TCP_NODELAY") { intLevel := 6 ;IPPROTO_TCP intOptName := 0x0001 ;TCP_NODELAY } ;Check if we're getting or setting If (iValue = -1) { iResult := DllCall("Ws2_32\getsockopt", "Ptr", iSocket, "Int", intLevel, "Int", intOptName , "UInt*", iOptVal, "Int*", iOptValLength) If (iResult = -1) Or ErrorLevel { ;Check for SOCKET_ERROR ErrorLevel := ErrorLevel ? ErrorLevel : AHKsock_LastError() Return -1 } Else Return iOptVal } Else { iResult := DllCall("Ws2_32\setsockopt", "Ptr", iSocket, "Int", intLevel, "Int", intOptName , "Ptr", &iOptVal, "Int", iOptValLength) If (iResult = -1) Or ErrorLevel { ;Check for SOCKET_ERROR ErrorLevel := ErrorLevel ? ErrorLevel : AHKsock_LastError() Return -2 } } } /*******************\ Support functions | */ AHKsock_Startup(iMode = 0) { Static bAlreadyStarted /* iMode = 0 ;Turns on WSAStartup() iMode = 1 ;Returns whether or not WSAStartup has been called iMode = 2 ;Resets the static variable to force another call next time iMode = 0 */ If (iMode = 2) bAlreadyStarted := False Else If (iMode = 1) Return bAlreadyStarted Else If Not bAlreadyStarted { ;iMode = 0. Call the function only if it hasn't already been called. ;Start it up - request version 2.2 VarSetCapacity(wsaData, A_PtrSize = 4 ? 400 : 408, 0) iResult := DllCall("Ws2_32\WSAStartup", "UShort", 0x0202, "Ptr", &wsaData) If (iResult != 0) Or ErrorLevel { ErrorLevel := ErrorLevel ? ErrorLevel : iResult Return 1 } ;Make sure the Winsock DLL supports at least version 2.2 If (NumGet(wsaData, 2, "UShort") < 0x0202) { DllCall("Ws2_32\WSACleanup") ;Abort ErrorLevel := "The Winsock DLL does not support version 2.2." Return 2 } bAlreadyStarted := True } } AHKsock_ShutdownSocket(iSocket) { ;Check if it's a listening socket sName := AHKsock_Sockets("GetName", iSocket) If (sName != A_Space) { ;It's not a listening socket. Shutdown send operations. iResult := DllCall("Ws2_32\shutdown", "Ptr", iSocket, "Int", 1) ;SD_SEND If (iResult = -1) Or ErrorLevel { sErrorLevel := ErrorLevel ? ErrorLevel : AHKsock_LastError() DllCall("Ws2_32\closesocket", "Ptr", iSocket) AHKsock_Sockets("Delete", iSocket) ErrorLevel := sErrorLevel Return 1 } ;Mark it AHKsock_Sockets("SetShutdown", iSocket) } Else { DllCall("Ws2_32\closesocket", "Ptr", iSocket) ;It's only a listening socket AHKsock_Sockets("Delete", iSocket) ;Remove it from the array } } /***********************\ AsyncSelect functions | */ ;FD_READ | FD_WRITE | FD_ACCEPT | FD_CLOSE AHKsock_RegisterAsyncSelect(iSocket, fFlags = 43, sFunction = "AHKsock_AsyncSelect", iMsg = 0) { Static hwnd := False If Not hwnd { ;Use the main AHK window A_DetectHiddenWindowsOld := A_DetectHiddenWindows DetectHiddenWindows, On WinGet, hwnd, ID, % "ahk_pid " DllCall("GetCurrentProcessId") " ahk_class AutoHotkey" DetectHiddenWindows, %A_DetectHiddenWindowsOld% } iMsg := iMsg ? iMsg : AHKsock_Settings("Message") If (OnMessage(iMsg) <> sFunction) OnMessage(iMsg, sFunction) iResult := DllCall("Ws2_32\WSAAsyncSelect", "Ptr", iSocket, "Ptr", hwnd, "UInt", iMsg, "Int", fFlags) If (iResult = -1) Or ErrorLevel { ;Check for SOCKET_ERROR ErrorLevel := ErrorLevel ? ErrorLevel : AHKsock_LastError() Return 1 } } AHKsock_AsyncSelect(wParam, lParam) { Critical ;So that messages are buffered ;wParam parameter identifies the socket on which a network event has occurred ;The low word of lParam specifies the network event that has occurred. ;The high word of lParam contains any error code ;Make sure the socket is on record. Fail-safe If Not AHKsock_Sockets("Index", wParam) Return iEvent := lParam & 0xFFFF, iErrorCode := lParam >> 16 /*! Used for debugging purposes OutputDebug, % "AsyncSelect - A network event " iEvent " has occurred on socket " wParam If iErrorCode OutputDebug, % "AsyncSelect - Error code = " iErrorCode */ If (iEvent = 1) { ;FD_READ ;Check for error If iErrorCode { ;WSAENETDOWN is the only possible ErrorLevel := iErrorCode ;FD_READ event received with an error. The error is in ErrorLevel. The socket is in iSocket. AHKsock_RaiseError(6, wParam) Return } VarSetCapacity(bufReceived, bufReceivedLength := AHKsock_Settings("Buffer"), 0) iResult := DllCall("Ws2_32\recv", "UInt", wParam, "Ptr", &bufReceived, "Int", bufReceivedLength, "Int", 0) If (iResult > 0) { ;We received data! VarSetCapacity(bufReceived, -1) ;Update the internal length ;Get associated function and call it If IsFunc(sFunc := AHKsock_Sockets("GetFunction", wParam)) %sFunc%("RECEIVED", wParam, AHKsock_Sockets("GetName", wParam) , AHKsock_Sockets("GetAddr", wParam) , AHKsock_Sockets("GetPort", wParam), bufReceived, iResult) ;Check for error other than WSAEWOULDBLOCK } Else If ErrorLevel Or ((iResult = -1) And Not ((iErrorCode := AHKsock_LastError()) = 10035)) { ErrorLevel := ErrorLevel ? ErrorLevel : iErrorCode AHKsock_RaiseError(7, wParam) ;The recv() call failed. The error is in ErrorLevel. The socket is in iSocket. iResult = -1 ;So that if it's a spoofed call from FD_CLOSE, we exit the loop and close the socket } ;Here, we bother with returning a value in case it's a spoofed call from FD_CLOSE Return iResult } Else If (iEvent = 2) { ;FD_WRITE ;Check for error If iErrorCode { ;WSAENETDOWN is the only possible ErrorLevel := iErrorCode ;FD_WRITE event received with an error. The error is in ErrorLevel. The socket is in iSocket. AHKsock_RaiseError(8, wParam) Return } ;Update socket's setting AHKsock_Sockets("SetSend", wParam, True) ;Make sure the socket isn't already shut down If Not AHKsock_Sockets("GetShutdown", wParam) If IsFunc(sFunc := AHKsock_Sockets("GetFunction", wParam)) %sFunc%("SEND", wParam, AHKsock_Sockets("GetName", wParam) , AHKsock_Sockets("GetAddr", wParam) , AHKsock_Sockets("GetPort", wParam)) } Else If (iEvent = 8) { ;FD_ACCEPT ;Check for error If iErrorCode { ;WSAENETDOWN is the only possible ErrorLevel := iErrorCode ;FD_ACCEPT event received with an error. The error is in ErrorLevel. The socket is in iSocket. AHKsock_RaiseError(9, wParam) Return } ;We need to accept the connection VarSetCapacity(tSockAddr, tSockAddrLength := 16, 0) sktClient := DllCall("Ws2_32\accept", "Ptr", wParam, "Ptr", &tSockAddr, "Int*", tSockAddrLength) If (sktClient = -1) And ((iErrorCode := AHKsock_LastError()) = 10035) ;Check specifically for WSAEWOULDBLOCK Return ;We'll be called again next time we can retry accept() Else If (sktClient = -1) Or ErrorLevel { ;Check for INVALID_SOCKET ErrorLevel := ErrorLevel ? ErrorLevel : iErrorCode ;The accept() call failed. The error is in ErrorLevel. The listening socket is in iSocket. AHKsock_RaiseError(10, wParam) Return } ;Add to array sName := "" sAddr := DllCall("Ws2_32\inet_ntoa", "UInt", NumGet(tSockAddr, 4, "UInt"), "AStr") sPort := AHKsock_Sockets("GetPort", wParam) sFunc := AHKsock_Sockets("GetFunction", wParam) AHKsock_Sockets("Add", sktClient, sName, sAddr, sPort, sFunc) ;Go back to listening iResult := DllCall("Ws2_32\listen", "Ptr", wParam, "Int", 0x7FFFFFFF) ;SOMAXCONN If (iResult = -1) Or ErrorLevel { ;Check for SOCKET_ERROR sErrorLevel := ErrorLevel ? ErrorLevel : AHKsock_LastError() DllCall("Ws2_32\closesocket", "Ptr", wParam) AHKsock_Sockets("Delete", wParam) ;Remove from array ErrorLevel := sErrorLevel ;The listen() call failed. The error is in ErrorLevel. The listening socket is in iSocket. AHKsock_RaiseError(12, wParam) Return } ;Get associated function and call it If IsFunc(sFunc) %sFunc%("ACCEPTED", sktClient, sName, sAddr, sPort) } Else If (iEvent = 32) { ;FD_CLOSE ;Keep receiving data before closing the socket by spoofing an FD_READ event to call recv() While (AHKsock_AsyncSelect(wParam, 1) > 0) Sleep, -1 ;Check if we initiated it If Not AHKsock_Sockets("GetShutdown", wParam) { ;Last chance to send data. Get associated function and call it. If IsFunc(sFunc := AHKsock_Sockets("GetFunction", wParam)) %sFunc%("SENDLAST", wParam, AHKsock_Sockets("GetName", wParam) , AHKsock_Sockets("GetAddr", wParam) , AHKsock_Sockets("GetPort", wParam)) ;Shutdown the socket. This is to attempt a graceful shutdown If AHKsock_ShutdownSocket(wParam) { ;The shutdown() call failed. The error is in ErrorLevel. The socket is in iSocket. AHKsock_RaiseError(13, wParam) Return } } ;We just have to close the socket then DllCall("Ws2_32\closesocket", "Ptr", wParam) ;Get associated data before deleting sFunc := AHKsock_Sockets("GetFunction", wParam) sName := AHKsock_Sockets("GetName", wParam) sAddr := AHKsock_Sockets("GetAddr", wParam) sPort := AHKsock_Sockets("GetPort", wParam) ;We can remove it from the array AHKsock_Sockets("Delete", wParam) If IsFunc(sFunc) %sFunc%("DISCONNECTED", wParam, sName, sAddr, sPort) } } /******************\ Array controller | */ AHKsock_Sockets(sAction = "Count", iSocket = "", sName = "", sAddr = "", sPort = "", sFunction = "") { Static Static aSockets0 := 0 Static iLastSocket := 0xFFFFFFFF ;Cache to lessen index lookups on the same socket Local i, ret, A_IsCriticalOld A_IsCriticalOld := A_IsCritical Critical If (sAction = "Count") { ret := aSockets0 } Else If (sAction = "Add") { aSockets0 += 1 ;Expand array aSockets%aSockets0%_Sock := iSocket aSockets%aSockets0%_Name := sName aSockets%aSockets0%_Addr := sAddr aSockets%aSockets0%_Port := sPort aSockets%aSockets0%_Func := sFunction aSockets%aSockets0%_Shutdown := False aSockets%aSockets0%_Send := False } Else If (sAction = "Delete") { ;First we need the index i := (iSocket = iLastSocket) ;Check cache ? iLastSocketIndex : AHKsock_Sockets("Index", iSocket) If i { iLastSocket := 0xFFFF ;Clear cache If (i < aSockets0) { ;Let the last item overwrite this one aSockets%i%_Sock := aSockets%aSockets0%_Sock aSockets%i%_Name := aSockets%aSockets0%_Name aSockets%i%_Addr := aSockets%aSockets0%_Addr aSockets%i%_Port := aSockets%aSockets0%_Port aSockets%i%_Func := aSockets%aSockets0%_Func aSockets%i%_Shutdown := aSockets%aSockets0%_Shutdown aSockets%i%_Send := aSockets%aSockets0%_Send } aSockets0 -= 1 ;Remove element } } Else If (sAction = "GetName") { i := (iSocket = iLastSocket) ;Check cache ? iLastSocketIndex : AHKsock_Sockets("Index", iSocket) ret := aSockets%i%_Name } Else If (sAction = "GetAddr") { i := (iSocket = iLastSocket) ;Check cache ? iLastSocketIndex : AHKsock_Sockets("Index", iSocket) ret := aSockets%i%_Addr } Else If (sAction = "GetPort") { i := (iSocket = iLastSocket) ;Check cache ? iLastSocketIndex : AHKsock_Sockets("Index", iSocket) ret := aSockets%i%_Port } Else If (sAction = "GetFunction") { i := (iSocket = iLastSocket) ;Check cache ? iLastSocketIndex : AHKsock_Sockets("Index", iSocket) ret := aSockets%i%_Func } Else If (sAction = "SetFunction") { i := (iSocket = iLastSocket) ;Check cache ? iLastSocketIndex : AHKsock_Sockets("Index", iSocket) aSockets%i%_Func := sName } Else If (sAction = "GetSend") { i := (iSocket = iLastSocket) ;Check cache ? iLastSocketIndex : AHKsock_Sockets("Index", iSocket) ret := aSockets%i%_Send } Else If (sAction = "SetSend") { i := (iSocket = iLastSocket) ;Check cache ? iLastSocketIndex : AHKsock_Sockets("Index", iSocket) aSockets%i%_Send := sName } Else If (sAction = "GetShutdown") { i := (iSocket = iLastSocket) ;Check cache ? iLastSocketIndex : AHKsock_Sockets("Index", iSocket) ret := aSockets%i%_Shutdown } Else If (sAction = "SetShutdown") { i := (iSocket = iLastSocket) ;Check cache ? iLastSocketIndex : AHKsock_Sockets("Index", iSocket) aSockets%i%_Shutdown := True } Else If (sAction = "GetSocketFromNamePort") { Loop % aSockets0 { If (aSockets%A_Index%_Name = iSocket) And (aSockets%A_Index%_Port = sName) { ret := aSockets%A_Index%_Sock Break } } } Else If (sAction = "GetSocketFromIndex") { ret := aSockets%iSocket%_Sock } Else If (sAction = "Index") { Loop % aSockets0 { If (aSockets%A_Index%_Sock = iSocket) { iLastSocketIndex := A_Index, iLastSocket := iSocket ret := A_Index Break } } } ;Restore old Critical setting Critical %A_IsCriticalOld% Return ret } /*****************\ Error Functions | */ AHKsock_LastError() { Return DllCall("Ws2_32\WSAGetLastError") } AHKsock_ErrorHandler(sFunction = """") { Static sCurrentFunction If (sFunction = """") Return sCurrentFunction Else sCurrentFunction := sFunction } AHKsock_RaiseError(iError, iSocket = -1) { If IsFunc(sFunc := AHKsock_ErrorHandler()) %sFunc%(iError, iSocket) } /*******************\ Settings Function | */ AHKsock_Settings(sSetting, sValue = "") { Static iMessage := 0x8000 Static iBuffer := 65536 If (sSetting = "Message") { If Not sValue Return iMessage Else iMessage := (sValue = "Reset") ? 0x8000 : sValue } Else If (sSetting = "Buffer") { If Not sValue Return iBuffer Else iBuffer := (sValue = "Reset") ? 65536 : sValue } }
声明:站内资源为整理优化好的代码上传分享与学习研究,如果是开源代码基本都会标明出处,方便大家扩展学习路径。请不要恶意搬运,破坏站长辛苦整理维护的劳动成果。本站为爱好者分享站点,所有内容不作为商业行为。如若本站上传内容侵犯了原著者的合法权益,请联系我们进行删除下架。
评论(0)