ReneMiner
26-03-2021, 21:38
When trying to create a user-defined control that will not mimic- but really act and react as the original cmd.exe - i did some research and stumbled across this article:
Using the DOS Command Prompt from Inside a VB Form (https://www.codeguru.com/columns/vb/using-the-dos-command-prompt-from-inside-a-vb-form.html)
After i studied that a bit - it not only shows how to create the basic control - but also how to spawn a new thread within the current script ] which might be interesting for those who want to do some multithreading-
- and again the requirement to pass an AddressOf :( which is substituted in thinBasic using CodePTR but not working with any combination of parameters i thought why it would not work. we have Function_GetPtr that is not accessible from outside of thinBasic-scope but in any case thinCore - that creates the pointers - could "delegate" by memorizing my passed thinBasic-Functionptr with
lets say the special keyword:
AddressOf( Byval mythinBasicScriptFunction As thinBasicScriptFunctionName, ByRef Handler As Number)
to create a handler
and instead of the parameter that asks the AddressOf i could later write
Call To Handler or Handler._I_CALL_NOW
the parser would find the keyword AddressOf - and send it to a function that i can not call nor see from thinBasic-script because it is not a regular function that would provide a keyword ---
--- but a method of a private thincore class-variable, udt, datatype.... Name it "Method.X" for now
Class methods are basically functions --- that should have an address in the sense of AddressOf.
' in thinBasic script i have to
Declare Function dllFuncName Alias "realFuncName"(,
...,{ByRef|Byval} pFuncAddr As lpWhateverProc,...,[ByRef|ByVal any parameters]) As ResultType
' so later I would use
dim myResult as ResultType
myResult = dllFuncName([any parameters]..., AddressOf(yeah what?),...,[any parameters])
' now think back to the declaration.
' When i study the declaration i know in advance that i
' must pass an AddressOf(something)
' but thincore does not know from the variables names.
' except (go back to declaration):
Declare Function dllFuncName Alias "realFuncName"([ByRef|ByVal any parameters],
..., {ByRef|ByVal} pFuncAddr As lpWhateverProc,... ,[{ByRef|ByVal} any parameters]) As ResultType
Dim Handler as Number ' dllFuncNameHandler for thinCore
[Declare|Substitute] AddressOf( [Callback] mythinBasicScriptFunction, Handler)
or using new vartype/udt/class:
Dim Handler As [New] AddressOf([Callback] mythinBasicScriptFunction)
now core is informed to connect mythinBasicScriptfunction before i call dllFuncName ---
Thincore will return the AddressOf method.X after the parser took a look ahead for the function that i named as myThinBasicScriptFunction. See below at "Is the function/header prefixed as CallBack?"
--- and i have received in Byref passed or dimensioned dedicated type variable "Handler" a number that i will use later when i call as
Dim myResult As ResultType
myResult = dllFuncName([any parameters],...,[Call[ At]|Callback[ At]|Using] Handler[.CallToActivate], ...,[any parameters])
Looking quite loose na - ...
the internal thincore-class method, thats address is to be used would look alike this:
class x[handler]class
internalCall as Long
' assumed user-scriptfunctions can/must receive a 32Bit number if not a callback.
' no matter if or what parameters are reality and must return a 32Bit-numeral as result
method X(Optional byval bReceived() as byte) As Long
static pScriptfunctions As String ' <= dynamic array of ptrs
static thinHandles As String ' from handles it should know if the associated scriptfunction is a callback
' alike if Handle > 0x7FFFFFFF consider it a callback
static activState as String
String sParam
Dword pParams, pMem, pFunc
Long lPos, lStart, Replied
Long isActive = 0x87654321
if Scriptfunctions <> "" then
local pScrFunc(lenf(pScriptfunctions)/SizeOf(Pointer))) As Pointer At Strptr(pScriptfunctions)
local hndle(ubound(pScrFunc)) as Dword at Strptr(thinHandles)
local state(ubound(pScrFunc)) as Long at Strptr(activState)
select case this.InternalCall
Case 0
' no its a call from somewhere else
' find a registered scriptfunction that is in active state = ready to be called
while lStart < Ubound(pScrFunc)
lPos = Array Scan state(lStart +1), = isActive
if lPos = 0 then
'did not find any active scriptfunction ?
' just ignore that we were called
' give an error-message to the user to register his scriptfunctions correctly
' free any allocated memory
if pMem then Heap_Free(pMem)
' and bye bye
Exit function
endif
lStart += lPos
if hndle(lStart) >= 0x7FFFFFFF then
' it's a callback
if not callbackReady then
' get it ready now (fill in pseudo-variables etc and evaluate the function parameters)
callBackReady = true
endif
' call the callback at pScriptFunc(lStart)
else
' its another scriptfunction
if sParam = "" then
' get parameters ready,
select case ubound(bReceived)
case 0
' no parameters...
sParam = String$(4, $NUL) ' create 32Bit var = 0
case 1 to 3
' pad using $NUL to have it 4 bytes
sParam = memory_get(varptr(bReceived(1)), Selectexpression) & Repeat$(4-SelectExpression, $NUL))
case 4
sParam = Memory_Get(Varptr(bReceived(1)),4)
case else
' its more than 32 bit... put it to memory and pass the pointer to the users scriptfunction
pMem = heap_Alloc(SelectExpression)
memory_copy(pMem, Varptr(bReceived), SelectExpression)
sParam = MKDWD$(pMem)
end Select
' sParam should hold 4 bytes now that are the binary apperance of a 32 bit numeral
' if user expects more than a 32 bit number he know its a pointer...
pFunc =pScriptFunc(lStart)
call pFunc( sParam ) to Replied
if replied > 0 then
' that was the function supposed to receive this message
' check what is the reply
' react and finally free the parameters data
if pMem then Heap_Free(pMem)
Exit While
endif
wend
case %RegisterNewScriptfunction
' bReceived() will hold the information that was passed with AddressOf and detected by the parser
' look ahead to the pointed function if meets requirements
' determine if callback, create new handle
' append scriptfunctionPtr, handle & state to the static strings/arrays
case %scriptfunctionActivated
' user called passing "Call To|Using" parameter with the handler
' check if handler valid, if callback and if ok return AddressOf Method.x to be inserted in the actual call
' set the scriptfunction to active state
end select
' clear the flag
this.InternalCall = 0
End Method
When i declare a Function|Sub as user i must specify ByRef/ByVal in front of a parameter.
For [B]any other parameters that are to pass ByRef ([B]or without to specify Byval) to the win32-Api:
i as user must declare them ByVal and
pass the VarPtr of a variable
or pass a Pointer32 to sufficient allocated memory.
(Alias DWord As Pointer32)
That works for sure.
Now when i declare some parameter AddressOf:
thincore does a call to its above mentioned private class-variables method or to a function which is not visible to COM.
The class needs a property that will be set by thinCore to signal the method when it was called by thincore to setup a new
AdressOf(something), to invoke a call = to mark a scriptfunction as currently active recipient or to set it inactive or to query any information internally so the method knows from the flag what to do.
The method must always clear the flag of course so if any call without flag: it is the call(back) from API to the method.X's address.
Method.X needs 3static function variables (or can be properties of the class as well).
Either dynamic strings or dynamic arrays of long or dword (or virtual dynamic arrays upon dynamic strings)
They correspond, means
element 1 of array scriptfuncptrs is associated to
element 1 of the handlers that the method assigned and associated to
element 1 of the current states
Array1 Array2 Array3
ScriptfuncPtr(1) Handler(1) State(1)
ScriptfuncPtr(2) Handler(2) State(2)
ScriptfuncPtr(...) Handler(...) State(...)
' simply:
<associate>ScriptfuncPtr(Index) <==> Handler(Index) <==> State(Index)</associate>
The first Array/String would hold the passed thinBasic-scriptFunction-Pointers and the second Array/String would hold the Handlers that the method assigned to keep track. The validity of a handler proves that the AddressOf-declaration was done correctly and successful if there is a valid scriptfuncptr with the same index.
the third array is actually not containing any pointers but flags that will keep the method informed if there is a call to expect / i.e.
the scriptfunction is an active recepient for any callbacks that might come in.
since any parameters are passed from thinBasic-script to the API are byval now there is nothing to store nor transform when my script calls the api/function, they can be passed right through.
When i Declare...AddressOf, i also name the function that will be used to receive any parameters.
Is the function/header prefixed as CallBack?
If yes,
it will receive the parameters as CBHNDL and all the other generic callback-pseudo-variables and the function header must not have any parameters between the parenthesis and return a value As Long, mandatory it is a function then. No Sub allowed.
Anything does not follow the rules => RTE !!!
If no,
all the parameters must be byval and it can return only a message to Method.X that says:
0: message not processed, wrong recipient
+1: message received and processed - if bit not set Method.X should call the next scriptfunction on its list
+2: reply "TRUE" to sender,( implements 1) means to return a value of -1 to the calling function from API
+4: continue passing messages here (implements 1) means to keep this scriptfunctions state as still active
====
result = 1 or result = 3 will set the state of this scriptfunction to 'no more active':method.x will not call this anymore
result = 0 or result = 5 or result = 7 will not change the state
to the
parameters of myScriptFunction
ByRef can not be here as usual. The user will have to split whatever parameters himself if more than 1 Byte to expect
it can be "Byval b() as Byte" or "byval bin$ as Rawtext"
thincore"s Method.X would just pass the bytes that it received as raw binary string
or allocate a heap and pass the pointer
its up to the user to split and parse it to parameters that can be passed to other script-functions.
He knows what data to expect and can be happy to receive the data at all since it was impossible before. And still is - regarding dynamic data to a universal format without to have billions of functions to achieve it.
Using the DOS Command Prompt from Inside a VB Form (https://www.codeguru.com/columns/vb/using-the-dos-command-prompt-from-inside-a-vb-form.html)
After i studied that a bit - it not only shows how to create the basic control - but also how to spawn a new thread within the current script ] which might be interesting for those who want to do some multithreading-
- and again the requirement to pass an AddressOf :( which is substituted in thinBasic using CodePTR but not working with any combination of parameters i thought why it would not work. we have Function_GetPtr that is not accessible from outside of thinBasic-scope but in any case thinCore - that creates the pointers - could "delegate" by memorizing my passed thinBasic-Functionptr with
lets say the special keyword:
AddressOf( Byval mythinBasicScriptFunction As thinBasicScriptFunctionName, ByRef Handler As Number)
to create a handler
and instead of the parameter that asks the AddressOf i could later write
Call To Handler or Handler._I_CALL_NOW
the parser would find the keyword AddressOf - and send it to a function that i can not call nor see from thinBasic-script because it is not a regular function that would provide a keyword ---
--- but a method of a private thincore class-variable, udt, datatype.... Name it "Method.X" for now
Class methods are basically functions --- that should have an address in the sense of AddressOf.
' in thinBasic script i have to
Declare Function dllFuncName Alias "realFuncName"(,
...,{ByRef|Byval} pFuncAddr As lpWhateverProc,...,[ByRef|ByVal any parameters]) As ResultType
' so later I would use
dim myResult as ResultType
myResult = dllFuncName([any parameters]..., AddressOf(yeah what?),...,[any parameters])
' now think back to the declaration.
' When i study the declaration i know in advance that i
' must pass an AddressOf(something)
' but thincore does not know from the variables names.
' except (go back to declaration):
Declare Function dllFuncName Alias "realFuncName"([ByRef|ByVal any parameters],
..., {ByRef|ByVal} pFuncAddr As lpWhateverProc,... ,[{ByRef|ByVal} any parameters]) As ResultType
Dim Handler as Number ' dllFuncNameHandler for thinCore
[Declare|Substitute] AddressOf( [Callback] mythinBasicScriptFunction, Handler)
or using new vartype/udt/class:
Dim Handler As [New] AddressOf([Callback] mythinBasicScriptFunction)
now core is informed to connect mythinBasicScriptfunction before i call dllFuncName ---
Thincore will return the AddressOf method.X after the parser took a look ahead for the function that i named as myThinBasicScriptFunction. See below at "Is the function/header prefixed as CallBack?"
--- and i have received in Byref passed or dimensioned dedicated type variable "Handler" a number that i will use later when i call as
Dim myResult As ResultType
myResult = dllFuncName([any parameters],...,[Call[ At]|Callback[ At]|Using] Handler[.CallToActivate], ...,[any parameters])
Looking quite loose na - ...
the internal thincore-class method, thats address is to be used would look alike this:
class x[handler]class
internalCall as Long
' assumed user-scriptfunctions can/must receive a 32Bit number if not a callback.
' no matter if or what parameters are reality and must return a 32Bit-numeral as result
method X(Optional byval bReceived() as byte) As Long
static pScriptfunctions As String ' <= dynamic array of ptrs
static thinHandles As String ' from handles it should know if the associated scriptfunction is a callback
' alike if Handle > 0x7FFFFFFF consider it a callback
static activState as String
String sParam
Dword pParams, pMem, pFunc
Long lPos, lStart, Replied
Long isActive = 0x87654321
if Scriptfunctions <> "" then
local pScrFunc(lenf(pScriptfunctions)/SizeOf(Pointer))) As Pointer At Strptr(pScriptfunctions)
local hndle(ubound(pScrFunc)) as Dword at Strptr(thinHandles)
local state(ubound(pScrFunc)) as Long at Strptr(activState)
select case this.InternalCall
Case 0
' no its a call from somewhere else
' find a registered scriptfunction that is in active state = ready to be called
while lStart < Ubound(pScrFunc)
lPos = Array Scan state(lStart +1), = isActive
if lPos = 0 then
'did not find any active scriptfunction ?
' just ignore that we were called
' give an error-message to the user to register his scriptfunctions correctly
' free any allocated memory
if pMem then Heap_Free(pMem)
' and bye bye
Exit function
endif
lStart += lPos
if hndle(lStart) >= 0x7FFFFFFF then
' it's a callback
if not callbackReady then
' get it ready now (fill in pseudo-variables etc and evaluate the function parameters)
callBackReady = true
endif
' call the callback at pScriptFunc(lStart)
else
' its another scriptfunction
if sParam = "" then
' get parameters ready,
select case ubound(bReceived)
case 0
' no parameters...
sParam = String$(4, $NUL) ' create 32Bit var = 0
case 1 to 3
' pad using $NUL to have it 4 bytes
sParam = memory_get(varptr(bReceived(1)), Selectexpression) & Repeat$(4-SelectExpression, $NUL))
case 4
sParam = Memory_Get(Varptr(bReceived(1)),4)
case else
' its more than 32 bit... put it to memory and pass the pointer to the users scriptfunction
pMem = heap_Alloc(SelectExpression)
memory_copy(pMem, Varptr(bReceived), SelectExpression)
sParam = MKDWD$(pMem)
end Select
' sParam should hold 4 bytes now that are the binary apperance of a 32 bit numeral
' if user expects more than a 32 bit number he know its a pointer...
pFunc =pScriptFunc(lStart)
call pFunc( sParam ) to Replied
if replied > 0 then
' that was the function supposed to receive this message
' check what is the reply
' react and finally free the parameters data
if pMem then Heap_Free(pMem)
Exit While
endif
wend
case %RegisterNewScriptfunction
' bReceived() will hold the information that was passed with AddressOf and detected by the parser
' look ahead to the pointed function if meets requirements
' determine if callback, create new handle
' append scriptfunctionPtr, handle & state to the static strings/arrays
case %scriptfunctionActivated
' user called passing "Call To|Using" parameter with the handler
' check if handler valid, if callback and if ok return AddressOf Method.x to be inserted in the actual call
' set the scriptfunction to active state
end select
' clear the flag
this.InternalCall = 0
End Method
When i declare a Function|Sub as user i must specify ByRef/ByVal in front of a parameter.
For [B]any other parameters that are to pass ByRef ([B]or without to specify Byval) to the win32-Api:
i as user must declare them ByVal and
pass the VarPtr of a variable
or pass a Pointer32 to sufficient allocated memory.
(Alias DWord As Pointer32)
That works for sure.
Now when i declare some parameter AddressOf:
thincore does a call to its above mentioned private class-variables method or to a function which is not visible to COM.
The class needs a property that will be set by thinCore to signal the method when it was called by thincore to setup a new
AdressOf(something), to invoke a call = to mark a scriptfunction as currently active recipient or to set it inactive or to query any information internally so the method knows from the flag what to do.
The method must always clear the flag of course so if any call without flag: it is the call(back) from API to the method.X's address.
Method.X needs 3static function variables (or can be properties of the class as well).
Either dynamic strings or dynamic arrays of long or dword (or virtual dynamic arrays upon dynamic strings)
They correspond, means
element 1 of array scriptfuncptrs is associated to
element 1 of the handlers that the method assigned and associated to
element 1 of the current states
Array1 Array2 Array3
ScriptfuncPtr(1) Handler(1) State(1)
ScriptfuncPtr(2) Handler(2) State(2)
ScriptfuncPtr(...) Handler(...) State(...)
' simply:
<associate>ScriptfuncPtr(Index) <==> Handler(Index) <==> State(Index)</associate>
The first Array/String would hold the passed thinBasic-scriptFunction-Pointers and the second Array/String would hold the Handlers that the method assigned to keep track. The validity of a handler proves that the AddressOf-declaration was done correctly and successful if there is a valid scriptfuncptr with the same index.
the third array is actually not containing any pointers but flags that will keep the method informed if there is a call to expect / i.e.
the scriptfunction is an active recepient for any callbacks that might come in.
since any parameters are passed from thinBasic-script to the API are byval now there is nothing to store nor transform when my script calls the api/function, they can be passed right through.
When i Declare...AddressOf, i also name the function that will be used to receive any parameters.
Is the function/header prefixed as CallBack?
If yes,
it will receive the parameters as CBHNDL and all the other generic callback-pseudo-variables and the function header must not have any parameters between the parenthesis and return a value As Long, mandatory it is a function then. No Sub allowed.
Anything does not follow the rules => RTE !!!
If no,
all the parameters must be byval and it can return only a message to Method.X that says:
0: message not processed, wrong recipient
+1: message received and processed - if bit not set Method.X should call the next scriptfunction on its list
+2: reply "TRUE" to sender,( implements 1) means to return a value of -1 to the calling function from API
+4: continue passing messages here (implements 1) means to keep this scriptfunctions state as still active
====
result = 1 or result = 3 will set the state of this scriptfunction to 'no more active':method.x will not call this anymore
result = 0 or result = 5 or result = 7 will not change the state
to the
parameters of myScriptFunction
ByRef can not be here as usual. The user will have to split whatever parameters himself if more than 1 Byte to expect
it can be "Byval b() as Byte" or "byval bin$ as Rawtext"
thincore"s Method.X would just pass the bytes that it received as raw binary string
or allocate a heap and pass the pointer
its up to the user to split and parse it to parameters that can be passed to other script-functions.
He knows what data to expect and can be happy to receive the data at all since it was impossible before. And still is - regarding dynamic data to a universal format without to have billions of functions to achieve it.