ActiveScript for AutoHotkey v1.1
Provides an interface to Active Scripting languages like VBScript and JScript, without relying on Microsoft's ScriptControl, which is not available to 64-bit programs.
License: Use, modify and redistribute without limitation, but at your own risk.
More information, examples and downloads
ComObject := ComDispatch0(Object) - not needed in AutoHotkey v1.1.17+
Wraps an AutoHotkey object in an IDispatch interface, allowing COM clients such as the VBScript or JScript engines to call methods and set and get values. See Example_Dispatch0.ahk for examples. ComDispatch0 is based on fincs' ComDispatch, modified by Coco.
ActiveScript - Host VBScript and JScript in-process
Re: ActiveScript - Host VBScript and JScript in-process
Very cool, thanks lexikos. How much tweaking does it need to make it work for v2? I noticed a couple of parsing loops and a .Remove() somewhere. Other than that most parts are v2 compatible... unless I missed something.
Re: ActiveScript - Host VBScript and JScript in-process
I would have to review the code closely and test to answer that. I didn't bother since there are some things it uses that I'm planning on changing soon, like meta-functions.
If you're interested in this topic, you should read Support for passing objects to COM APIs - Wish List.
If you're interested in this topic, you should read Support for passing objects to COM APIs - Wish List.
Re: ActiveScript - Host VBScript and JScript in-process
This is very nice & much more elegant than my previous ScriptControl marshaling workaround. I'm also glad to see that my old ComDispatch lib is deemed useful by others and is updated/enhanced.
Huh? I thought the new property syntax was introduced in order to minimize the need to use meta-functions and such avoid having to change how they work?lexikos wrote:there are some things it uses that I'm planning on changing soon, like meta-functions.
fincs
Windows 11 Pro (Version 22H2) | AMD Ryzen 7 3700X with 32 GB of RAM | AutoHotkey v2.0.0 + v1.1.36.02
Get SciTE4AutoHotkey v3.1.0 -[My project list]
Windows 11 Pro (Version 22H2) | AMD Ryzen 7 3700X with 32 GB of RAM | AutoHotkey v2.0.0 + v1.1.36.02
Get SciTE4AutoHotkey v3.1.0 -
Re: ActiveScript - Host VBScript and JScript in-process
Very Nice!
Just one thing tho... Is it just me or is this file not supposed to be on github? ??
https://github.com/Lexikos/ActiveScript ... properties
![Very Happy :D](./images/smilies/icon_e_biggrin.gif)
https://github.com/Lexikos/ActiveScript ... properties
![Image](https://joedf.github.io/assets/img/user/ahkneedhelp.gif)
![Image](https://joedf.github.io/assets/img/user/ahkfoundation.gif)
![Image](https://joedf.github.io/assets/img/user/mitlicense.gif)
![Image](https://joedf.github.io/assets/img/user/+greenpeace.gif)
![Image](https://joedf.github.io/assets/img/user/githubahkscript.gif)
Windows 10 x64 Professional, Intel i5-8500, NVIDIA GTX 1060 6GB, 2x16GB Kingston FURY Beast - DDR4 3200 MHz | [About Me] | [About the AHK Foundation] | [Courses on AutoHotkey]
[ASPDM - StdLib Distribution] | [Qonsole - Quake-like console emulator] | [LibCon - Autohotkey Console Library]
Re: ActiveScript - Host VBScript and JScript in-process
fincs: The idea was to minimize the cost in terms of any inconvenience that might be caused by making the meta-functions less quirky. It's still true that (as I said) "the rules for how meta-functions work are complicated and error-prone," and that needs to be changed even if meta-functions aren't needed as much. By eliminating properties from the things meta-functions are commonly used for, there's less need to have them tied into the inheritance chain as they are now.
joedf: It's just you.
joedf: It's just you.
Re: ActiveScript - Host VBScript and JScript in-process
:0 ok
![Image](https://joedf.github.io/assets/img/user/ahkneedhelp.gif)
![Image](https://joedf.github.io/assets/img/user/ahkfoundation.gif)
![Image](https://joedf.github.io/assets/img/user/mitlicense.gif)
![Image](https://joedf.github.io/assets/img/user/+greenpeace.gif)
![Image](https://joedf.github.io/assets/img/user/githubahkscript.gif)
Windows 10 x64 Professional, Intel i5-8500, NVIDIA GTX 1060 6GB, 2x16GB Kingston FURY Beast - DDR4 3200 MHz | [About Me] | [About the AHK Foundation] | [Courses on AutoHotkey]
[ASPDM - StdLib Distribution] | [Qonsole - Quake-like console emulator] | [LibCon - Autohotkey Console Library]
Re: ActiveScript - Host VBScript and JScript in-process
Edit: Ok, I got it, the same interface pointer is used for subsequent instance(s). So, say for SetWBClientSite(), can I recycle the same IOleClientSite, IServiceProvider and IInternetSecurityManager interface pointers(previously used) for subsequent WebBrowser control(s).
@lexikos: I noticed that when you called .vftable() you used the class object itself (ActiveScriptSite), hence, the buffer is stored in the class object's member(s). Is this OK, since it gets overridden every time a new instance of ActiveScriptSite is created? Or it's OK since IActiveScript::SetScriptSite is only needed one-time? I'm currently writing a mod for your SetWBClientSite() routine and would like to base it on your ActiveScriptSite class, e.g.: the way you stored a weak reference to the instance of ActiveScript etc..
PS: It would be nice if you(or anyone knowledgeable) can do a tutorial on how to implement COM interfaces. I did learn/understand a few things, (while updating ComDispatch, etc.)![Wink ;)](./images/smilies/icon_e_wink.gif)
PS: It would be nice if you(or anyone knowledgeable) can do a tutorial on how to implement COM interfaces. I did learn/understand a few things, (while updating ComDispatch, etc.)
![Wink ;)](./images/smilies/icon_e_wink.gif)
Re: ActiveScript - Host VBScript and JScript in-process
No.Coco wrote:Edit: Ok, I got it, the same interface pointer is used for subsequent instance(s).
It's not an interface pointer, but a virtual function table. Normally in compiled languages, there is one table per class (per super-class/interface if it inherits multiple), and this is constructed at compile time, since the functions don't change at run-time. An interface pointer is a pointer to a pointer to the virtual function table. This is why you can compare NumGet(&obj) between objects to identify whether they're the same type of object. The interface pointer either points at the beginning of the object, or math can be used to locate the beginning of the object (the compiler handles this). For example, the second field of my ActiveScriptSite is a pointer to the IActiveScriptSiteWindow vtable, hence this line:
Code: Select all
this -= A_PtrSize ; Cast to IActiveScriptSite
It doesn't get overridden. Each vftable (_vft and _vft_w) is constructed only once; the first two lines of _vftable() ensure this.Is this OK, since it gets overridden every time a new instance of ActiveScriptSite is created?
Re: ActiveScript - Host VBScript and JScript in-process
I see that's why: func_addr := NumGet(NumGet(interface_ptr + 0), zero_based_index * A_PtrSize)lexikos wrote:An interface pointer is a pointer to a pointer to the virtual function table.
Edit: Ok, after carefully reviewing it, I think I got it. So given the code below, is it OK to implement AddRef and Release? You do have a comment about avoiding circular references, does it apply in this case?:
Code: Select all
class WBClientSite
{
__New(self)
{
ObjSetCapacity(this, "__Site", 3*A_PtrSize)
NumPut(&self
, NumPut(this.base._vftable("_vft_IInternetSecurityManager", "11348733")
, NumPut(this.base._vftable("_vft_IServiceProvider", "3")
, NumPut(this.base._vftable("_vft_IOleClientSite", "031010")
, this.__Ptr := ObjGetAddress(this, "__Site") ) ) ) )
}
_vftable(name, args)
{
if ( ptr := ObjGetAddress(this, name) )
return ptr
static IUnknown
if !IUnknown
{
IUnknown := {
(Join, Q C
"QueryInterface": RegisterCallback(this._QueryInterface, "Fast")
"AddRef": RegisterCallback(this._AddRef, "Fast")
"Release": RegisterCallback(this._Release, "Fast")
)}
}
sizeof_VFTABLE := (3 + StrLen(args)) * A_PtrSize
ObjSetCapacity(this, name, sizeof_VFTABLE)
ptr := ObjGetAddress(this, name)
NumPut(IUnknown.QueryInterface, ptr + 0*A_PtrSize)
NumPut(IUnknown.AddRef, ptr + 1*A_PtrSize)
NumPut(IUnknown.Release, ptr + 2*A_PtrSize)
for i, argc in StrSplit(args)
{
vfunc := RegisterCallback(this[SubStr(name, 6)], "Fast", argc+1, i)
NumPut(vfunc, ptr + (3+i-1)*A_PtrSize)
}
return ptr
}
_QueryInterface(riid, ppvObject)
{
static IID_IUnknown := "{00000000-0000-0000-C000-000000000046}"
, IID_IOleClientSite := "{00000118-0000-0000-C000-000000000046}"
, IID_IServiceProvider := "{6d5140c1-7436-11ce-8034-00aa006009fa}"
iid := WBClientSite._GUID2String(riid)
if (iid = IID_IOleClientSite || iid = IID_IUnknown)
{
NumPut(this, ppvObject+0) ;// IOleClientSite
return 0 ;// S_OK
}
if (iid = IID_IServiceProvider)
{
NumPut(this + A_PtrSize, ppvObject+0) ;// IServiceProvider
return 0 ;// S_OK
}
NumPut(0, ppvObject+0)
return 0x80004002 ;// E_NOINTERFACE
}
_AddRef()
{
return 1
}
_Release()
{
return 1
}
IOleClientSite(p1:="", p2:="", p3:="")
{
if (A_EventInfo == 3) ;// GetContainer
{
NumPut(0, p1+0) ;// *ppContainer := NULL
return 0x80004002 ;// E_NOINTERFACE
}
return 0x80004001 ;// E_NOTIMPL
}
IServiceProvider(guidService, riid, ppv) ;// QueryService
{
static IID_IUnknown := "{00000000-0000-0000-C000-000000000046}"
, IID_IInternetSecurityManager := "{79eac9ee-baf9-11ce-8c82-00aa004ba90b}"
if (WBClientSite._GUID2String(guidService) = IID_IInternetSecurityManager)
{
iid := WBClientSite._GUID2String(riid)
if (iid = IID_IInternetSecurityManager || iid = IID_IUnknown)
{
NumPut(this + A_PtrSize, ppv+0) ;// IInternetSecurityManager
return 0 ;// S_OK
}
NumPut(0, ppv+0)
return 0x80004002 ;// E_NOINTERFACE
}
NumPut(0, ppv+0)
return 0x80004001 ;// E_NOTIMPL
}
IInternetSecurityManager(p1:="", p2:="", p3:="", p4:="", p5:="", p6:="", p7:="", p8:="")
{
if (A_EventInfo == 5) ;// ProcessUrlAction
{
if (p2 == 0x1400) ;// dwAction = URLACTION_SCRIPT_RUN
{
NumPut(0x00, p3+0) ;// *pPolicy := URLPOLICY_ALLOW = 0x00
return 0 ;// S_OK
}
}
return 0x800C0011 ;// INET_E_DEFAULT_ACTION
}
_GUID2String(pGUID)
{
VarSetCapacity(string, 38*2)
DllCall("ole32\StringFromGUID2", "ptr", pGUID, "str", string, "int", 39)
return string
}
}
Re: ActiveScript - Host VBScript and JScript in-process
The comment was:
The references are: your ahk script -> ActiveScript -> IActiveScript -> site -> ActiveScript -> IActiveScript ... If the references were counted, there would be a circular reference.
If your interface might be called after the ahk script releases all references to its WBClientSite instance, you need to implement reference counting to keep the interface alive until the WebBrowser no longer needs it. Otherwise, you don't need to implement reference counting.
Code: Select all
; AddRef and Release don't do anything because we want to avoid circular references.
; The site and IActiveScript are both released when the AHK script releases its last
; reference to the ActiveScript object.
If your interface might be called after the ahk script releases all references to its WBClientSite instance, you need to implement reference counting to keep the interface alive until the WebBrowser no longer needs it. Otherwise, you don't need to implement reference counting.
I guess you figured out this is the interface pointer, not the object.Another thing
Re: ActiveScript - Host VBScript and JScript in-process
lexikos,
I see your vbscript example... is there a way to pass parameters into the vbscript. You show how to access variables to get them out but what if the vbscript function accepts and requires parameters? I tried:
vb.variable := "blah" before executing vb.exec(code) but that didn't work
Relayer
I see your vbscript example... is there a way to pass parameters into the vbscript. You show how to access variables to get them out but what if the vbscript function accepts and requires parameters? I tried:
vb.variable := "blah" before executing vb.exec(code) but that didn't work
Relayer
Re: ActiveScript - Host VBScript and JScript in-process
That's not passing a parameter...Relayer wrote:vb.variable := "blah" before executing vb.exec(code) but that didn't work
The example uses vb.GetAnswer() to call the GetAnswer function which was defined via Exec. If you want to pass a parameter, just do it.
Code: Select all
vb.GetAnswer(42)
However, in AutoHotkey v1.1.18+ this requirement doesn't apply to JScript.Example.ahk wrote:vb.Answer := 42 ; Set a previously-declared global variable.
Re: ActiveScript - Host VBScript and JScript in-process
I've updated the script:
- JScript 5.8 (IE8) features such as JSON.parse() and JSON.stringify() are enabled automatically when using JScript.
- AddObject() now supports normal AutoHotkey objects on AutoHotkey v1.1.17+.
Re: ActiveScript - Host VBScript and JScript in-process
I've added a method for enabling the use of WinRT (the framework used by Windows 10 apps) with JsRT.Edge, and an example for displaying a toast notification.
![Image](https://autohotkey.com/boards/download/file.php?id=1226)
There's a list of WinRT APIs which can be used from desktop apps:
https://msdn.microsoft.com/en-au/library/dn554295
There's a list of WinRT APIs which can be used from desktop apps:
https://msdn.microsoft.com/en-au/library/dn554295
Re: ActiveScript - Host VBScript and JScript in-process
Looks great. Thank you lexikos.
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
Re: ActiveScript - Host VBScript and JScript in-process
Ohhh fancy! ![Very Happy :D](./images/smilies/icon_e_biggrin.gif)
![Very Happy :D](./images/smilies/icon_e_biggrin.gif)
![Image](https://joedf.github.io/assets/img/user/ahkneedhelp.gif)
![Image](https://joedf.github.io/assets/img/user/ahkfoundation.gif)
![Image](https://joedf.github.io/assets/img/user/mitlicense.gif)
![Image](https://joedf.github.io/assets/img/user/+greenpeace.gif)
![Image](https://joedf.github.io/assets/img/user/githubahkscript.gif)
Windows 10 x64 Professional, Intel i5-8500, NVIDIA GTX 1060 6GB, 2x16GB Kingston FURY Beast - DDR4 3200 MHz | [About Me] | [About the AHK Foundation] | [Courses on AutoHotkey]
[ASPDM - StdLib Distribution] | [Qonsole - Quake-like console emulator] | [LibCon - Autohotkey Console Library]
Re: ActiveScript - Host VBScript and JScript in-process
@lexikos, not sure if you've seen it already, I submitted a PR - mainly to provide v2.0-a compatibility.
Re: ActiveScript - Host VBScript and JScript in-process
Merged, thanks.
Re: ActiveScript - Host VBScript and JScript in-process
Hello!
I want to know why this is not right!?
I want to know why this is not right!?
Code: Select all
#NoEnv
#Include ActiveScript.ahk
#Include JsRT.ahk
Gui, +Resize MinSize671x300
Gui, Show, W671 H300, JsRT
Gui, Color, Gray
Gui, Add, ActiveX, X0 Y0 W671 H300 VIE, "Shell.Explorer"
; Toast notifications from desktop apps can only use local image files.
if !FileExist("sample.png")
URLDownloadToFile https://autohotkey.com/boards/styles/simplicity/theme/images/announce_unread.png
, % A_ScriptDir "\sample.png"
; The templates are described here:
; http://msdn.com/library/windows/apps/windows.ui.notifications.toasttemplatetype.aspx
toast_template := "toastImageAndText02"
; Image path/URL must be absolute, not relative.
toast_image := A_ScriptDir "\sample.png"
; Text is an array because some templates have multiple text elements.
toast_text := ["Hello, world!", "This is the sub-text."]
; Only the Edge version of JsRT supports WinRT.
js := new JsRT.Edge
js.AddObject("yesno", Func("yesno"))
yesno(s) {
MsgBox 4,, %s%
IfMsgBox Yes
return true
}
; Enable use of WinRT. "Windows.UI" or "Windows" would also work.
js.ProjectWinRTNamespace("Windows.UI.Notifications")
code =
(
function toast(template, image, text, app) {
// Alias for convenience.
var N = Windows.UI.Notifications;
// Get the template XML as an XmlDocument.
var toastXml = N.ToastNotificationManager
.getTemplateContent(N.ToastTemplateType[template]);
// Insert our content.
var i = 0;
for (let el of toastXml.getElementsByTagName("text")) {
if (typeof text == 'string') {
el.innerText = text;
break;
}
el.innerText = text[++i];
}
toastXml.getElementsByTagName("image")[0]
.setAttribute("src", image);
// Show the notification.
var toastNotifier = N.ToastNotificationManager
.createToastNotifier(app || "AutoHotkey");
var notification = new N.ToastNotification(toastXml);
toastNotifier.show(notification);
// Unlike TrayTip, this API lets us hide the notification:
if (yesno("Hide the notification?")) {
toastNotifier.hide(notification);
}
}
)
try {
; Define the toast function.
js.Exec(code)
; Show a toast notification.
js.toast(toast_template, toast_image, toast_text)
}
catch ex {
try errmsg := ex.stack
if !errmsg
errmsg := "Error: " ex.message
MsgBox % errmsg
}
; Note: If the notification wasn't hidden, it will remain after we exit.
; ExitApp
Last edited by F4Jonatas on 29 Apr 2017, 12:33, edited 2 times in total.
Return to “Scripts and Functions (v1)”
Who is online
Users browsing this forum: Google [Bot] and 167 guests