DotNetBrowser v2

Using (or providing) Microsoft.NET Classes

DotNetBrowser v2

Postby PGilbert on Mon Mar 16, 2020 4:15 pm

Hello, the new way with version 2 of DotNetBrowser to execute javascript from .Net is like this:

string title = browser.MainFrame.ExecuteJavaScript<string>("document.title").Result;

The APL signature looks like this:
System.Threading.Tasks.Task`1[T] ExecuteJavaScript[T](System.String, Boolean)
System.Threading.Tasks.Task`1[System.Object] ExecuteJavaScript(System.String, Boolean)

The Visual Studio signature looks like this:
System.Threading.Tasks.Task<string> DotNetBrowser.Frames.IFrame.ExecuteJavaScript<string>(string javaScript, [bool userGesture=false])
Asynchronously executes JavaScript code using the current context and converts the execution result to the specified .NET type.

When I execute the following in APL:
browser.MainFrame.ExecuteJavaScript(⊂'document.title')
I am getting a LENGTH ERROR

Is there a way to make this work ?

Thanks in advance.
User avatar
PGilbert
 
Posts: 379
Joined: Sun Dec 13, 2009 8:46 pm
Location: Montréal, Québec, Canada

Re: DotNetBrowser v2

Postby StefanoLanzavecchia on Tue Mar 17, 2020 9:42 am

I don't have the thousand dollars to license the product (which looks interesting, to be honest), and don't have the patience to try and install the trial version. But, I think the solution to your problem may be in the C# signature:
System.Threading.Tasks.Task<string> DotNetBrowser.Frames.IFrame.ExecuteJavaScript<string>(string javaScript, [bool userGesture=false])

The second argument is optional. But that's only in C#, because it seems to be implemented using a feature (https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/named-and-optional-arguments) only available to the C# language at the moment. From APL, try and specify the second argument as well:
Code: Select all
      browser.MainFrame.ExecuteJavaScript 'document.title' 0


Let us know if it works!
User avatar
StefanoLanzavecchia
 
Posts: 79
Joined: Fri Oct 03, 2008 9:37 am

Re: DotNetBrowser v2

Postby PGilbert on Tue Mar 17, 2020 11:46 am

Hello Stefano, thanks for the suggestion. Unfortunately, it is not working. I am getting the following error:

browser.MainFrame.ExecuteJavaScript 'document.title' 0
EXCEPTION: Late bound operations cannot be performed on types or methods for which ContainsGenericParameters is true.
browser.MainFrame.ExecuteJavaScript'document.title' 0


Any more suggestions?
User avatar
PGilbert
 
Posts: 379
Joined: Sun Dec 13, 2009 8:46 pm
Location: Montréal, Québec, Canada

Re: DotNetBrowser v2

Postby Vince|Dyalog on Tue Mar 17, 2020 4:03 pm

Hi Pierre,

This signature reminds me of another question I have seen in our support archives. I will give John Daintree's answer in the hope that it can serve as an example of what to do.

DotNetBrowser.Frames.IFrame.ExecuteJavaScript<string>(string javaScript, [bool userGesture=false])

The question was to translate the following into APL:
spreadSheet.WorkbookPart.AddNewPart<WorksheetPart>();

At that time, John Daintree wrote this:
OK, so this is a bit tricky, because we don’t directly support Generics, which is what all the `1 and <type> and [T] stuff is all about. But we can get there.

ssnew2.WorkbookPart.AddNewPart<WorksheetPart>();
The <WorksheetPart> above indicates that we need to call a version of AddNewPart which returns a WorksheetPart object. In fact the display form of AddNewPart gives us a clue:

wkbpt.AddNewPart
T AddNewPart[T]()
T AddNewPart[T](System.String)
T AddNewPart[T](System.String, System.String)


To call spreadSheet.WorkbookPart.AddNewPart<WorksheetPart>(), what we need to do is define a function that calls the first of these overloads, AND we need to specify that T is WorksheetPart.

MakeGenericMethod to the rescue (https://docs.microsoft.com/en-us/dotnet ... ericmethod)

We want to call the Niladic overload above (the T AddNewPart[T]()), so we use GetMethod to get the Generic definition:

⎕←m1←wkbpt.GetType.GetMethod'AddNewPart' (0⍴⊂⎕null)
T AddNewPart[T]()

We then need to create a concrete version of the generic where T is WorksheetPart. First we need to find the Type of WorksheetPart. Fortunately this is defined in the same assembly as WorkbookPart, which we already have an instance of.

assem←wkbpt.GetType.Assembly
atype←assem.GetType⊂'DocumentFormat.OpenXml.Packaging.WorksheetPart'

Now we can create the concrete function:

⎕←m←m1.MakeGenericMethod (⊂,atype)
DocumentFormat.OpenXml.Packaging.WorksheetPart AddNewPart[WorksheetPart]()

We can then invoke the method m, on the appropriate WorkbookPart with an empty parameter list:

m.Invoke wkbpt ⍬
DocumentFormat.OpenXml.Packaging.WorksheetPart

So, the sequence is:

m1←wkbpt.GetType.GetMethod'AddNewPart' (0⍴⊂⎕null)
atype←wkbpt.GetType.Assembly.GetType⊂'DocumentFormat.OpenXml.Packaging.WorksheetPart'
m←m1.MakeGenericMethod (⊂,atype)
m.Invoke wkbpt ⍬

Regards,

Vince
Vince|Dyalog
 
Posts: 323
Joined: Wed Oct 01, 2008 9:39 am

Re: DotNetBrowser v2

Postby PGilbert on Tue Mar 17, 2020 6:53 pm

Hello Vince, thanks for the info. Here is what I have tried:

browser.MainFrame.ExecuteJavaScript
System.Threading.Tasks.Task`1[T] ExecuteJavaScript[T](System.String, Boolean)
System.Threading.Tasks.Task`1[System.Object] ExecuteJavaScript(System.String, Boolean)

typeArray←System.Array.CreateInstance(System.Type)(2)
typeArray[0]←System.Type.GetType⊂'System.String'
typeArray[1]←System.Type.GetType⊂'System.Boolean'

typeArray
System.Type[]

browser.MainFrame.GetType.GetMethod'ExecuteJavaScript' typeArray
EXCEPTION: Ambiguous match found.
browser.MainFrame.GetType.GetMethod'ExecuteJavaScript' typeArray


From what I can see the 2 APL signatures are the same so it is normal to get an 'Ambiguous match'.

Is there a way to help the interpreter distinguish between the 2 signatures?

Thanks in advance.
User avatar
PGilbert
 
Posts: 379
Joined: Sun Dec 13, 2009 8:46 pm
Location: Montréal, Québec, Canada

Re: DotNetBrowser v2

Postby PGilbert on Tue Mar 17, 2020 8:52 pm

Hello Vince, what I am looking really is using the second signature that has no 'generics':

System.Threading.Tasks.Task`1[T] ExecuteJavaScript[T](System.String, Boolean)
System.Threading.Tasks.Task`1[System.Object] ExecuteJavaScript(System.String, Boolean) ⍝ ← I want to use this one

The second signature has no generics. Is there a way to 'help' the interpreter to use a signature when there are 2 that are identical or ambiguous?

Thanks in advance.
User avatar
PGilbert
 
Posts: 379
Joined: Sun Dec 13, 2009 8:46 pm
Location: Montréal, Québec, Canada

Re: DotNetBrowser v2

Postby PGilbert on Fri Mar 20, 2020 12:13 am

Hello Vince, DotNetBrowser's company has suggested me to make a dll wrapper for the ambiguous calls which I did and it is working now.

Let me know if there is a way to resolve those calls in APL without resorting to write a dll wrapper.

Regards,

Pierre Gilbert
User avatar
PGilbert
 
Posts: 379
Joined: Sun Dec 13, 2009 8:46 pm
Location: Montréal, Québec, Canada

Re: DotNetBrowser v2

Postby gil on Fri Mar 20, 2020 8:39 pm

Pierre, I had a look at this as an exercise and found that you can ignore all the hassle normal C# developers go through and simply explore the methods available in a class by formatting them as text and then picking the one you want.

Instead of:
Code: Select all
typeArray←System.Array.CreateInstance(System.Type)(2)
typeArray[0]←System.Type.GetType⊂'System.String'
typeArray[1]←System.Type.GetType⊂'System.Boolean'
browser.MainFrame.GetType.GetMethod'ExecuteJavaScript' typeArray


You can do:
Code: Select all
methods_txt←⍕¨methods←browser.MainFrame.GetType.GetMethods ⍬
ind←1⍳⍨∨/'[System.Object] ExecuteJavaScript(System.String, Boolean)'⍷↑methods_txt
MethodInfo←ind⊃methods


Also, unfortunately you can't invoke the method with a 0 as the second argument for false as this doesn't seem to get automatically converted into a Boolean type. I found you have to explicitly cast it.

Here is a class that on instantiation prepares the engine, browser, method and arguments. In the ExecuteJS method, it simply replaces the first argument with the statement passed in and then invokes the method call and waits for the result.

Code: Select all
:Class DotNetBrowserAPL
:Using System
:Using ,.\DotNetBrowser.dll
:Using ,.\DotNetBrowser.Core.dll

    ∇ make;methods;methods_txt;ind
      :Access Public
      :Implements Constructor
      Engine←DotNetBrowser.Engine.EngineFactory.Create ⍬
      Browser←Engine.CreateBrowser
     
      methods_txt←⍕¨methods←Browser.MainFrame.GetType.GetMethods ⍬
      ind←1⍳⍨∨/'[System.Object] ExecuteJavaScript(System.String, Boolean)'⍷↑methods_txt
      MethodInfo←ind⊃methods
      Args←Array.CreateInstance Object 2
      (Args.SetValue⍠('CastToTypes'(Boolean Int32)))0 1
    ∇

    ∇ r←ExecuteJS statement;res
      :Access Public
      Args.SetValue (,statement) 0
      res←MethodInfo.Invoke Browser.MainFrame Args
      r←res.Result
    ∇

:EndClass


And then you use it like this:
      js←⎕NEW DotNetBrowserAPL
js.ExecuteJS'1+2+3'
6
gil
 
Posts: 67
Joined: Mon Feb 15, 2010 12:42 am

Re: DotNetBrowser v2

Postby PGilbert on Sat Mar 21, 2020 1:00 pm

Thanks Gil, it's working. You are a lifesaver.
User avatar
PGilbert
 
Posts: 379
Joined: Sun Dec 13, 2009 8:46 pm
Location: Montréal, Québec, Canada

Re: DotNetBrowser v2

Postby Vince|Dyalog on Mon Mar 23, 2020 9:16 am

Ah, that's great, Gil! Thanks.

I will take note of this example for others in the future.

Regards,

Vince
Vince|Dyalog
 
Posts: 323
Joined: Wed Oct 01, 2008 9:39 am


Return to Microsoft.NET

Who is online

Users browsing this forum: No registered users and 1 guest