Class WebSocket2 v1.1 (32bit+64bit) without IE support

Post your working scripts, libraries and tools for AHK v1.1 and older
User avatar
Xeo786
Posts: 759
Joined: 09 Nov 2015, 02:43
Location: Karachi, Pakistan

Class WebSocket2 v1.1 (32bit+64bit) without IE support

Post by Xeo786 » 21 Sep 2021, 08:11

Spoiler
WebSocket2 V1.1 (32bit+64bit) no IE support
1)Code Tested on Win7 32bit having IE8 and chrome debugging mode worked
2)Code Tested on Win10 64bit chrome debugging mode worked

Download DLL:
We only need WebSocketAsio-x86.dll or WebSocketAsio-x64.dll in Script Dir
https://github.com/pdckxd/WebSocketAsioAHK/tree/master/bin

Class:

Code: Select all

/*
	WS2 WebSocket 2 (No IE support) Class by XEO786
	
	project started with 
	Based on 	https://github.com/agrippa1994/WebSocket-API and https://github.com/xNilsx/Websocket-API
	but there was no websocket 64bit dll 
	
	now we using dllfiles from pdckxd WebSocketAsioAHK 
	following Class will need websocket dll files in script Dir
	https://github.com/pdckxd/WebSocketAsioAHK/tree/master/bin
	now using WebSocketAsioAHK for 32bit + 64bit support
	
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;;;;;;; Intelegent Class  ;;;;;;
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	This Class remebers how many __new() WS2 have been created 
	one can check how many working websocket have been created using WS2.base.WScount
	 	
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;;;;;;;Dll Loading and Unloading ;;;;;;
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	Dll unloaded and loaded automatically no matter how many New WS2 Created  
 	1) load DLL automatically when Very First WS2 called/created			i.e. obj := New WS2
 	2) unload DLL automatically when Last existing WS get deleted 			i.e. WS2 := ""
	but one can unload Dll and Load DLL manually as per following Condition
	1) You cannot Unload Dll when there are more than one working Websocket 	i.e. WS2.base.WScount > 1
 	2) Dll can only be unloaded manually when there is Last Websocket 		i.e. WS2.base.WScount = 1, WS2.UnloadDll() return 0 when failed
 	3) One can only load Dll manually after manually unloading			i.e. WS2.LoadDll() return 0 when failed
	
	examples
	ws1 := New WS2 ; create new Websocket and Load Dll library
	ws2 := New WS2 ; create another Websocket but will not Load Dll library again
	
	;this will first check for connection if connection found it will first disconnect then delete ws2
	ws2 := ""
	
	; after dealing with connection it will unload dll using dllcall FreeLibrary then delete ws1
	ws1 := "" 
*/
Class WS2
{
	__new()
	{
		static WScount := 0
		if(this.base.dll != 1) ; if library is already loaded
			this.LoadDll()
		this.base.WScount := ++WScount
	}
	
	__delete()
	{
		if this.isconnected()
			if this.Disconnect()
				while( this.Connection = 1) ; Wiating for connection termination
					sleep, 10
		this.base.WScount := this.base.WScount - 1
		if( this.base.WScount = 0) ; unload dll when Last WS getting deleted/closed
			if this.base.dll
				this.unLoadDll()
	}
	
	UnloadDll()
	{
		if( this.base.WScount > 1) ; incase someone manually unload dll 
			return
		
		if this.isconnected()  ; checking WS connection before unloading 
			if this.Disconnect() 
				while( this.Connection = 1) ; confirming ws connection terminated
					sleep, 10
			
		DllCall("FreeLibrary", "Ptr", this.base.hModule)
		this.base.dll := ""
		this.base.dllname := ""
		return 1
	}
	
	LoadDll()
	{
		if(this.base.dll = 1)  ; if library is already loaded
			return
		this.base.dll := 1
		if(A_PtrSize = 4)
			int := "int", Dll := "x86"
		else
			int := "int64", Dll := "x64"
		this.base.dllname := "WebSocketAsio-" Dll ".dll"
		hModule := DllCall("LoadLibrary", "str", A_ScriptDir "\WebSocketAsio-" Dll ".dll")
		if(hModule == -1 || hModule == 0)
		{
			MsgBox, 48, Error, The dll-file couldn't be found!
			ExitApp
		}
		this.base.hModule := hModule
		DllCall(this.base.dllname "\websocket_register_on_connect_cb"	, int, RegisterCallback(this._Handshake,"F",,&this), "char")
		DllCall(this.base.dllname "\websocket_register_on_fail_cb"		, int, RegisterCallback(this._Fail,"F",,&this), "char")
		DllCall(this.base.dllname "\websocket_register_on_disconnect_cb"	, int, RegisterCallback(this._close,"C F",,&this), "char")
		DllCall(this.base.dllname "\websocket_register_on_data_cb"		, int, RegisterCallback(this._receive,"C F",,&this), "char")
		return 1
	}
	
	Connect(hostStr)
	{
		if(this.base.dll = 0)
			return ;DLL have been manually unloaded Previously `nConnection Fail...!
		this.uri := hostStr
		return DllCall(this.base.dllname "\websocket_connect", "Str", hostStr, "Char")
	}
	
	Disconnect()
	{
		return DllCall(this.base.dllname "\websocket_disconnect", "UChar")
	}
	
	Send(data, len, is_binary)
	{
		This.response := "" ; simply empty respose before sending new request so we can simply wait
		return DllCall(this.base.dllname "\websocket_send", "Str", data, "Ptr", len, "UChar", is_binary, "UChar")
	}
	
	isconnected()
	{
		return DllCall(this.base.dllname "\websocket_isconnected", "UChar")
	}
	
	_Handshake()
	{
		Critical
		this := Object(A_EventInfo)
		this.Connection := 1
	}
	
	_fail()
	{
		Critical
		this := Object(A_EventInfo)
		this.Connection := 2
	}
	
	_close()
	{
		Critical
		this := Object(A_EventInfo)
		if this.notify
			MsgBox, 48, WS Notify, Disconnect...!
		this.Connection := 3
	}
	
	_receive(length,a,b,c,D,e,f,g,h,data)
	{
		Critical
		this := Object(A_EventInfo)
		this.response := StrGet(Data,length,"UTF-8")
	}
	
}
Example:

Code: Select all

#NoEnv
#SingleInstance Force
#Include WebSocket.ahk
ws := new WS2()
; I used chrome debugging mode method
; 1) run chrome into debugging protocole mode using localhost and port for example 127.0.0.1:9222
; 2) open some website in new tab
; 3) navigate to some Web page like . www.google.com
;  open some other browser
; 4) navigate to 127.0.0.1:9222/json
; 5) there you get that id 

pageid := "AB5D8071E53C71B3706DEB230C68EC1F"
wsUri := "ws://127.0.0.1:9222/devtools/page/" pageid

Gui, Add, ListBox, x12 y10 w590 h400 vView ,
; Edits
Gui, Add, Edit, x372 y420 w120 h20 vHost, % wsUri
Gui, Add, Edit, x22 y420 w260 h20 vMessage, {"id":1,"method":"DOM.getDocument"}
; Buttons
Gui, Add, Button, x502 y410 w100 h20 gBtnConnect, Connect
Gui, Add, Button, x502 y430 w100 h20 gBtnDisconnect, Disconnect
Gui, Add, Button, x292 y420 w70 h20 gBtnSend, Send
; Show GUI
Gui, Show, w620 h471, Websocket2
return

websocket_register_on_connect_cb()
{
	msgbox, here
}
GuiClose:
ws := "" ; being last WS2 this disconnect and unload Dll 
Gui, Destroy
exitapp

BtnConnect:
GuiControlGet, ip,, Host
if(strLen(ip) == 0)
{
	addText("The IP textfield doesn't contain any string!")
	return
}

res := ws.connect(ip)
if(res == 0)
{
	addText("ws.connect() returned 0.")
	return
}

while !(ws.connection = 1)
{
	sleep, 10
}

if(ws.connection = 1)
	addText("The connection has been established!")
else if(ws.connection = 2)
	addText("The connection couldn't be created!")
return

BtnDisconnect:
if(!ws.disconnect())
{
	addText("The connection couldn't be closed. The connection is already closed!")
	return
}
addText("The connection has been closed!")
return

BtnSend:

if(ws.connection = 2)
{
	addText("Unable to Send connection with Websocket have aleready been terminated")
	return
}

if(!ws.isconnected())
{
	addText("The connection isn't estabilshed!")
	return
}

GuiControlGet, msg,, Message
if(strLen(msg) == 0)
{
	addText("The message textfield doesn't contain any string!")
	return
}

if(ws.send(msg, strLen(msg), false))
	addText("The message has been sent!")
else
{
	addText("The message couldn't be sent!")
	return
}

while !ws.response
{
	sleep, 10
}

	addText("Data has been received:" ws.response)
return

addText(t)
{
	GuiControl,, View, %t%
}
Image

Thanks:
agrippa1994 for his Websocket
pdckxd for his WebSocketAsioAHK
Geek aka Geekdude, Oldman, helgef their code and posts helped to understand callbacks binding with class methods
swagfag for helping me correct stuff

Changes:
1) Shortened few function Names for writing convenience
2)have implemented response mechanism to Websocket class by binding register callback to class methods
3)Made few changes in class point out by swagfag
4)I left Agrippa Websocket.dll reason I didnot find 64bit support
5)now using WebSocketAsioAHK dll by pdckxd reason now WS2 support 32bit and 64 and I have change the class entirely
6)swagfag pointed out dll library loading so I made this class to be intelligent enough it decide by itself when to load and unload dll
Last edited by Xeo786 on 24 Sep 2021, 11:17, edited 18 times in total.
tuzi
Posts: 223
Joined: 27 Apr 2016, 23:40

Re: Class WebSocket2 without IE support

Post by tuzi » 21 Sep 2021, 08:39

Awesome, it looks like we can get a chrome library that doesn't need ie10 support. :thumbup: :thumbup:
User avatar
Xeo786
Posts: 759
Joined: 09 Nov 2015, 02:43
Location: Karachi, Pakistan

Re: Class WebSocket2 without IE support

Post by Xeo786 » 21 Sep 2021, 08:46

tuzi wrote:
21 Sep 2021, 08:39
Awesome, it looks like we can get a chrome library that doesn't need ie10 support. :thumbup: :thumbup:
:angel: Yah
"When there is no gravity, there is absolute vacuum and light travel with no time" -Game changer theory
gregster
Posts: 8921
Joined: 30 Sep 2013, 06:48

Re: Class WebSocket2 without IE support

Post by gregster » 21 Sep 2021, 09:09

tuzi wrote:
21 Sep 2021, 08:39
Awesome, it looks like we can get a chrome library that doesn't need ie10 support. :thumbup: :thumbup:
Geek (aka GeekDude) is working on this update anyway, afaik.
User avatar
Xeo786
Posts: 759
Joined: 09 Nov 2015, 02:43
Location: Karachi, Pakistan

Re: Class WebSocket2 without IE support

Post by Xeo786 » 21 Sep 2021, 09:55

gregster wrote:
21 Sep 2021, 09:09
tuzi wrote:
21 Sep 2021, 08:39
Awesome, it looks like we can get a chrome library that doesn't need ie10 support. :thumbup: :thumbup:
Geek (aka GeekDude) is working on this update anyway, afaik.
I am desperately waiting for geekdude MCL version websockets, and his chrome.ahk is the reason why I learned about websocket, If dllcall method did not work, then I would have written websocket class using clr.ahk

My next step to use this websocket with Chrome DOM wrapper. :)
"When there is no gravity, there is absolute vacuum and light travel with no time" -Game changer theory
User avatar
Xeo786
Posts: 759
Joined: 09 Nov 2015, 02:43
Location: Karachi, Pakistan

Re: Class WebSocket2 without IE support

Post by Xeo786 » 22 Sep 2021, 08:31

I need insight on following code,

Code: Select all

DllCall(this.5, "UInt", 0, "Ptr", RegisterCallback(this._Handshake,"C Fast",,&this), "ptr")
agrippa1994 was using "F" as Option parameter for RegisterCallback, but it did not worked (as autohotkey process crash right after this dllcall) when I tried to bind callback with class method untill I change it to "C Fast"

and I want to know is there need to use "GlobalFree" in this class, if there is need than when I and where I should use it,

Code: Select all

DllCall("GlobalFree", "Ptr", Address, "Ptr")
"When there is no gravity, there is absolute vacuum and light travel with no time" -Game changer theory
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: Class WebSocket2 without IE support

Post by swagfag » 22 Sep 2021, 09:42

https://github.com/agrippa1994/WebSocket-API/commit/6b5cc12694bc1a82276ca00c104f0d9150b9ed0d
which ahk did u run this with?
the function is declared as

Code: Select all

extern "C" __declspec(dllexport) bool websocket_registerCallback(uint32_t dwType, uint32_t dwAddress);
ure trying to pass a Ptr, which would be 8 bytes on x64:

Code: Select all

this.5 := DllCall("GetProcAddress", "Ptr", hModule, "AStr", "websocket_registerCallback", "Ptr")
...                        vvvvv
DllCall(this.5, "UInt", 0, "Ptr", RegisterCallback(this._Handshake,"C Fast",,&this), "ptr")
but u shouldnt run it with ahk64 anyway, since the lib uses hardcoded asm intended for 32bit only

Code: Select all

"C Fast"
doubt this actually fixed anything. the callbacks are allegedly supposed to be __stdcall, not __cdecl. this change either does nothing(best case scenario), or corrupts ur stack(which may or may not cause ur script to crash at a later point in time/cause other issues/data loss/corruption/etc)

Code: Select all

hModule := DllCall("LoadLibrary", "str", A_ScriptDir "\websocket.dll", "ptr")
that loads the dll every time u create a new object. though not necessarily harmful, super pointless.
is there need to use "GlobalFree" in this class
yes, on the RegisterCallback return values - function pointers(that u arent storing anywhere)
User avatar
Xeo786
Posts: 759
Joined: 09 Nov 2015, 02:43
Location: Karachi, Pakistan

Re: Class WebSocket2 without IE support

Post by Xeo786 » 22 Sep 2021, 11:30

I am using ahk unicode 32bit on 32bit win7, Script from main post working flawlessly,

Using "F" worked fine with normal registrycallbacks, when I am not using class,
"F" did not worked when i set class method for resgitercallback, and I tried with "C fast" and it worked
I did have not run this script on 32bit ANSI AHK
I just test 64bit ahk, websocket.dll not even loading, do we need 64bit websocket.dll?

Callback become unresponsive if I try to set number of parameters in registrycallback that how many parameters should callback function have for this._receive
Last edited by Xeo786 on 22 Sep 2021, 12:29, edited 2 times in total.
"When there is no gravity, there is absolute vacuum and light travel with no time" -Game changer theory
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: Class WebSocket2 without IE support

Post by swagfag » 22 Sep 2021, 12:19

his callbacks all define 0 parameters and one 2 parameters:

Code: Select all

/// Enumeration which is used for callback identification
enum eCallbackType : uint32_t
{
	callback_on_connect = 0,
	callback_on_fail,
	callback_on_disconnect,
	callback_on_message,
	callback_numElements
};
...
if ((address = g_dwCallbackAdresses[callback_on_connect]))
    __asm call address
...
if ((address = g_dwCallbackAdresses[callback_on_fail]))
    __asm call address
...
if ((address = g_dwCallbackAdresses[callback_on_disconnect]))
    __asm call address
...
if ((address = g_dwCallbackAdresses[callback_on_message]))
{
	try
	{
		auto c_str = msg->get_payload().c_str();
		auto len = msg->get_payload().length();

		__asm push len
		__asm push c_str
		__asm call address
	}
        ...
ur methods all define 1 and 3 parameters respectively(because of the hidden this). replace with:

Code: Select all

DllCall(this.5, "UInt", 0, "UInt", RegisterCallback(this._Handshake.Bind(""), "Fast", 0, &this), "Char")
and paramCount 0, 0, 2 for the remaining ones
User avatar
Xeo786
Posts: 759
Joined: 09 Nov 2015, 02:43
Location: Karachi, Pakistan

Re: Class WebSocket2 without IE support

Post by Xeo786 » 22 Sep 2021, 12:45

Thanks, I did not know it would not work on 64bit OS, websocket.dll loading failed do we need 64bit dll version, I dont know how to convert or compile a 32bit dll into 64 bit dll :wtf:
"When there is no gravity, there is absolute vacuum and light travel with no time" -Game changer theory
User avatar
Xeo786
Posts: 759
Joined: 09 Nov 2015, 02:43
Location: Karachi, Pakistan

Re: Class WebSocket2 without IE support

Post by Xeo786 » 23 Sep 2021, 01:40

swagfag wrote:
22 Sep 2021, 12:19

Code: Select all

"C Fast"
doubt this actually fixed anything. the callbacks are allegedly supposed to be __stdcall, not __cdecl. this change either does nothing(best case scenario), or corrupts ur stack(which may or may not cause ur script to crash at a later point in time/cause other issues/data loss/corruption/etc)
You are describing "C" Stuff and I am pure Ahkteer :think: in simple I am noob :( I have to read about __stdcall and __cdecl first to answer that
swagfag wrote:
22 Sep 2021, 12:19
ur methods all define 1 and 3 parameters respectively(because of the hidden this). replace with:

Code: Select all

DllCall(this.5, "UInt", 0, "UInt", RegisterCallback(this._Handshake.Bind(""), "Fast", 0, &this), "Char")
and paramCount 0, 0, 2 for the remaining ones
above code is not working
thanks I have changed types according to your suggestion

Code: Select all

		; resgistering callbacks
		; what I have found...! I do not include C its not working..... just for this call back 
		DllCall(this.5, "UInt", 0, "UInt", RegisterCallback(this._Handshake,"C F",,&this), "char")
		
		; I let following option to "F" just like original source 
		DllCall(this.5, "UInt", 1, "UInt", RegisterCallback(this._Fail,"F",,&this), "char")
		DllCall(this.5, "UInt", 2, "UInt", RegisterCallback(this._close,"F",,&this), "char")
		; setting length does not work even for above callbacks
		DllCall(this.5, "UInt", 3, "UInt", RegisterCallback(this._receive,"F",,&this), "char")
Setting parameters length for callback make the callback unresponsive even using .bind()

Code: Select all

DllCall(this.5, "UInt", 3, "UInt", RegisterCallback(this._receive.Bind("Data","length"),"C Fast",2,&this), "char")
swagfag wrote:

Code: Select all

hModule := DllCall("LoadLibrary", "str", A_ScriptDir "\websocket.dll", "ptr")
that loads the dll every time u create a new object. though not necessarily harmful, super pointless.
I am thinking of using something like this to free library on disconnect and close event when connection lost or terminated

Code: Select all


	Disconnect() ; this is manual disconnect method
	{
		DllCall("FreeLibrary", "Ptr", this.hModule)
		return DllCall(this.2, "UChar")
	}
	
	_close() ; this is callback triggered when connection lost or terminated
	{
		Critical
		this := Object(A_EventInfo)
		this.Connection := 3
		DllCall("FreeLibrary", "Ptr", this.hModule)
		msgbox,, Websocket Disconnected alert, % "Following Webcsocket Connection have been terminated`n" this.uri
	}
swagfag wrote:
22 Sep 2021, 12:19
yes, on the RegisterCallback return values - function pointers(that u arent storing anywhere)
well first of all I am not storing any callback address in class, can callback address exist globally outside the function? just asking, as far what I understand by reading about callbacks they exist in memory and we need to free them or close terminate the script/process in order to free
please correct me If I am wrong.

can I use this code to free address before sending? where I will store all in memory addresses in this.address from callback function this._receive(length,a,b,c,Data)
in Agrippa's code he is using on_data(data, len), as for me I can't set callback parameters length, describe above, I figure out these five parameters, first is length and 5th is data and Idk above 2,3 and 4 (a,b,c) :HeHe:

Code: Select all

	Send(data, len, is_binary)
	{
		for k, v in this.Address
			DllCall("GlobalFree", "Ptr", v, "Ptr")
		This.response := "" ; simply empty respose before sending new request so we can simply wait
		return DllCall(this.3, "AStr", data, "Ptr", len, "UChar", is_binary, "UChar")
	}
"When there is no gravity, there is absolute vacuum and light travel with no time" -Game changer theory
User avatar
Xeo786
Posts: 759
Joined: 09 Nov 2015, 02:43
Location: Karachi, Pakistan

Re: Class WebSocket2 without IE support

Post by Xeo786 » 24 Sep 2021, 07:59

swagfag wrote:
22 Sep 2021, 09:42
but u shouldnt run it with ahk64 anyway, since the lib uses hardcoded asm intended for 32bit only
I took your point seriously, almost change entire class and now using different dll for 64bit support,
swagfag wrote:
22 Sep 2021, 09:42

Code: Select all

hModule := DllCall("LoadLibrary", "str", A_ScriptDir "\websocket.dll", "ptr")
that loads the dll every time u create a new object. though not necessarily harmful, super pointless.
Now we can do __new() more than once but class will only load dll once and unload on last __delete()
"When there is no gravity, there is absolute vacuum and light travel with no time" -Game changer theory
User avatar
Xeo786
Posts: 759
Joined: 09 Nov 2015, 02:43
Location: Karachi, Pakistan

Re: Class WebSocket2 v1.1 (32bit+64bit) without IE support

Post by Xeo786 » 24 Sep 2021, 10:07

Chrome.ahk moded for Websocket2
you need to include websocket lib and dll files
Now user with no IE11 or win7 IE8 can now automate chrome :angel:

Code: Select all

; Chrome.ahk v1.2
; Copyright GeekDude 2018
; https://github.com/G33kDude/Chrome.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()}
		else
			ChromeInst := new Chrome(ProfilePath)
		```
	*/
	FindInstances()
	{
		static Needle := "--remote-debugging-port=(\d+)"
		Out := {}
		for Item in ComObjGet("winmgmts:")
			.ExecQuery("SELECT CommandLine FROM Win32_Process"
			. " WHERE Name = 'chrome.exe'")
			if RegExMatch(Item.CommandLine, Needle, Match)
				Out[Match1] := Item.CommandLine
		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 Pahs\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 this.Jxon_Load(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:="")
	{
		;msgbox, % PageData[Key] "`n" MatchMode "`n" Value
		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)
				;msgbox, PageData[Key] "`n" MatchMode "`n" Value
			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)
			this.tabid := tabid
			; TODO: Throw exception on invalid objects
			if IsObject(wsurl)
				wsurl := wsurl.webSocketDebuggerUrl
			
			ws := new WS2
			ws.Connect(wsurl)
			
			if ws.isconnected()  ; checking WS connection before unloading 
				while( this.Connection = 1) ; confirming ws connection terminated
					sleep, 10
			this.ws := ws
			;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
			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": Chrome.Jxon_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.
		*/
		/*
			Document()
			{
				New Document
			}
		*/
		Call(DomainAndMethod, Params:="", WaitForResponse:=True)
		{
			ws := this.ws
			if !ws.isconnected()
				return
			
			ID := this.ID += 1
			
			msg := Chrome.Jxon_Dump({"id": ID
			, "params": Params ? Params : {}
			, "method": DomainAndMethod})
			
			if ws.send(msg, strLen(msg), false)
				while !ws.response
					sleep, 10
			response := Chrome.Jxon_Load(ws.response)
			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": Chrome.Jxon_True(),
				"silent": Chrome.Jxon_False(),
				"returnByValue": Chrome.Jxon_False(),
				"userGesture": Chrome.Jxon_True(),
				"awaitPromise": Chrome.Jxon_False()
			}
			))
			
			return response
		}
		
		Eval_obj(JS)
		{
			response := this.Call("Runtime.evaluate",
			( LTrim Join
			{
				"expression": JS,
				"objectGroup": "console",
				"includeCommandLineAPI": Chrome.Jxon_True(),
				"silent": Chrome.Jxon_False(),
				"returnByValue": Chrome.Jxon_True(),
				"userGesture": Chrome.Jxon_True(),
				"awaitPromise": Chrome.Jxon_False()
			}
			))
			
			if (response.exceptionDetails)
			{
				throw Exception(Chrome.Jxon_Dump(response.result.description)) ; Chrome.Jxon_Dump(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
		}
		
		
		/*
			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()
		{
			ws := this.ws 
			if !ws.isConnected()
				return
			ws.disconnect()
			ws := ""
			this.ws := ""
		}
	}
	
	
	Jxon_Load(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 ; string | number | true | false | null
			{
				if (ch == q) ; string
				{
					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
						
						; \uXXXX - JSON unicode escape sequence
						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 ; number | true | false | null
				{
					val := SubStr(src, pos, i := RegExMatch(src, "[\]\},\s]|$",, pos)-pos)
					
					; For numerical values, numerify integers and keep floats as is.
					; I'm not yet sure if I should numerify floats in v2.0-a ...
					static number := "number", integer := "integer"
					if val is %number%
					{
						if val is %integer%
							val += 0
					}
					; in v1.1, true,false,A_PtrSize,A_IsUnicode,A_Index,A_EventInfo,
					; SOMETIMES return strings due to certain optimizations. Since it
					; is just 'SOMETIMES', numerify to be consistent w/ v2.0-a
					else if (val == "true" || val == "false")
						val := %value% + 0
					; AHK_H has built-in null, can't do 'val := %value%' where value == "null"
					; as it would raise an exception in AHK_H(overriding built-in var)
					else if (val == "null")
						val := ""
					; any other values are invalid, continue to trigger error
					else if (pos--, next := "#")
						continue
					
					pos += i-1
				}
				
				is_array? ObjPush(obj, val) : obj[key] := val
				next := obj==tree ? "" : is_array ? ",]" : ",}"
			}
		}
		
		return tree[1]
	}
	
	Jxon_Dump(obj, indent:="", lvl:=1)
	{
		static q := Chr(34)
		
		if IsObject(obj)
		{
			static Type := Func("Type")
			if Type ? (Type.Call(obj) != "Object") : (ObjGetCapacity(obj) == "")
				throw Exception("Object type not supported.", -1, Format("<Object at 0x{:p}>", &obj))
			
			prefix := SubStr(A_ThisFunc, 1, InStr(A_ThisFunc, ".",, 0))
			fn_t := prefix "Jxon_True",  obj_t := this ? %fn_t%(this) : %fn_t%()
			fn_f := prefix "Jxon_False", obj_f := this ? %fn_f%(this) : %fn_f%()
			
			if (&obj == &obj_t)
				return "true"
			else if (&obj == &obj_f)
				return "false"
			
			is_array := 0
			for k in obj
				is_array := k == A_Index
			until !is_array
			
			static integer := "integer"
			if indent is %integer%
			{
				if (indent < 0)
					throw Exception("Indent parameter must be a postive integer.", -1, indent)
				spaces := indent, indent := ""
				Loop % spaces
					indent .= " "
			}
			indt := ""
			Loop, % indent ? lvl : 0
				indt .= indent
			
			this_fn := this ? Func(A_ThisFunc).Bind(this) : A_ThisFunc
			lvl += 1, out := "" ; Make #Warn happy
			for k, v in obj
			{
				if IsObject(k) || (k == "")
					throw Exception("Invalid object key.", -1, k ? Format("<Object at 0x{:p}>", &obj) : "<blank>")
				
				if !is_array
					out .= ( ObjGetCapacity([k], 1) ? %this_fn%(k) : q . k . q ) ;// key
				.  ( indent ? ": " : ":" ) ; token + padding
				out .= %this_fn%(v, indent, lvl) ; value
				.  ( indent ? ",`n" . indt : "," ) ; token + indent
			}
			
			if (out != "")
			{
				out := Trim(out, ",`n" . indent)
				if (indent != "")
					out := "`n" . indt . out . "`n" . SubStr(indt, StrLen(indent)+1)
			}
			
			return is_array ? "[" . out . "]" : "{" . out . "}"
		}
		
		; Number
		else if (ObjGetCapacity([obj], 1) == "")
			return obj
		
		; String (null -> not supported by AHK)
		if (obj != "")
		{
			obj := StrReplace(obj,  "\",    "\\")
			, obj := StrReplace(obj,  "/",    "\/")
			, obj := StrReplace(obj,    q, "\" . q)
			, obj := StrReplace(obj, "`b",    "\b")
			, obj := StrReplace(obj, "`f",    "\f")
			, obj := StrReplace(obj, "`n",    "\n")
			, obj := StrReplace(obj, "`r",    "\r")
			, obj := StrReplace(obj, "`t",    "\t")
			
			static needle := (A_AhkVersion<"2" ? "O)" : "") . "[^\x20-\x7e]"
			while RegExMatch(obj, needle, m)
				obj := StrReplace(obj, m[0], Format("\u{:04X}", Ord(m[0])))
		}
		
		return q . obj . q
	}
	
	Jxon_True()
	{
		static obj := {}
		return obj
	}
	
	Jxon_False()
	{
		static obj := {}
		return obj
	}
}
"When there is no gravity, there is absolute vacuum and light travel with no time" -Game changer theory
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: Class WebSocket2 v1.1 (32bit+64bit) without IE support

Post by swagfag » 24 Sep 2021, 17:08

Xeo786 wrote:
23 Sep 2021, 01:40
above code is not working
:facepalm: :facepalm: :facepalm: yea, cause in v1 u couldnt pass boundfuncs to RegisterCallback(), how could i have forgotten... its almost as if there was a reason why i stopped using it
u can try RegisterCallback(this._Handshake, "F", 0, &this), which may or may not trick it into invoking it, despite the 1 formal parameter - hidden this, and leave it unpopulated
can callback address exist globally outside the function?
RegisterCallback internally GlobalAllocs a bunch of bytes for mcode. if u arent immediately passing the return value(a pointer to the allocated bytes) to GlobalFree(which would be an entirely pointless thing to do), and u arent keeping track of where the bytes were allocated(ie by assigning the returned pointer to a variable, for you to later GlobalFree with), thats what ud call "leaking memory"
can I use this code to free address before sending?
idk. if u free the callbacks by invoking Send() and there already was an OnReceive handler registered for use by the DLL, the DLL will try to call it and calling it, is calling already freed memory, which will crash ur script. at minimum, ud also have to clear the callback pointers u had stored in the DLL, since the DLLs do a null-check to see if they should try calling the handlers or not. in other words, ud have to DllCall websocket_registerCallback with <whatever the callback's enumtype was> and 0. which conveniently brings us to the second second point apparently:
neither of these libraries were designed with multiple instantiation in mind. they store their callbacks in global vars, so if u newed up another one of ur WS2s, ud be overwriting (all/any) already previously registered callbacks....perhaps it was best that these functions stayed functions...which brings us to the third point:
Xeo786 wrote:
24 Sep 2021, 07:59
Now we can do __new() more than once but class will only load dll once and unload on last __delete()
yeah this approach is a mess and in some cases will crash the script, but even if u coded around that, it still would be pointless since the DLLs only store 1 set of callback pointers at a time as already mentioned(u could copypaste the DLL with a different name and load it again, so each uniquely named loaded DLL would have its own set of callback pointers. and then delete the DLL files whenever the object ceases to exist, but frankly thats retarded). distilled down to the relevant parts only(from ur new code and Chrome.ahk), imagine the following scenario:
  1. user does some chrome shit that constructs them a Page, call it page1
  2. one of ur WS2s is created, call it ws1.
    • ws1.__new() is called, which in turn...
    • ...calls LoadDll(), which registers all callbacks to use the address of ws1(which is important since those callbacks use that address(weak ref, really) to recreate the object that registered them by calling Object(A_EventInfo))
  3. all is fine, the script works so far. now the user does more chrome shit that constructs them a Page, call it page1
  4. another WS2s is created, call it ws2.
    • ws2.__new() is called, which in turn...
    • fails the if-check and increments a counter, which meeeansss.... since LoadDll() wasnt called to set new callback addresses and the last time it was called, it set the callbacks to those of ws1, when u eventually connect with ws2, the callbacks of ws1 will be invoked! yeah..now at this point thats just bad logic, at least the script is still alive(although u can argue its better it had crashed, than having to track down heisenbugs), but not for long
  5. now the user decides page1 is no longer needed and calls Disconnect() on it
  6. all references to ws1 are released:

    Code: Select all

    ws := ""
    this.ws := ""
    ahk deallocates the object. its address now points to uninitialized memory
  7. some websocket activity causes ws2's callbacks to fire, eg _receive
    • this := Object(A_EventInfo) is run, which tries to recreate the original(ws1) object that registered the handler with ws1's now no longer valid address -> CRASH

Code: Select all

Class WS2 {
	__new() {
		static WScount := 0
		if(this.base.dll != 1) ; if library is already loaded
			this.LoadDll()
		this.base.WScount := ++WScount
	}

	LoadDll() {
		if(this.base.dll = 1)  ; if library is already loaded
			return

		this.base.dll := 1

		DllCall("websocket_register_on_data_cb"		, int, RegisterCallback(this._receive,"C F",,&this), "char")
	}

	_receive(length,a,b,c,D,e,f,g,h,data) {
		Critical
		this := Object(A_EventInfo) ; << POTENTIAL CRASH HERE, invalid ptr of already released obj
		this.response := StrGet(Data,length,"UTF-8")
	}
	
	__delete() {
                --this.base.WScount
		if( this.base.WScount = 0) ; unload dll when Last WS getting deleted/closed
			if this.base.dll
				this.unLoadDll()
	}
	
	UnloadDll() {
		if( this.base.WScount > 1) ; incase someone manually unload dll 
			return
		
		this.base.dll := ""
	}
}

class Page {
	__New(wsurl, fnCallback:="") {
		ws := new WS2
		ws.Connect(wsurl)
		this.ws := ws
	}

	Call(DomainAndMethod, Params:="", WaitForResponse:=True) {
		ws := this.ws
		if !ws.isconnected()
			return
		
		ID := this.ID += 1
		
		msg := Chrome.Jxon_Dump({"id": ID
		, "params": Params ? Params : {}
		, "method": DomainAndMethod})
		
		if ws.send(msg, strLen(msg), false)
			while !ws.response
				sleep, 10
		response := Chrome.Jxon_Load(ws.response)
		return response.result
	}
	
	/*
		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() {
		ws := this.ws 
		if !ws.isConnected()
			return
		ws.disconnect()
		ws := ""
		this.ws := ""
	}
}

function this._receive(length,a,b,c,Data)
in Agrippa's code he is using on_data(data, len), as for me I can't set callback parameters length, describe above, I figure out these five parameters, first is length and 5th is data and Idk above 2,3 and 4 (a,b,c) :HeHe:
(length,a,b,c,Data) is garbage, resulting from garbage parameter counts(or lack thereof). the fact that it (allegedly) "works", is at best a fluke and at worst a disaster in the making....
if u look at the source, ull see that those parameters are incorrect:
and if u dont wanna read all this shit, the summary is: find/write another DLL(or dont do classes, or if ure gonna do classes theyd have to be singletons)
User avatar
Xeo786
Posts: 759
Joined: 09 Nov 2015, 02:43
Location: Karachi, Pakistan

Re: Class WebSocket2 v1.1 (32bit+64bit) without IE support

Post by Xeo786 » 25 Sep 2021, 00:27

swagfag wrote:
24 Sep 2021, 17:08
and if u dont wanna read all this shit, the summary is: find/write another DLL(or dont do classes, or if ure gonna do classes theyd have to be singletons)
Thank you for detailed replay, and I read whole, now I understand flaw in my code

I might came up with few more question for that, I have to make few changes and test.

I keep asking question to myself after reading, is time have come to learn C? there are few reasons stopping me to do that :facepalm:
"When there is no gravity, there is absolute vacuum and light travel with no time" -Game changer theory
Milchmann
Posts: 112
Joined: 05 Nov 2016, 08:50

Re: Class WebSocket2 v1.1 (32bit+64bit) without IE support

Post by Milchmann » 25 Nov 2021, 05:18

Thank you very much.
It helps me extremely, because the normal Chrome.ahk no longer works for us due to the port lock at work.

The example of @burque505 : viewtopic.php?f=6&t=42890&hilit=selenium&start=320#p361682 works except for the feedback of
PageInst.WaitForLoad()
and
Easting := PageInst.Evaluate("document.getElementById('utm-easting').value;").value
There I don't get a value of chrome for either of them.
Under the regular chrome.ahk v1.2 it works without problems at home.

Do you have a solution there?

Thanks
Milchmann
Posts: 112
Joined: 05 Nov 2016, 08:50

Re: Class WebSocket2 v1.1 (32bit+64bit) without IE support

Post by Milchmann » 28 Jan 2022, 03:48

push 😎
User avatar
Xeo786
Posts: 759
Joined: 09 Nov 2015, 02:43
Location: Karachi, Pakistan

Re: Class WebSocket2 v1.1 (32bit+64bit) without IE support

Post by Xeo786 » 28 Jan 2022, 07:38

Milchmann wrote:
25 Nov 2021, 05:18
Thank you very much.
It helps me extremely, because the normal Chrome.ahk no longer works for us due to the port lock at work.
you can run chrome with different debugging port something like this

Code: Select all

ChromeInst := new Chrome(ChromeProfile,,,,7070)
"When there is no gravity, there is absolute vacuum and light travel with no time" -Game changer theory
Milchmann
Posts: 112
Joined: 05 Nov 2016, 08:50

Re: Class WebSocket2 v1.1 (32bit+64bit) without IE support

Post by Milchmann » 29 Jan 2022, 06:43

Xeo786 wrote:
28 Jan 2022, 07:38
Milchmann wrote:
25 Nov 2021, 05:18
Thank you very much.
It helps me extremely, because the normal Chrome.ahk no longer works for us due to the port lock at work.
you can run chrome with different debugging port something like this

Code: Select all

ChromeInst := new Chrome(ChromeProfile,,,,7070)
That is not the problem. Access to Chrome works. I can no longer access the local port (127.0.0.1) at work with Chrome.ahk. That's why I can not call the IE services, for that was also this contribution , which should make it possible to work without the IE services ( WS2 WebSocket 2 (No IE support) Class ) . The access with WS2 Websockets works, but I don't get any values from the elements.

Thanks
tuzi
Posts: 223
Joined: 27 Apr 2016, 23:40

Re: Class WebSocket2 v1.1 (32bit+64bit) without IE support

Post by tuzi » 23 Mar 2022, 22:29

Milchmann wrote:
25 Nov 2021, 05:18
Thank you very much.
It helps me extremely, because the normal Chrome.ahk no longer works for us due to the port lock at work.

The example of @burque505 : viewtopic.php?f=6&t=42890&hilit=selenium&start=320#p361682 works except for the feedback of
PageInst.WaitForLoad()
and
Easting := PageInst.Evaluate("document.getElementById('utm-easting').value;").value
There I don't get a value of chrome for either of them.
Under the regular chrome.ahk v1.2 it works without problems at home.

Do you have a solution there?

Thanks
@Milchmann

rename Evaluate() -> Eval_obj()
and rename Eval_obj() -> Evaluate()

but it has other problems, such as the process not exiting on exit.
Post Reply

Return to “Scripts and Functions (v1)”