Here is the cJSon version of the script.
The LightJson class is not used, but I left it commented in case I would need it when some programs require 32 bit.
Maybe I add an If statement to use LightJson if 32bit is used
Code: Select all
; Chrome.ahk
; Created by GeekDude
; https://github.com/G33kDude/Chrome.ahk/blob/master/Chrome.ahk
; This version is modified with cJson class
; COMMENT: cJson only works for 64bit AHK!
class Chrome
{
static DebugPort := 9222
/*
Escape a string in a manner suitable for command line parameters
*/
CliEscape(Param)
{
return """" RegExReplace(Param, "(\\*)""", "$1$1\""") """"
}
/*
Finds instances of chrome in debug mode and the ports they're running
on. If no instances are found, returns a false value. If one or more
instances are found, returns an associative array where the keys are
the ports, and the values are the full command line texts used to start
the processes.
One example of how this may be used would be to open chrome on a
different port if an instance of chrome is already open on the port
you wanted to used.
```
; If the wanted port is taken, use the largest taken port plus one
DebugPort := 9222
if (Chromes := Chrome.FindInstances()).HasKey(DebugPort)
DebugPort := Chromes.MaxIndex() + 1
ChromeInst := new Chrome(ProfilePath,,,, DebugPort)
```
Another use would be to scan for running instances and attach to one
instead of starting a new instance.
```
if (Chromes := Chrome.FindInstances())
ChromeInst := {"base": Chrome, "DebugPort": Chromes.MinIndex(), PID: Chromes[Chromes.MinIndex(), "PID"]}
else
ChromeInst := new Chrome(ProfilePath)
```
*/
FindInstances()
{
Out := {}
for Item in ComObjGet("winmgmts:").ExecQuery("SELECT * FROM Win32_Process WHERE Name = 'chrome.exe'")
if RegExMatch(Item.CommandLine, "i)chrome.exe""?\s+--remote-debugging-port=(\d+)", Match)
Out[Match1] := {cmd: Item.CommandLine, PID: Item.ProcessId}
return Out.MaxIndex() ? Out : False
}
/*
ProfilePath - Path to the user profile directory to use. Will use the standard if left blank.
URLs - The page or array of pages for Chrome to load when it opens
Flags - Additional flags for chrome when launching
ChromePath - Path to chrome.exe, will detect from start menu when left blank
DebugPort - What port should Chrome's remote debugging server run on
*/
__New(ProfilePath:="", URLs:="about:blank", Flags:="", ChromePath:="", DebugPort:="")
{
; Verify ProfilePath
if (ProfilePath != "" && !InStr(FileExist(ProfilePath), "D"))
throw Exception("The given ProfilePath does not exist")
this.ProfilePath := ProfilePath
; Verify ChromePath
if (ChromePath == "")
FileGetShortcut, %A_StartMenuCommon%\Programs\Google Chrome.lnk, ChromePath
if (ChromePath == "")
RegRead, ChromePath, HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe
if !FileExist(ChromePath)
throw Exception("Chrome could not be found")
this.ChromePath := ChromePath
; Verify DebugPort
if (DebugPort != "")
{
if DebugPort is not integer
throw Exception("DebugPort must be a positive integer")
else if (DebugPort <= 0)
throw Exception("DebugPort must be a positive integer")
this.DebugPort := DebugPort
}
; Escape the URL(s)
for Index, URL in IsObject(URLs) ? URLs : [URLs]
URLString .= " " this.CliEscape(URL)
Run, % this.CliEscape(ChromePath)
. " --remote-debugging-port=" this.DebugPort
. (ProfilePath ? " --user-data-dir=" this.CliEscape(ProfilePath) : "")
. (Flags ? " " Flags : "")
. URLString
,,, OutputVarPID
this.PID := OutputVarPID
}
/*
End Chrome by terminating the process.
*/
Kill()
{
Process, Close, % this.PID
}
/*
Queries chrome for a list of pages that expose a debug interface.
In addition to standard tabs, these include pages such as extension
configuration pages.
*/
GetPageList()
{
http := ComObjCreate("WinHttp.WinHttpRequest.5.1")
http.open("GET", "http://127.0.0.1:" this.DebugPort "/json")
http.send()
;return LightJson.Parse(http.responseText)
return cJson.Loads(http.responseText)
}
/*
Returns a connection to the debug interface of a page that matches the
provided criteria. When multiple pages match the criteria, they appear
ordered by how recently the pages were opened.
Key - The key from the page list to search for, such as "url" or "title"
Value - The value to search for in the provided key
MatchMode - What kind of search to use, such as "exact", "contains", "startswith", or "regex"
Index - If multiple pages match the given criteria, which one of them to return
fnCallback - A function to be called whenever message is received from the page
*/
GetPageBy(Key, Value, MatchMode:="exact", Index:=1, fnCallback:="")
{
Count := 0
for n, PageData in this.GetPageList()
{
if (((MatchMode = "exact" && PageData[Key] = Value) ; Case insensitive
|| (MatchMode = "contains" && InStr(PageData[Key], Value))
|| (MatchMode = "startswith" && InStr(PageData[Key], Value) == 1)
|| (MatchMode = "regex" && PageData[Key] ~= Value))
&& ++Count == Index)
return new this.Page(PageData.webSocketDebuggerUrl, fnCallback)
}
}
/*
Shorthand for GetPageBy("url", Value, "startswith")
*/
GetPageByURL(Value, MatchMode:="startswith", Index:=1, fnCallback:="")
{
return this.GetPageBy("url", Value, MatchMode, Index, fnCallback)
}
/*
Shorthand for GetPageBy("title", Value, "startswith")
*/
GetPageByTitle(Value, MatchMode:="startswith", Index:=1, fnCallback:="")
{
return this.GetPageBy("title", Value, MatchMode, Index, fnCallback)
}
/*
Shorthand for GetPageBy("type", Type, "exact")
The default type to search for is "page", which is the visible area of
a normal Chrome tab.
*/
GetPage(Index:=1, Type:="page", fnCallback:="")
{
return this.GetPageBy("type", Type, "exact", Index, fnCallback)
}
/*
Connects to the debug interface of a page given its WebSocket URL.
*/
class Page
{
Connected := False
ID := 0
Responses := []
/*
wsurl - The desired page's WebSocket URL
fnCallback - A function to be called whenever message is received
*/
__New(wsurl, fnCallback:="")
{
this.fnCallback := fnCallback
this.BoundKeepAlive := this.Call.Bind(this, "Browser.getVersion",, False)
; TODO: Throw exception on invalid objects
if IsObject(wsurl)
wsurl := wsurl.webSocketDebuggerUrl
wsurl := StrReplace(wsurl, "localhost", "127.0.0.1")
this.ws := {"base": this.WebSocket, "_Event": this.Event, "Parent": this}
this.ws.__New(wsurl)
while !this.Connected
Sleep, 50
}
/*
Calls the specified endpoint and provides it with the given
parameters.
DomainAndMethod - The endpoint domain and method name for the
endpoint you would like to call. For example:
PageInst.Call("Browser.close")
PageInst.Call("Schema.getDomains")
Params - An associative array of parameters to be provided to the
endpoint. For example:
PageInst.Call("Page.printToPDF", {"scale": 0.5 ; Numeric Value
, "landscape": LightJson.true ; Boolean Value
, "pageRanges: "1-5, 8, 11-13"}) ; String value
PageInst.Call("Page.navigate", {"url": "https://autohotkey.com/"})
WaitForResponse - Whether to block until a response is received from
Chrome, which is necessary to receive a return value, or whether
to continue on with the script without waiting for a response.
*/
Call(DomainAndMethod, Params:="", WaitForResponse:=True)
{
if !this.Connected
throw Exception("Not connected to tab")
; Use a temporary variable for ID in case more calls are made
; before we receive a response.
ID := this.ID += 1
;~ this.ws.Send(LightJson.Stringify({"id": ID
;~ , "params": Params ? Params : {}
;~ , "method": DomainAndMethod}))
this.ws.Send(cJson.Dumps({"id": ID
, "params": Params ? Params : {}
, "method": DomainAndMethod}))
if !WaitForResponse
return
; Wait for the response
this.responses[ID] := False
while !this.responses[ID]{
Sleep, 50
; Added extra to prevent getting stuck
; if (A_Index=200){
; throw Exception("Chrome not responding",,"")
; }
}
; Get the response, check if it's an error
response := this.responses.Delete(ID)
if (response.error)
throw Exception("Chrome indicated error in response",, cJson.Dumps(response.error))
;~ throw Exception("Chrome indicated error in response",, LightJson.Stringify(response.error))
return response.result
}
/*
Run some JavaScript on the page. For example:
PageInst.Evaluate("alert(""I can't believe it's not IE!"");")
PageInst.Evaluate("document.getElementsByTagName('button')[0].click();")
*/
Evaluate(JS)
{
response := this.Call("Runtime.evaluate",
( LTrim Join
{
"expression": JS,
"objectGroup": "console",
"includeCommandLineAPI": cJson.true,
"silent": cJson.false,
"returnByValue": cJson.false,
"userGesture": cJson.true,
"awaitPromise": cJson.false
}
))
if (response.exceptionDetails)
throw Exception(response.result.description,, cJson.Dumps(response.exceptionDetails))
;~ throw Exception(response.result.description,, LightJson.Stringify(response.exceptionDetails))
return response.result
}
/*
Waits for the page's readyState to match the DesiredState.
DesiredState - The state to wait for the page's ReadyState to match
Interval - How often it should check whether the state matches
*/
WaitForLoad(DesiredState:="complete", Interval:=100)
{
while this.Evaluate("document.readyState").value != DesiredState
Sleep, Interval
}
/*
Internal function triggered when the script receives a message on
the WebSocket connected to the page.
*/
Event(EventName, Event)
{
; If it was called from the WebSocket adjust the class context
if this.Parent
this := this.Parent
; TODO: Handle Error events
if (EventName == "Open")
{
this.Connected := True
BoundKeepAlive := this.BoundKeepAlive
SetTimer, %BoundKeepAlive%, 15000
}
else if (EventName == "Message")
{
;~ data := LightJson.Parse(Event.data)
data := cJson.Loads(Event.data)
; Run the callback routine
fnCallback := this.fnCallback
if (newData := %fnCallback%(data))
data := newData
if this.responses.HasKey(data.ID)
this.responses[data.ID] := data
}
else if (EventName == "Close")
{
this.Disconnect()
}
else if (EventName == "Error")
{
throw Exception("Websocket Error!")
}
}
/*
Disconnect from the page's debug interface, allowing the instance
to be garbage collected.
This method should always be called when you are finished with a
page or else your script will leak memory.
*/
Disconnect()
{
if !this.Connected
return
this.Connected := False
this.ws.Delete("Parent")
this.ws.Disconnect()
BoundKeepAlive := this.BoundKeepAlive
SetTimer, %BoundKeepAlive%, Delete
this.Delete("BoundKeepAlive")
}
class WebSocket
{
__New(WS_URL)
{
static wb
; Create an IE instance
Gui, +hWndhOld
Gui, New, +hWndhWnd
this.hWnd := hWnd
Gui, Add, ActiveX, vWB, Shell.Explorer
Gui, %hOld%: Default
; Write an appropriate document
WB.Navigate("about:<!DOCTYPE html><meta http-equiv='X-UA-Compatible'"
. "content='IE=edge'><body></body>")
while (WB.ReadyState < 4)
sleep, 50
this.document := WB.document
; Add our handlers to the JavaScript namespace
this.document.parentWindow.ahk_savews := this._SaveWS.Bind(this)
this.document.parentWindow.ahk_event := this._Event.Bind(this)
this.document.parentWindow.ahk_ws_url := WS_URL
; Add some JavaScript to the page to open a socket
Script := this.document.createElement("script")
Script.text := "ws = new WebSocket(ahk_ws_url);`n"
. "ws.onopen = function(event){ ahk_event('Open', event); };`n"
. "ws.onclose = function(event){ ahk_event('Close', event); };`n"
. "ws.onerror = function(event){ ahk_event('Error', event); };`n"
. "ws.onmessage = function(event){ ahk_event('Message', event); };"
this.document.body.appendChild(Script)
}
; Called by the JS in response to WS events
_Event(EventName, Event)
{
this["On" EventName](Event)
}
; Sends data through the WebSocket
Send(Data)
{
this.document.parentWindow.ws.send(Data)
}
; Closes the WebSocket connection
Close(Code:=1000, Reason:="")
{
this.document.parentWindow.ws.close(Code, Reason)
}
; Closes and deletes the WebSocket, removing
; references so the class can be garbage collected
Disconnect()
{
if this.hWnd
{
this.Close()
Gui, % this.hWnd ": Destroy"
this.hWnd := False
}
}
}
}
}
class LightJson
{
static JS := LightJson.GetJS(), true := {}, false := {}, null := {}
Parse(json, _rec := false) {
if !_rec
obj := this.Parse(this.JS.eval("(" . json . ")"), true)
else if !IsObject(json)
obj := json
else if this.JS.Object.prototype.toString.call(json) == "[object Array]" {
obj := []
Loop % json.length
obj.Push( this.Parse(json[A_Index - 1], true) )
}
else {
obj := {}
keys := this.JS.Object.keys(json)
Loop % keys.length {
k := keys[A_Index - 1]
obj[k] := this.Parse(json[k], true)
}
}
Return obj
}
Stringify(obj, indent := "") {
if indent|1 {
for k, v in ["true", "false", "null"]
if (obj = this[v])
Return v
if IsObject( obj ) {
isArray := true
for key in obj {
if IsObject(key)
throw Exception("Invalid key")
if !( key = A_Index || isArray := false )
break
}
for k, v in obj
str .= ( A_Index = 1 ? "" : "," ) . ( isArray ? "" : """" . k . """:" ) . this.Stringify(v, true)
Return str = "" ? "{}" : isArray ? "[" . str . "]" : "{" . str . "}"
}
else if !(obj*1 = "" || RegExMatch(obj, "^-?0|\s"))
Return obj
for k, v in [["\", "\\"], [A_Tab, "\t"], ["""", "\"""], ["/", "\/"], ["`n", "\n"], ["`r", "\r"], [Chr(12), "\f"], [Chr(8), "\b"]]
obj := StrReplace( obj, v[1], v[2] )
Return """" obj """"
}
sObj := this.Stringify(obj, true)
Return this.JS.eval("JSON.stringify(" . sObj . ",'','" . indent . "')")
}
GetJS() {
static Doc, JS
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
}
}
#Include <cJson>
Code: Select all
;
; cJson.ahk 0.1.0
; Copyright (c) 2021 Philip Taylor (known also as GeekDude, G33kDude)
; https://github.com/G33kDude/cJson.ahk
;
; MIT License
;
; Permission is hereby granted, free of charge, to any person obtaining a copy
; of this software and associated documentation files (the "Software"), to deal
; in the Software without restriction, including without limitation the rights
; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
; copies of the Software, and to permit persons to whom the Software is
; furnished to do so, subject to the following conditions:
;
; The above copyright notice and this permission notice shall be included in all
; copies or substantial portions of the Software.
;
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
; SOFTWARE.
;
class cJson
{
static version := "0.1.0"
_init()
{
if (A_PtrSize =4 && !A_IsCompiled){
MsgBox,16,cJson, Please run this script with 64-bit Autohotkey.`n32-bit is not supported by cJson.`nCurrent thread will exit.
Exit
}
if (this.lib)
return
this.lib := this.MCLib.FromString("V0;__mcode_e_dumps-0,__mcode_e_loads-3437,__size-7328;"
. "trUAVUiJ5UiD7HAASIlNEEiJVRgATIlFIEyJTShASItFEEiLABBFADhIiwBIOcIPAIS0AAAAx0X8AQAUAOtHSIN9GAAAdC2LRfxImABIjRXlGwAA"
. "RCgPtgQBYBgBYI1IAAJIi1UYSIkKAGZBD77QZokQBOsPADYgiwCNUAIBARCJEINF/AENBX6lAD8BPoTAdaUJA3pNIAE/SYnISICJweiqCwAAA3FC"
. "GRBgxwAiAA5duAEBp+kuBgAAxkUk+wADU1AwAwdAIAkA0HVbABgBx0X0hQJoNYQQGItF9IBIAMHgBUgB0EiJTEXQgAuAAVAQgAuDDMABAA0AhZTA"
. "iEUA+4NF9AGAfftoAHQTARljARQFLXwKsgNWLIIPCEG4W2EBMQZBuHuAAxBgRCKJj1/HRfCCYA4FCwIZhE3wiU3Ig33waAB+XhmMLA8sGRcgCY8L"
. "D7aAOvABhMDAD4TYAQAAgSDGOnIwAUONgQE1K2dAAchTwA+NecMJRRIcVBLpZvZDUAkT6eLABMolOFXCJbDCq+zMq+zCm0baGfGr7MCrww8GwA/I"
. "qykRM/YInkUsTBjrG2nUEm0D2xI6vnkBDw+AtkAYPAF1H4MQi8AsahAm4BnpfALCL2FDBQYPhZRAQsUFOYBFKHVpx0XoDC6y6AIm3hcfLg8u6AAu"
. "TeMHnuAHBS7p9qoOMNWhDuSsDuSiBm2/DrAOVuSgDuMHLakOgKoOOJWhDuCsDuCiBv0Wvw5trw7goA7jB73gB6YOCsGnDkyLTShMoAViCYCLTThI"
. "iUwkALUUTTABASBhNC76/1j/6dhDZGM0BdU5GqngCumtQmTcrBjcohA2PL8YsBjcoBjjB/wVu01kTRDUQZsfhwsM8AAMY0h9wa0PjNvgHkyuXQ1E"
. "rn1frkrGSIPEcDxdw8TigCrH4jyTCAQNL5P04XYAFQ+3AGagg/gidWbZHVyVeK1WDpDDC0oOfEoOXF8OJy8HLwfmAukdLwfpCX0qBwgvBy8HLwcv"
. "B+ICYtgA6aozRyoHljMBJAc+DC8HLwcvBy8H4gJmAKTpNy8H6SMqBwovBw8vBy8HLwfiAm4A6cTbcz4qB7AzASQHDS8HLweHLwcvB+ICcgDpUS8H"
. "9Ok9KgcJLwcvBy8HLwch4gJ0AOne73vpyiMzASQHH3YNxwB+dr58/wf/B29v7wLgAnXvAnGkBg+3wLhJoEkxjeu6NMMEHs8EYABgAxIvUBkBCEUQ"
. "YAITDIXAD3iF/Pu1SV8JX04yBJCdQEggR0jRUiNIZolATxCNBbEQEBOJRfAB1bgyD7dFEIPgUA9IicIABPDQoQ8YtgBmMFritWaJVIRF6BECZsHo"
. "BBEEAYG3g338A37Ix0RF+LA7AOs8AwoiAQ8Ki1X4SGPSDzC3VFXo3w7wWG344aAE+AB5vsVTdwtgaVHHU8ZF/5Bm+EMCfRAQAHkIEAEBSPeCXaGO"
. "EEi6Z2YDAAJIwA736kiJ0EgIwfgCsBhJwfg/mEwpwJENMQHgAqGvAAHASCnBSInKIInRi0X4sAiJVfD4jVEwkw6AYf8E9ASQSMH5P2ADSCnRswGh"
. "IhAAdYCAff8YAHQShgRQBMdERbjQLQChDY8RjxFVn3BLjxGAETCFEcSAz9ZFWChIx3ECgTAwkwDr9hKRBEIHUAElsGjhKhEBEcMxIHTe+gAKdM4R"
. "+gANdL76AAl0rmH6AHsPhY50Tl4GGNK5sHkA/3ESwI8IjwgPjwiPCI8I9AB9D4S6J6RINAFwM4SnIAFMjQxFuIM54BeNVbRIgIlUJCBNicEChAGw"
. "Lk0Q6Kj+//9AhcB0HLj/AADp/FMNDw3fCt8K3wrfCvQASDp0CrMG5wy/BonOEACaMZkjC4tVQDAuCyz1/SILZASgYASLRSi0SGMQklXQDkUw+5Ce"
. "wQBFwJ4BBsAvcZ5xBBpMcEe6kRngk0H/0h13D1u/CH8Pfw+LRfO0ACBIiwAPtwBmAIP4DXS+SItFEQfwCXSuCngsdRchBDxIjVACAiiJEEDp3/3/"
. "/5AKTn0QdAq4/wAA6ccLDAAAD2IADChIi1UCwAMUMMcABQAAxAC4AAMA6ZYHMANKIFsPhd8BFEQYuQIEADP/0EiJRciM6xISIgdIIHTeCg8YCnTO"
. "Cg+dfV0PhAYLB0SBCYXAD4T4lQBATAJnTQALRRgAawAwSIlUJCBNiYDBSYnISInCgAwAEOgG/P//hcAphYqxCoIjMABWY8gDA4aBVchIx0QkKAsB"
. "gwEEIAEETItVEIBJiclJidC6AXVASInBQf/SACQcnQMkaAMkWXuTfKL+jXzyXQU31AnUJMB5wjADfzWARhCDfKMHDIMSIg/YhegDGDaAe/hAEMAA"
. "x0AShRYADADpeIcNA2gKdQQlP80YXA+FJzvZGEQidYAqABuBB4lVIPhmxwAikAzpAZeLHUAXzg1c0Q3JAswNqi/ODS/RDZHNDWLODaoI0Q1ZzQ1m"
. "zg0M8QaqIe0Gbu4GCvEG6etlqIP4cu4GDfEGse0GKnTuBgnxBnntBnUPDIU3lYehCAAAx0Uq9IJQ/yNT+OAGweCOBKF2AASsQS9+NuoBODl/JkQG"
. "4gUEAwHQyIPoMAYI6ZADC6cGiEB+M+oBRn8jtAhSN6YI601KBmBMCGZNVghXRwgkYTUG8SGDAEX0AYN99AMPBI73IHtIg0X4AhTrLgMGBAYGEEiN"
. "qkoEBgjDMUjAMU3gDvYSTSVAeHRAnsYsLw5jf2anIL8KBy10IT1oLg+EjtetazkPj8NgAnjHRfBgO+OJYQxjqsfmAIQCaAp1GWAFgRncfVgwdSKr"
. "CQ1MjU0LMER+ckxCYus+JbqJANBIweACSAHQwEgBwEmJwOYJoCYUi1XgCQpgBkgPv9DATAHAgAzQx5lJTtIc6wt+ogU5bICpS7AwLg+FsvIWAxIA"
. "ZoAP78DySA8qIRFwKPIPEeQlgHuwFOwBYRPrVYtV7InQAVALAdABwIlF7Hs/C6AGmAAusgSgBHAA0gFwAFXsZg8oyPIID17KswUQAPIPHFjBeAYp"
. "DSsOOX6LEfoAZXQU+gBFD4UGfz88dF+LAIP4AUdUGw8OCA7GRetNIRg1MAEBbwXrQT13Ait1vha4A58jTw1DDVQaxsAHCMdF5PEk6y6LVVrkxhVB"
. "DyHGFUSxF+RDDwYKBrLHReAxG8eERdzSBROLVeCoGwDgg0XcAYtF3AA7ReR85YB961gAdB+lGWASyTAbTabg8Bo2Gusd5AEIpB1aReABWSkcxBYZ"
. "5C2LgEXwSJhID691K6brgTsyAgJ1vgTwuQSFoz+sT15/x0XYsgwQQ4tF2MAFjRWrAUACD7YEEGYPvtNxBsUCOcLVhmavIeJMFtiwDyMEaCQEhMB1"
. "vqnzIABNVUMBFOMIHe0IqmbhCNTlCNSyBCHvCG3rCNf/N+II1OAIIwTeLxAC7whoR+MIjp1GbnUQecdF0OIIQItFWtCyBJi0BO8IB+II62JLvwiD"
. "RdCwCPMDWBW3CKyzCAe/CADrBQHCBEiD7IBdw5ABAAAiVW5rbm93AG5fT2JqZWN0CF8AIoUAdHJ1ZQAAZmFsc2UAbhB1bGwAlgJWYWwAdWVfADAx"
. "MjMANDU2Nzg5QUIQQ0RFRkIGAEdDAEM6ICh0ZG02ADQtMSkgMTAuGDMuMKMBAgA=")
this.pfnPush := RegisterCallback(this._Push, "Fast CDecl",, &this)
this.pfnGetObj := RegisterCallback(this._GetObj, "Fast CDecl",, &this)
}
_GetObj()
{
type := this, this := Object(A_EventInfo)
return Object(Object())
}
_Push(typeObject, value, typeValue, key, typeKey)
{
pObject := this, this := Object(A_EventInfo)
value := this._Value(value, typeValue)
if (typeObject == 4)
ObjPush(Object(pObject), value)
else if (typeObject == 5)
ObjRawSet(Object(pObject), this._Value(key, typeKey), value)
return 0
}
Dumps(obj)
{
this._init()
if (!IsObject(obj))
throw Exception("Input must be object")
size := 0
DllCall(this.lib.dumps, "Ptr", &obj, "Ptr", 0, "Int*", size
, "Ptr", &this.True, "Ptr", &this.False, "Ptr", &this.Null, "CDecl Ptr")
VarSetCapacity(buf, size*2+2, 0)
DllCall(this.lib.dumps, "Ptr", &obj, "Ptr*", &buf, "Int*", size
, "Ptr", &this.True, "Ptr", &this.False, "Ptr", &this.Null, "CDecl Ptr")
return StrGet(&buf, size, "UTF-16")
}
Loads(json)
{
this._init()
VarSetCapacity(pJson, A_PtrSize)
NumPut(&json, &pJson, 0, "Ptr")
if (r := DllCall(this.lib.loads, "Ptr", this.pfnPush, "Ptr", this.pfnGetObj, "Ptr", &pJson, "Int64*", pResult
, "Int*", resultType, "CDecl Int")) || ErrorLevel
{
throw Exception("Failed to parse JSON (" r "," ErrorLevel ")", -1
, Format("Unexpected character at position {}: '{}'"
, (NumGet(pJson)-&json)//2+1, Chr(NumGet(NumGet(pJson), "short"))))
}
return this._Value(pResult, resultType)
}
; type = 1: Integer, 2: Double, 3: String, 4: Array, 5: Object
_Value(value, type)
{
switch (type)
{
case 1: return value+0
case 2:
VarSetCapacity(tmp, 8, 0)
NumPut(value, &tmp, "Int64")
return NumGet(&tmp, 0, "Double")
case 3: return StrGet(value, "UTF-16")
case 4, 5: return Object(value), ObjRelease(value)
case 6: return value ? this.true : this.false
case 7: return this.null
}
throw Exception("Rehydration error: " type)
}
True[]
{
get
{
static _ := {"value": true, "name": "true"}
return _
}
}
False[]
{
get
{
static _ := {"value": false, "name": "false"}
return _
}
}
Null[]
{
get
{
static _ := {"value": "", "name": "null"}
return _
}
}
;
; MCLib.ahk redistributable pre-release
; https://github.com/G33kDude/MCLib.ahk
;
; Copyright (c) 2021 G33kDude, CloakerSmoker (CC-BY-4.0)
; https://creativecommons.org/licenses/by/4.0/
;
class MClib {
class LZ {
Decompress(pCData, cbCData, pData, cbData) {
if (r := DllCall("ntdll\RtlDecompressBuffer"
, "UShort", 0x102 ; USHORT CompressionFormat
, "Ptr", pData ; PUCHAR UncompressedBuffer
, "UInt", cbData ; ULONG UncompressedBufferSize
, "Ptr", pCData ; PUCHAR CompressedBuffer
, "UInt", cbCData ; ULONG CompressedBufferSize,
, "UInt*", cbFinal ; PULONG FinalUncompressedSize
, "UInt")) ; NTSTATUS
throw Exception("Error calling RtlDecompressBuffer",, Format("0x{:08x}", r))
return cbFinal
}
}
Load(pCode, CodeSize, Symbols) {
for SymbolName, SymbolOffset in Symbols {
if (RegExMatch(SymbolName, "O)__mcode_i_(\w+?)_(\w+)", Match)) {
DllName := Match[1]
FunctionName := Match[2]
hDll := DllCall("GetModuleHandle", "Str", DllName, "Ptr")
if (ErrorLevel || A_LastError) {
Throw "Could not load dll " DllName ", ErrorLevel " ErrorLevel ", LastError " Format("{:0x}", A_LastError)
}
pFunction := DllCall("GetProcAddress", "Ptr", hDll, "AStr", FunctionName, "Ptr")
if (ErrorLevel || A_LastError) {
Throw "Could not find function " FunctionName " from " DllName ".dll, ErrorLevel " ErrorLevel ", LastError " Format("{:0x}", A_LastError)
}
NumPut(pFunction, pCode + 0, SymbolOffset, "Ptr")
}
}
if !DllCall("VirtualProtect", "Ptr", pCode, "Ptr", CodeSize, "UInt", 0x40, "UInt*", OldProtect, "UInt")
Throw Exception("Failed to mark MCLib memory as executable")
Exports := {}
for SymbolName, SymbolOffset in Symbols {
if (RegExMatch(SymbolName, "O)__mcode_e_(\w+)", Match)) {
Exports[Match[1]] := pCode + SymbolOffset
}
}
if (Exports.Count()) {
return Exports
}
else {
return pCode + Symbols["__main"]
}
}
FromString(Code) {
Parts := StrSplit(Code, ";")
if (Parts[1] != "V0") {
Throw "Unknown MClib packed code format"
}
Symbols := {}
for k, SymbolEntry in StrSplit(Parts[2], ",") {
SymbolEntry := StrSplit(SymbolEntry, "-")
Symbols[SymbolEntry[1]] := SymbolEntry[2]
}
CodeBase64 := Parts[3]
DecompressedSize := Symbols.__Size
if !DllCall("Crypt32\CryptStringToBinary", "Str", CodeBase64, "UInt", 0, "UInt", 1
, "UPtr", 0, "UInt*", CompressedSize, "Ptr", 0, "Ptr", 0, "UInt")
throw Exception("Failed to parse MCLib b64 to binary")
CompressedSize := VarSetCapacity(DecompressionBuffer, CompressedSize, 0)
pDecompressionBuffer := &DecompressionBuffer
if !DllCall("Crypt32\CryptStringToBinary", "Str", CodeBase64, "UInt", 0, "UInt", 1
, "Ptr", pDecompressionBuffer, "UInt*", CompressedSize, "Ptr", 0, "Ptr", 0, "UInt")
throw Exception("Failed to convert MCLib b64 to binary")
if !(pBinary := DllCall("GlobalAlloc", "UInt", 0, "Ptr", DecompressedSize, "Ptr"))
throw Exception("Failed to reserve MCLib memory")
this.LZ.Decompress(pDecompressionBuffer, CompressedSize, pBinary, DecompressedSize)
return this.Load(pBinary, DecompressedSize, Symbols)
}
}
}