How to create an C#.Net DLL to use in JavaScript - javascript

I have created a .Net DLL with few simple classes. I have registered the DLL using RegAsm and I got a message that types were registered successfully.
RegAsm Syntax Used :
C:\Windows\Microsoft.NET\Framework\v4.0.30319>RegAsm.exe "D:\Projects\TestDLL.Core.dll"
C# Code:
namespace MyTestDLL.Core
{
public class PacketInfo
{
// constructor
public PacketInfo()
{
}
public string HostName { get; set; }
// and so on ......
}
}
I have set the ComVisible property to true in AssemblyInfo.cs file of this DLL.
// [assembly: ComVisible(true)]
However when I create an object out of it in JavaScript and run the script in Command prompt , I'm getting either it is not an object or null.
JS Code :
var testObj = new ActiveXObject(MyTestDLL.Core.PacketInfo);
testObj.HostName = "Test";
Can anyone advise me how to resolve this ?

You need to add a ProgId attribute to the class, something like this:
[Guid("some guid that you will never change")]
[ProgId("MyLib.PacketInfo")] // this is arbitrary
public class PacketInfo
{
....
}
The guid is optional, but if you don't set it yourself, it will be something you won't control, so it's better to define it. And of course I did not add the ComVisible because you already defined it at assembly level (I personnally prefer to set it at class level).
Then, once registered, you shall be able to use it like this (use the progid as a string):
var testObj = new ActiveXObject('MyLib.PacketInfo');
testObj.HostName = "Test";

I was able to achieve that by adding the following line to My DLL just above the class,
[Guid("A32AB771-9967-4604-B193-57FAA25557D4"), ClassInterface(ClassInterfaceType.None)]
Earlier I wasn't having the ClassInterfaceType part in my code.
Also ensure that each of your classes are having unique GUID.
FYI : You can create GUID using Visual Studio Tools GUID Creator GUI.

Related

Jint: using CLR object 's properties in Javascript functions

I have been testing the jint library and hit a snag. Given this class in C#:
public class Foo
{
public string Name { get; } = "Bar";
}
And this code:
Engine engine = new Engine(x => x.AllowClr());
object rc = _engine
.SetValue("foo", new Foo())
.Execute("foo.Name.startsWith('B')")
.GetCompletionValue()
.ToObject();
I get the error: 'Jint.Runtime.JavaScriptException: 'Object has no method 'startsWith'''
This works, however:
"foo.Name == 'Bar'"
So can I get the former to work?
Support for extension methods is added here. But can't get it to work with the .NET string extension methods directly, it does work with an intermediate extensions class.
Update: The string methods like StartsWith aren't real extension methods indeed.
Looks like startsWith is already supported natively now. Replaced GetCompletionValue with the suggested Evaluate
// Works natively with Jint 3.0.0-beta-2032
Engine engine = new Engine();
bool result = engine
.SetValue("foo", new Foo())
.Evaluate("foo.Name.startsWith('B')")
.AsBoolean();
I've tried to add the string extension methods, but that doesn't seem to work. But using your own class for the extension methods and use it that way does work.
public static class CustomStringExtensions
{
public static bool StartsWith(this string value, string value2) =>
value.StartsWith(value2);
}
Engine engine = new Engine(options =>
{
options.AddExtensionMethods(typeof(CustomStringExtensions));
});
bool result = engine
.SetValue("foo", new Foo())
.Evaluate("foo.Name.StartsWith('B')")
.AsBoolean();
I've asked about the native extension methods support here, as I'm curious if and how it is supposed to work.

Send Object from Javascript to Kotlin using Webview

I have loaded a webpage using WebView component and added a JavascriptInterface. Please check the code below,
val webview = WebView(this)
setContentView(webview)
webview.settings.javaScriptEnabled = true
webview.loadUrl(HOME_PAGE_URL)
webview.addJavascriptInterface(JavascriptInterface(),”javascript_bridge”)
And when I call the invoke from Javascript using window.javascript_bridge.showToast(“Information Saved”);
private inner class JavascriptInterface
{
#android.webkit.JavascriptInterface
fun showToast(text: String?)
{
Log.d("WEBVIEW", text);
}
}
I am able to call the method from Javascript to Kotlin without any trouble.
But now I want to pass an Object from Javascript to Kotlin like below,
var info = {
message: “Information Saved”,
ID: 123456
}
And when I call the invoke from Javascript using window.javascript_bridge.showToast(info);
I tried to change to the data type to Any, but the value passed from Javascript is null
private inner class JavascriptInterface
{
#android.webkit.JavascriptInterface
fun showToast(text: Any?)
{
Log.d("WEBVIEW", text.toString());
}
}
As far as i know, the javaScript interface methods only accepts primitive types of data as parameters (as discussed on this question).
If you still want to achieve that, you may serialize the object (to JSON format, for instance) in the javaScript and then Deserialize it in Java.
Hope it helps :)

How WebBrowser control in .net handles ObjectForScripting

As far as I know we can call C# function from Javascript, that is loaded inside a WebBrowser control, following code shows how I usually do it.
Form1.cs
public partial class Form1 : Form{
private WebBrowser webBrowser1;
public ApplicationWindow(){
InitializeComponent();
WebBrowser webBrowser1 = new WebBrowser();
//some code follows
webBrowser1.ObjectForScripting = new ScriptManager();
this.webBrowser1.Url = new Uri("file:///d:/ui/application.html");
}
}
}
ScriptManager.cs
namespace WindowsFormsApplication10 {
[ComVisible(true)]
public class ScriptManager{
public string GetAllDomains(){
string result=null;
//does something;
return result;
}
}
}
application.html
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script>
$(document).ready(function () {
var result = window.external.GetAllDomains();
//it works but this is what puzzles me.
});
</script>
The questions that intrigues me are
why we need ComVisible to be true for class whose object we are going to use as objectForScripting?
How Javascript object window.external has the same methods as in objectForScripting?
How they handle cross language type conversion?
I wonder why no one answered for so long. The answer to all your questions is COM - Component Object Model.
Windows is providing ability (using COM) for classes and functions from one program (exe) to be accessible outside the exe.
So
1) why we need ComVisible to be true for class whose object we are going to use as objectForScripting?
-> This tells windows to make the class and its methods visible to the webbrowser.
2) How Javascript object window.external has the same methods as in objectForScripting?
-> The javascript is calling methods of the class made visible in above answer.
3) How they handle cross language type conversion?
-> COM handles the types internally so methods in one programming language can be called from another programming language.

How to build an API with QJSEngine?

I am starting with Qt and one of my projects is using QJSEngine to evaluate javascript and I want to provide an entire API to the script, with classes and global functions.
Right now my program provides only the ECMAScript default stuff (eval, encodeURI, parseInt, etc...), but I need to expose some custom classes to the code, like the browsers API (WebSocket class, Image class, document object). For example:
var obj = new CustomClass("", 0);
var ret = obj.customClassMethod("[...]!");
customFunction(ret);
I need to define the behavior of the classes in C++, it wouldn't help evaluate the classes definition and let the user code run.
In contrast to QScriptEngine, where you can add custom classes if they inherit from QObject by using the Q_SCRIPT_DECLARE_QMETAOBJECT macro, the QJSEngine does not directly provide this functionality.
You can still use the Qt Meta-object system to provide interfaces for Javascript, but you have to instantiate the object in C++ and add it to the Javascript context.
Then its slots, methods defined with Q_INVOKABLE, and properties defined with Q_PROPERTY are all accessible from within the Javascript runtime.
Now you can create a factory which creates instances of your custom class CustomClass for a given QJSEngine wrapped as Javascript objects:
class CustomClassFactory : public QObject
{
Q_OBJECT
public:
CustomClassFactory(QJSEngine* engine) : m_engine(engine) {}
Q_INVOKABLE QJSValue createInstance() {
// The engine takes ownership and destroys the object if no longer required.
return m_engine->newQObject(new CustomClass());
}
private:
QJSEngine* m_engine;
}
A factory instance needs to be constructed and added to the global object of the Javascript runtime:
QJSEngine engine;
QJSValue factoryObj = engine.newQObject(new CustomClassFactory());
engine.globalObject().setProperty("_customClassFactory", factoryObj);
Now we can construct objects in Javascript with:
var obj = _customClassFactory.createInstance()
As we've come this far, lets additionally inject a constructor for the custom class into the Javascript runtime:
QJSEngine engine;
// Again, the QJSEngine will take ownership of the created object.
QJSValue factoryObj = engine.newQObject(new CustomClassFactory());
engine.globalObject().setProperty("_customClassFactory", factoryObj);
engine.evaluate(
"function CustomClass() {"
" return _customClassFactory.createInstance()"
"}");
Et voilà, now you can construct C++ object in Javascript, like you would custom Javascript classes:
var obj = new CustomClass()
For the mentioned WebSocket API you could wrap QtWebSocket for that purpose – that was exactly what I required when I came up with the proposed approach.
Note that for the sake of simplicity I omitted parameters for the constructor, but they can simply be added as well.
PS: I would have added more links to the official documentation, but due to the lack of reputation I'm not allowed to.
In Qt 5.8 a new feature was added to QJSEngine: newQMetaObject
You simple add the static meta object e.g. &MyQObjectDerivedClass::staticMetaObject to the JSEngine using the above function.
You will then be able to new those objects in Javascript from within QML. I have found this a very neat solution.
As the documentation says you must mark you constructor Q_INVOKABLE or you won't be able to instantiate an object of your class.
The property system (setters/getters) works as expected as do slots.
https://doc.qt.io/qt-5/qjsengine.html#newQMetaObject
Here is my test code - first is the C++ part that adds the meta object
QQmlApplicationEngine engine;
QJSValue jsMetaObject = engine.newQMetaObject(&MyClassOfObject::staticMetaObject);
engine.globalObject().setProperty("MyClassOfObject", jsMetaObject);
I can now write JS that news an object of that type and use setters/getters etc. This code actually exists in a MouseArea onClicked handler for manual testing.
var bob = new MyClassOfObject();
print(bob.x);
bob.x = 918264;
print(bob.x);
print(bob.words);
And here is the class definition...
class MyClassOfObject : public QObject
{
Q_OBJECT
Q_PROPERTY(int x READ getX WRITE setX)
Q_PROPERTY(int y READ getY WRITE setX)
Q_PROPERTY(QStringList words READ getWords)
public:
Q_INVOKABLE explicit MyClassOfObject(QObject *parent = nullptr);
public slots:
int getX() const { return x; }
int getY() const { return y; }
void setX(int x) { this->x = x; }
void setY(int y) { this->y = y; }
QStringList getWords() const;
private:
int x = -113;
int y = 616;
QStringList stringList;
};
As of Qt5.5 QScriptEngine has been deprecated, so only QJsEngine should be used in the future.
https://wiki.qt.io/New_Features_in_Qt_5.5#Deprecated_Functionality
If you look up Documentation of QScriptEngine, or by searching "QScriptEngine examples" you can find some stuff about making Custom C++ Classes available to QScriptEngine.
Here is a good place to start:
link to example
QScriptEngine is very similiar to QJsEngine, so it shouldn't be a big problem for you.
Hope this helps :)

Storing complex GWT-Types as Javascript

In my first GWT module I want to store a JavaScript object, later on I want to receive this object in my second GWT module.
Everything works fine for primitive types, but my complex type will always have all fields set to "undefined".
My class, that I want to transfer from one module to the other:
public class SomeThing {
public Set<String> strings = new HashSet<String>();
}
The entry point of my first module looks like this:
public class EntryA implements EntryPoint {
#Override
public void onModuleLoad() {
// define test data
SomeThing someThing = new SomeThing();
someThing.strings.add("hallo123");
// save data to JavaScript
saveToJavaScript(someThing);
// read and show saved data
Window.alert("ModuleA:"+readFromJavaScript());
Window.alert("ModuleA strings:"+readFromJavaScript().strings);
}
private native void saveToJavaScript(SomeThing thing) /*-{
$wnd.storedThing = thing;
}-*/;
private native SomeThing readFromJavaScript() /*-{
return $wnd.storedThing;
}-*/;
}
The entry point of my second module looks like this:
public class EntryB implements EntryPoint {
#Override
public void onModuleLoad() {
// run delayed, so that ModuleA will be executed first
new Timer() {
#Override
public void run() {
// read and show saved data
Window.alert("ModuleB:"+readFromJavaScript());
Window.alert("ModuleB strings:"+readFromJavaScript().strings);
}
}.schedule(5000);
}
private native SomeThing readFromJavaScript() /*-{
return $wnd.storedThing;
}-*/;
}
I am compiling each module separately. Both generated JavaScript files are included in one html file.
The output is:
ModuleA:moduleA.client.SomeThing#a
ModuleA strings:[hallo123]
ModuleB:moduleA.client.SomeThing#a
ModuleB strings:undefined
Does anyone have an idea how to store such complex types? Let me know, if you need some more information.
UPDATE
I found out, that it actually works, if I am "refreshing" the fields in JavaScript. I have no idea why this works!
private native SomeThing readFromJavaScript() /*-{
var a = $wnd.storedThing;
a.#moduleA.client.SomeThing::strings = a['moduleA_client_SomeThing_strings'];
return $wnd.storedThing;
}-*/;
Nevertheless I need a generic approach, which allows to transfer any object - and I don't want to have to mention every possible field... :(
Maybe this has something to do the way modules are loaded.
The preferred way to load multiple modules it is described in: Loading multiple modules in an HTML host page:
If you have multiple GWT modules in your application, there are two ways to
approach loading them.
1. Compile each module separately and include each module with a separate
<script> tag in your HTML host page.
2. Create a top level module XML definition that includes all the modules you
want to include. Compile the top level module to create a single set of
JavaScript output.
...[cut for brevity] The second approach is strongly recommended.
The reason that you can't read fields between GWT modules is that each module is compiled and obfuscated independently. This means that SomeThing.strings could be mapped to .a in one module and `.q' in another. Your "refresh" trick only works because compiling the module in detailed mode usually results in the same name.
You might want to consider using the AutoBeans framework, which supports JSON-encoding the objects in a stable manner.

Categories

Resources