#Requires AutoHotkey v2.0 /* 天黑聊天记录: 这个有点费内存 winhttprequest的缓冲区中途不会释放 winhttp调ahk写的回调会死掉【下载大文件,回调次数多了会死掉】 还是创建curl.exe子进程读stdout省力 */ Persistent DownloadAsync('https://www.autohotkey.com/download/ahk-v2.zip', A_ScriptDir '\xx.zip', callback) callback(a, b) { if (a > 0) { if (a = b) ToolTip('下载完成') else ToolTip('下载中...`n已下载:' DownloadSize(a) '`n文件大小:' (b < 0 ? '未知' : DownloadSize(b)) (b ? '`n百分比:' Round(a * 100 / b, 2) '%' : '')) } } DownloadSize(n) { n /= 1024 if (n > 1024) return Round(n/1024, 2) " MB" return Round(n, 2) " KB" } ; ============================ DownloadAsync.ahk ============================ class DownloadAsync { size := 0, totalsize := -1 ; 异步下载, 可获取下载进度 ; @param URL 待下载的URL地址, 包含http(s)头 ; @param Filename 保存的文件路径 ; @param OnProgress 下载进度回调函数 __New(URL, Filename, OnProgress := 0) { this.file := file := FileOpen(Filename, 'w-wd') if (!file) throw Error('创建文件失败', -1) this.req := req := WinHttpRequest() if (OnProgress) { req2 := WinHttpRequest() req2.Open('HEAD', URL, true) req2.OnResponseFinished := getFileSize.Bind(, ObjPtrAddRef(req2)) req2.Send() } req.Open('GET', URL, true) req.OnResponseDataAvailable := receiveData req.OnError := req.OnResponseFinished := finished req.Send() getFileSize(self, pself) { ObjRelease(pself) if (size := Integer(self.GetResponseHeader('Content-Length'))) this.totalsize := size } receiveData(self, pvData, cbElements) { this.file.RawWrite(pvData, cbElements) try OnProgress(this.size += cbElements, this.totalsize) } finished(self, msg := 0, data := 0) { this.file.Close() try msg ? OnProgress(-1, {msg: msg, err: data}) : OnProgress(s := this.size, s) self.OnError := self.OnResponseDataAvailable := self.OnResponseFinished := 0 } } } ; @file: WinHttpRequest.ahk ; @description: 网络请求库 ; @author thqby ; @date 2021/08/01 ; @version 0.0.18 class WinHttpRequest { static AutoLogonPolicy := { Always: 0, OnlyIfBypassProxy: 1, Never: 2 } static Option := { UserAgentString: 0, URL: 1, URLCodePage: 2, EscapePercentInURL: 3, SslErrorIgnoreFlags: 4, SelectCertificate: 5, EnableRedirects: 6, UrlEscapeDisable: 7, UrlEscapeDisableQuery: 8, SecureProtocols: 9, EnableTracing: 10, RevertImpersonationOverSsl: 11, EnableHttpsToHttpRedirects: 12, EnablePassportAuthentication: 13, MaxAutomaticRedirects: 14, MaxResponseHeaderSize: 15, MaxResponseDrainSize: 16, EnableHttp1_1: 17, EnableCertificateRevocationCheck: 18, RejectUserpwd: 19 } static PROXYSETTING := { PRECONFIG: 0, DIRECT: 1, PROXY: 2 } static SETCREDENTIALSFLAG := { SERVER: 0, PROXY: 1 } static SecureProtocol := { SSL2: 0x08, SSL3: 0x20, TLS1: 0x80, TLS1_1: 0x200, TLS1_2: 0x800, All: 0xA8 } static SslErrorFlag := { UnknownCA: 0x0100, CertWrongUsage: 0x0200, CertCNInvalid: 0x1000, CertDateInvalid: 0x2000, Ignore_All: 0x3300 } __New(UserAgent := unset) { (this.whr := ComObject('WinHttp.WinHttpRequest.5.1')).Option[0] := IsSet(UserAgent) ? UserAgent : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36 Edg/89.0.774.68' } request(url, method := 'GET', post_data?, headers := {}) { this.Open(method, url) for k, v in headers.OwnProps() this.SetRequestHeader(k, v) this.Send(post_data?) return this.ResponseText } enableRequestEvents(Enable := true) { static vtable := init_vtable() if !Enable return this._ievents := this._ref := 0 if this._ievents return IConnectionPointContainer := ComObjQuery(pwhr := ComObjValue(this.whr), '{B196B284-BAB4-101A-B69C-00AA00341D07}') DllCall('ole32\CLSIDFromString', 'str', '{F97F4E15-B787-4212-80D1-D380CBBF982E}', 'ptr', IID_IWinHttpRequestEvents := Buffer(16)) ComCall(4, IConnectionPointContainer, 'ptr', IID_IWinHttpRequestEvents, 'ptr*', IConnectionPoint := ComValue(0xd, 0)) ; IConnectionPointContainer->FindConnectionPoint IWinHttpRequestEvents := Buffer(3 * A_PtrSize) NumPut('ptr', vtable.Ptr, 'ptr', ObjPtr(this), 'ptr', ObjPtr(IWinHttpRequestEvents), IWinHttpRequestEvents) ComCall(5, IConnectionPoint, 'ptr', IWinHttpRequestEvents, 'uint*', &dwCookie := 0) ; IConnectionPoint->Advise this._ievents := { __Delete: (*) => ComCall(6, IConnectionPoint, 'uint', dwCookie) } static init_vtable() { vtable := Buffer(A_PtrSize * 7), offset := vtable.Ptr for nParam in StrSplit('3113213') offset := NumPut('ptr', CallbackCreate(EventHandler.Bind(A_Index), , Integer(nParam)), offset) vtable.DefineProp('__Delete', { call: __Delete }) return vtable static EventHandler(index, this, arg1 := 0, arg2 := 0) { if (index < 4) { IEvents := NumGet(this, A_PtrSize * 2, 'ptr') if index == 1 NumPut('ptr', this, arg2) if index == 3 ObjRelease(IEvents) else ObjAddRef(IEvents) return 0 } req := ObjFromPtrAddRef(NumGet(this, A_PtrSize, 'ptr')) req.readyState := index - 2 switch index { case 4: ; OnResponseStart try req.OnResponseStart(arg1, StrGet(arg2, 'utf-16')) case 5: ; OnResponseDataAvailable try req.OnResponseDataAvailable( NumGet((pSafeArray := NumGet(arg1, 'ptr')) + 8 + A_PtrSize, 'ptr'), NumGet(pSafeArray + 8 + A_PtrSize * 2, 'uint')) case 6: ; OnResponseFinished try req._ref := 0, req.OnResponseFinished() case 7: ; OnError try req.readyState := req._ref := 0, req.OnError(arg1, StrGet(arg2, 'utf-16')) } } static __Delete(this) { loop 7 CallbackFree(NumGet(this, (A_Index - 1) * A_PtrSize, 'ptr')) } } } ;#region IWinHttpRequest https://learn.microsoft.com/en-us/windows/win32/winhttp/iwinhttprequest-interface SetProxy(ProxySetting, ProxyServer, BypassList) => this.whr.SetProxy(ProxySetting, ProxyServer, BypassList) SetCredentials(UserName, Password, Flags) => this.whr.SetCredentials(UserName, Password, Flags) SetRequestHeader(Header, Value) => this.whr.SetRequestHeader(Header, Value) GetResponseHeader(Header) => this.whr.GetResponseHeader(Header) GetAllResponseHeaders() => this.whr.GetAllResponseHeaders() Send(Body?) => (this._ievents && this._ref := this, this.whr.Send(Body?)) Open(verb, url, async := false) { this.readyState := 0 this.whr.Open(verb, url, async) this.readyState := 1 } WaitForResponse(Timeout := -1) => this.whr.WaitForResponse(Timeout) Abort() => (this._ref := this.readyState := 0, this.whr.Abort()) SetTimeouts(ResolveTimeout := 0, ConnectTimeout := 60000, SendTimeout := 30000, ReceiveTimeout := 30000) => this.whr.SetTimeouts(ResolveTimeout, ConnectTimeout, SendTimeout, ReceiveTimeout) SetClientCertificate(ClientCertificate) => this.whr.SetClientCertificate(ClientCertificate) SetAutoLogonPolicy(AutoLogonPolicy) => this.whr.SetAutoLogonPolicy(AutoLogonPolicy) Status => this.whr.Status StatusText => this.whr.StatusText ResponseText => this.whr.ResponseText ResponseBody { get { pSafeArray := ComObjValue(t := this.whr.ResponseBody) pvData := NumGet(pSafeArray + 8 + A_PtrSize, 'ptr') cbElements := NumGet(pSafeArray + 8 + A_PtrSize * 2, 'uint') return ClipboardAll(pvData, cbElements) } } ResponseStream => this.whr.responseStream Option[Opt] { get => this.whr.Option[Opt] set => (this.whr.Option[Opt] := Value) } Headers { get { m := Map(), m.Default := '' loop parse this.GetAllResponseHeaders(), '`r`n' if (p := InStr(A_LoopField, ':')) m[SubStr(A_LoopField, 1, p - 1)] .= LTrim(SubStr(A_LoopField, p + 1)) return m } } /** * The OnError event occurs when there is a run-time error in the application. * @prop {(this,errCode,errDesc)=>void} OnError */ OnError := 0 /** * The OnResponseDataAvailable event occurs when data is available from the response. * @prop {(this,safeArray)=>void} OnResponseDataAvailable */ OnResponseDataAvailable := 0 /** * The OnResponseStart event occurs when the response data starts to be received. * @prop {(this,status,contentType)=>void} OnResponseDataAvailable */ OnResponseStart := 0 /** * The OnResponseFinished event occurs when the response data is complete. * @prop {(this)=>void} OnResponseDataAvailable */ OnResponseFinished := 0 ;#endregion readyState := 0, whr := 0, _ievents := 0 static __New() { if this != WinHttpRequest return this.DeleteProp('__New') for prop in ['OnError', 'OnResponseDataAvailable', 'OnResponseStart', 'OnResponseFinished'] this.Prototype.DefineProp(prop, { set: make_setter(prop) }) make_setter(prop) => (this, value := 0) => value && (this.DefineProp(prop, { call: value }), this.enableRequestEvents()) } }
声明:站内资源为整理优化好的代码上传分享与学习研究,如果是开源代码基本都会标明出处,方便大家扩展学习路径。请不要恶意搬运,破坏站长辛苦整理维护的劳动成果。本站为爱好者分享站点,所有内容不作为商业行为。如若本站上传内容侵犯了原著者的合法权益,请联系我们进行删除下架。
评论(0)