The WebView2 control allows you to add host objects to the script. The information on what types it allows is described here - AddHostObjectToScript.
It states that it supports IDispatch and I thought that AHK's objects supported that since we can pass them to IE. I seem to be able to pass an object by first calling ObjPtrAddRef on the object and then passing the returned address. I can see that the browser can see the object however it just shows that its an empty array (which its not). My test object was just { stuff: "things" }. Do I maybe need to manually implement Invoke for the object?
Webview2 iDispatch
Re: Webview2 iDispatch
Guessing I shouldn't implement it directly...
IDispatch wrote:Generally, you should not implement Invoke directly
Re: Webview2 iDispatch
I'm able to get this to work by passing vref to the browser.
Setting vref to an AHK Array sort of works however I can't figure out how to properly access the values. window.chrome.webview.hostObjects.sync.AHK[0] just seems to return the JavaScript proxy object.
Setting vref to a SAFEARRAY works nicely. Setting vref to an AHK object sort of works too. You can directly access the members however trying to call the object itself in the JavaScript console similarly returns a proxy object promise. Passing a SAFEARRAY directly didn't seem to work either. It seems the VT_BYREF | VT_VARIANT is required or at least circumvents some issue that I'm unfamiliar with.
Passing values from JS to AHK via functions works pretty well. AHK receives a ComObjArray if JS passes an array. If JS passes an object then AHK receives a ComObj but I'm unable to figure out how to reference the properties as dot notation isn't working. Let's say I pass the object {hay: "horses"} from JS to AHK. Trying to access the "hay" property returns this in the JavaScript console: Error: (0x80070005) Access is denied. Value is not modifiable
Code: Select all
vbuf := BufferAlloc(24, 0)
vref := ComObject(0x400C, vbuf.ptr) ; 0x400C is a combination of VT_BYREF and VT_VARIANT.
vref[] := (*) => MsgBox("From AHK", "Hello!", "YN")
Setting vref to a SAFEARRAY works nicely. Setting vref to an AHK object sort of works too. You can directly access the members however trying to call the object itself in the JavaScript console similarly returns a proxy object promise. Passing a SAFEARRAY directly didn't seem to work either. It seems the VT_BYREF | VT_VARIANT is required or at least circumvents some issue that I'm unfamiliar with.
Passing values from JS to AHK via functions works pretty well. AHK receives a ComObjArray if JS passes an array. If JS passes an object then AHK receives a ComObj but I'm unable to figure out how to reference the properties as dot notation isn't working. Let's say I pass the object {hay: "horses"} from JS to AHK. Trying to access the "hay" property returns this in the JavaScript console: Error: (0x80070005) Access is denied. Value is not modifiable
Re: Webview2 iDispatch
I feel like the issue with JS objects is somehow related to this note:
Microsoft wrote:Remote JavaScript objects like callback functions are represented as an VT_DISPATCH VARIANT with the object implementing IDispatch. The JavaScript callback method may be invoked using DISPID_VALUE for the DISPID.
Re: Webview2 iDispatch
All AutoHotkey objects implement IDispatch. There is no need to implement it unless you want to change the behaviour.
In JavaScript, there is no distinction between array elements and properties. The JScript engine (which isn't in use here) invokes x['y'] the same way as x.y: it first calls IDispatch::GetIDsOfNames, and then IDispatch::Invoke. AutoHotkey objects assign an ID to each unique name, then map the ID back to a name when invoked. The flags for Invoke specify to invoke a property; there is no flag for array indexing.
Functions are invoked via IDispatch with DISPID_VALUE instead of mapping a name to an ID. DISPID_VALUE maps to either Call or __Item depending on the type of invocation. Since COM supports parameterised properties, clients such as JScript, VBScript and C# will specify the flags for "either property or method". In such cases, AutoHotkey tries one and then the other. So in other words, the JScript call v = arr(n) can result in a call to arr.__Item[n]. However, the statement arr(n) (where the return value is discarded) is only ever a method call.
That's probably your answer for reading arrays: use arr(n), not arr[n].
For writing to arrays, I was surprised to find that arr(n) = value is accepted by JScript and works. I doubt it would work in any other JavaScript engine.
%obj%() translates to a method call with DISPID_VALUE if obj is a COM object (IDispatch).
If you want to call the object itself, you just call the object itself: .AHK(). In JScript this would result in a call to IDispatch::Invoke with DISPID_VALUE, which would translate to Call (I think the flags would only specify a method call in this context).
In JavaScript, there is no distinction between array elements and properties. The JScript engine (which isn't in use here) invokes x['y'] the same way as x.y: it first calls IDispatch::GetIDsOfNames, and then IDispatch::Invoke. AutoHotkey objects assign an ID to each unique name, then map the ID back to a name when invoked. The flags for Invoke specify to invoke a property; there is no flag for array indexing.
Functions are invoked via IDispatch with DISPID_VALUE instead of mapping a name to an ID. DISPID_VALUE maps to either Call or __Item depending on the type of invocation. Since COM supports parameterised properties, clients such as JScript, VBScript and C# will specify the flags for "either property or method". In such cases, AutoHotkey tries one and then the other. So in other words, the JScript call v = arr(n) can result in a call to arr.__Item[n]. However, the statement arr(n) (where the return value is discarded) is only ever a method call.
That's probably your answer for reading arrays: use arr(n), not arr[n].
For writing to arrays, I was surprised to find that arr(n) = value is accepted by JScript and works. I doubt it would work in any other JavaScript engine.
Which objects are like callback functions? It's too vague. It tells you how to invoke a callback, but nothing about any other type of object or operation.I feel like the issue with JS objects is somehow related to this note:
%obj%() translates to a method call with DISPID_VALUE if obj is a COM object (IDispatch).
How did you try to call the object itself? I see no such attempt in your screenshot; you only evaluate the object (.AHK), retrieve its property (.stuff), and invoke two methods (.invoke() and .things()). I'd guess that .invoke() was supposed to do something special, since it seems you've defined things but not invoke.You can directly access the members however trying to call the object itself in the JavaScript console similarly returns a proxy object promise.
If you want to call the object itself, you just call the object itself: .AHK(). In JScript this would result in a call to IDispatch::Invoke with DISPID_VALUE, which would translate to Call (I think the flags would only specify a method call in this context).
Re: Webview2 iDispatch
I'm trying to find the time to gather my thoughts and questions a bit better. And to provide a working example for reference. Please excuse my delay in a formulated response and thank you for your reply.
Re: Webview2 iDispatch
I've looked at this again. Not sure I am much further along but am finding naunces that I've overcome a bit.
Let's say I expose a plain AHK array via a ComObject of type VT_BYREF|VT_VARIANT. I can do some interesting things with it from the browser side. I can call standard AHK Array methods but I must pass the Array itself as the first argument.
I've attached everything. You just need to have the current version of the Webview2 runtime installed.
Here is the JS for visibility (but it is also included in the zip). The interesting thing is that AHK native Array methods must be called by passing the object itself as the first argument. So it would be arr.has(arr, 1) or arr.push(arr, "test"). I was never able to get element indexing to work. I tried manually calling many combinations of arr[1] or arr.__index[0] or arr.getHostProperty("__index")[0]. Any time I try with __index I get an error in the console stating there are two few parameters being passed. Oddly enough, I implemented a simple get() method and that works just fine from JS without needing to pass the object itself as the first parameter. Its both fun and frustrating haha.
Let's say I expose a plain AHK array via a ComObject of type VT_BYREF|VT_VARIANT. I can do some interesting things with it from the browser side. I can call standard AHK Array methods but I must pass the Array itself as the first argument.
I've attached everything. You just need to have the current version of the Webview2 runtime installed.
Here is the JS for visibility (but it is also included in the zip). The interesting thing is that AHK native Array methods must be called by passing the object itself as the first argument. So it would be arr.has(arr, 1) or arr.push(arr, "test"). I was never able to get element indexing to work. I tried manually calling many combinations of arr[1] or arr.__index[0] or arr.getHostProperty("__index")[0]. Any time I try with __index I get an error in the console stating there are two few parameters being passed. Oddly enough, I implemented a simple get() method and that works just fine from JS without needing to pass the object itself as the first parameter. Its both fun and frustrating haha.
Code: Select all
const container = document.querySelector("#curr-time");
setInterval(() => {
container.textContent = (new Date()).toLocaleString();
}, 1000);
(async function() {
const arr = await window.chrome.webview.hostObjects.AHK;
const len = await arr.getHostProperty("length"); // otherwise we get the Proxy object's length
const first = await arr.push(arr, "of");
// for (let item in arr.applyHostFunction("__enum")) {
for (let i = 0; i < len; i++) {
console.log(await arr.get(i));
}
console.log(first);
})();
- Attachments
-
- webview2.zip
- (74.68 KiB) Downloaded 83 times
Re: Webview2 iDispatch
It would have been helpful to mention which alpha you are using, since it isn't the latest one. It must be either v2.0-a128 or v2.0-a129.
You can use arr.Length instead.
JavaScript fundamentally does not support indexed or parameterized properties. arr[1] in JavaScript is just querying the property 1 of arr. arr.__item[0] is querying arr.__item and then the 0 property of the result, like (arr.__item).0 in AutoHotkey. You can't do it that way in AutoHotkey either.
getHostProperty shouldn't be bound by the limitations of the JavaScript language since it's obviously intended for invoking external objects, but if you query it in the console, you'll get something like:
The Scripting.Dictionary COM object has a parameterized property named Item. As far as I can tell, it is not possible to call this from WebView2 either. However, the methods of this object work, like arr.get and not like arr.push.
With v2.0-a130 or later, arr.get(1) will not work, because arr.get will succeed; i.e. it will return a Func.
It would be interesting to see whether WebView2 uses the IDispatchEx interface if present. Unlike IDispatch, IDispatchEx allows specifying this in a call.
Let's not confuse matters. The ComObject of type VT_BYREF|VT_VARIANT is only used to store the array's IDispatch pointer in the VARIANT. You pass vref to a DllCall "Ptr" parameter, not a COM method - "ptr", vref is equivalent to "ptr", vref.ptr, which is just the address of your VARIANT buffer. You could just as well do this:Let's say I expose a plain AHK array via a ComObject of type VT_BYREF|VT_VARIANT.
Code: Select all
arr := ["this", "is", "a", "test"]
NumPut("int64", 9, "ptr", ObjPtr(arr), arrbuf := BufferAlloc(24))
wv.AddHostObjectToScript("arr", arrbuf)
I don't think there's any meaning in awaiting the proxy object. You just get another proxy object, apparently representing the same target object.const arr = await window.chrome.webview.hostObjects.AHK;
Why does a proxy object have a length property?const len = await arr.getHostProperty("length");
You can use arr.Length instead.
I assume you tried __item rather than __index.I was never able to get element indexing to work. I tried manually calling many combinations of arr[1] or arr.__index[0] or arr.getHostProperty("__index")[0].
JavaScript fundamentally does not support indexed or parameterized properties. arr[1] in JavaScript is just querying the property 1 of arr. arr.__item[0] is querying arr.__item and then the 0 property of the result, like (arr.__item).0 in AutoHotkey. You can't do it that way in AutoHotkey either.
getHostProperty shouldn't be bound by the limitations of the JavaScript language since it's obviously intended for invoking external objects, but if you query it in the console, you'll get something like:
Clearly it does not support parameters.getHostProperty(propertyName) { return (new AsyncRemoteProxy( this._options, true, this._options.remoteMessenger.postRequestMessage( this._remoteObjectId, propertyName, "get", []).then(message => { t…
The Scripting.Dictionary COM object has a parameterized property named Item. As far as I can tell, it is not possible to call this from WebView2 either. However, the methods of this object work, like arr.get and not like arr.push.
That was interesting, so I fired up the debugger.The interesting thing is that AHK native Array methods must be called by passing the object itself as the first argument.
- await arr.push(1) calls arr->IDispatch::Invoke with the DISPATCH_PROPERTYGET flag and no parameters to retrieve arr.push, then calls (Array.Prototype.Push)->IDispatch::Invoke with the DISPATCH_METHOD flag and one parameter, 1. In other words, it is equivalent to the AutoHotkey v2 expression (arr.push)(1).
- await arr.get(1) calls arr->IDispatch::Invoke with the DISPATCH_PROPERTYGET flag and no parameters to retrieve arr.get, but this fails. Next, arr->IDispatch::Invoke is called again, but with the DISPATCH_METHOD flag and one parameter, 1.
With v2.0-a130 or later, arr.get(1) will not work, because arr.get will succeed; i.e. it will return a Func.
It would be interesting to see whether WebView2 uses the IDispatchEx interface if present. Unlike IDispatch, IDispatchEx allows specifying this in a call.
Re: Webview2 iDispatch
Apologies about the alpha version discrepancy. Doesn't help I started this post a while ago and the version's changed a lot since then. I see what you're saying on the latest (a131) version.
Not sure! But the key was your following comment.Why does a proxy object have a length property?
I had to use Length and not length. Since JS is case-sensitive, the lowercase version was retrieving that property locally, hence the use of getHostProperty. Using the capitalized version worksYou can use arr.Length instead.
You're correct. I confused the names there.I assume you tried __item rather than __index.
Right, I think I understand that. So is there no way to get the elements of the array without creating a wrapper method on the AHK-side to get() the item? Certainly seems like this makes it difficult to enumerate the array too with any for-loop structure in JS aside from a traditional for-loop and using the loop iteration to get() the item in question.JavaScript fundamentally does not support indexed or parameterized properties.
I have posed the question on the Webview2 Feedback repository and will report back what I find out.It would be interesting to see whether WebView2 uses the IDispatchEx interface if present. Unlike IDispatch, IDispatchEx allows specifying this in a call.
Re: Webview2 iDispatch
They only support IDispatch currently - https://github.com/MicrosoftEdge/WebView2Feedback/issues/513#issuecomment-821313146
EDIT: Here's the feature request https://github.com/MicrosoftEdge/WebView2Feedback/issues/1195
EDIT: Here's the feature request https://github.com/MicrosoftEdge/WebView2Feedback/issues/1195
Re: Webview2 iDispatch
IDispatchEx would provide a workaround if implemented, but keep in mind that AutoHotkey objects currently don't implement it by default.
The inability to invoke parameterized properties, even explicitly with something like getHostProperty, seems like a gap in the API that would be simpler to fill than implementing IDispatchEx.
Of course, it would be easier for us if x.y(z) called IDispatch::Invoke with DISPATCH_METHOD by default, but there's probably some reason it doesn't. For instance, maybe the host engine (V8?) always evaluates x.y first, so translating x.y(z) to DISPATCH_METHOD means creating a temporary function object containing just x and "y", which would subsequently be called with the parameter z *. (I suppose this is how you would have to do it if you implemented a proxy in pure JavaScript or Lua; I considered this when I first implemented objects, and designed them to not require this.)
* Oh right, you could do that to work around the issue;
Note:
The inability to invoke parameterized properties, even explicitly with something like getHostProperty, seems like a gap in the API that would be simpler to fill than implementing IDispatchEx.
Of course, it would be easier for us if x.y(z) called IDispatch::Invoke with DISPATCH_METHOD by default, but there's probably some reason it doesn't. For instance, maybe the host engine (V8?) always evaluates x.y first, so translating x.y(z) to DISPATCH_METHOD means creating a temporary function object containing just x and "y", which would subsequently be called with the parameter z *. (I suppose this is how you would have to do it if you implemented a proxy in pure JavaScript or Lua; I considered this when I first implemented objects, and designed them to not require this.)
* Oh right, you could do that to work around the issue;
Code: Select all
Array.Prototype.DefineProp("Push", {Get: this => ObjBindMethod(this, "Push"), Call: Array.Prototype.Push})
- This is untested.
- It's better to do this only for your specific host objects, to avoid changing the behaviour of AutoHotkey code.
- The "Call: ..." part would be unnecessary for v2.0-a130+, since the method is already implemented as a Call accessor and not a value property.
Who is online
Users browsing this forum: vmech and 38 guests