C# class attributes not accessible in Javascript - javascript

I want to make a class of mine accessible in JavaScript via a C# WebView-Control.
Therefore I am using the WebView.AddWebAllowedObject method. However if I assign an attribute, it works fine, but if I assign the whole class to get all attributes in js, all of the attributes(and methods btw) are "undefined". I tried everything I found in the www. See the attached code:
//The class I want to make accessible
[AllowForWeb, ComVisible(true)]
[MarshalingBehavior(MarshalingType.Agile)]
public class DeviceInformation
{
public string IPAdress { get; private set; }
public DeviceInformation()
{
IPAdress = GetIPAdress();
}
public string GetDeviceUUID()
{
EasClientDeviceInformation deviceinfo = new EasClientDeviceInformation();
return deviceinfo.Id.ToString();
}
public string GetIPAdress()
{
List<string> ipAddresses = new List<string>();
var hostnames = NetworkInformation.GetHostNames();
foreach (var hn in hostnames)
{
if (hn?.IPInformation != null && (hn.IPInformation.NetworkAdapter.IanaInterfaceType == 71 ||
hn.IPInformation.NetworkAdapter.IanaInterfaceType == 6))
{
string ipadress = hn.DisplayName;
return ipadress;
}
}
return string.Empty;
}
}
Here the objects are initialized.
DeviceInformation devinf = new DeviceInformation();
private void View_NavigationStarting(WebView sender, WebViewNavigationStartingEventArgs args)
{
if (args.Uri.Host == "")
{
//win_ipadress has an ipadress as value
view.AddWebAllowedObject("win_ipadress", devinf.IPAdress);
//deviceInformation is initialized as well but I have no access to its attributes
view.AddWebAllowedObject("deviceInformation", devinf);
}
}
That's the way i call it in js:
else if ($.os.ie) {
myIpAdr = window.win_ipadress;
//Throws an exception because GetIPAdress() is "undefined"
myIpAdr = window.deviceInformation.GetIPAdress();
}
I am using this in a Windows Universal App. The Javascript and in the WebView displayed HTML-Code is already in use for Android an iOS.

I believe you need to define the method name starting with a lower case character.
For example: change GetIPAddress to getIPAddress.
I tested it on my side and found if I use the upper case name 'GetIPAddress', it won't work. But if I use getIPAddress, it works.
And after I read kangax's explanation in this thread, I think it makes sense.
[Update]
Since it still doesn't work after you make the change on method name, I think the issue should be related to how you expose the windows runtime object. I guess you simply defined the DeviceInformation class and tried to use it in the same project.
First, we need to create a separate windows universal windows runtime component project.
The c# class DeviceInformation should be put into this project. Keep the same code.
Then, in your universal app project, add reference to the windows runtime component and keep rest code to consume the windows runtime object.
[Update 2]
Just noticed an interesting behavior in VS. No matter if the Method name we defined in C# is starting with uppercase or lowercase, the visual studio intellisense shows the lowercase, so the method name will be automatically converted when we try to use it in js.

Related

Setting a member in current class using Frida

I am hooking a certain function in Frida which uses the code:
this.carrier.getId()
However, at this point in time this.carrier has not been set yet, which causes the app to crash.
So I am thinking of manually setting this member in the current function in the class. So that carrier will exist by the time the code takes place.
The problem is that I encounter a problem by doing that.
So far this is what I got:
Java.perform(function () {
var SignUpActivity = Java.use('com.app.features.authentication.SignUpActivity');
SignUpActivity.validatePhoneNumber.implementation = function() {
var Carrier = Java.use("com.app.Carrier");
this.carrier = Carrier.$new();
console.log(this.carrier) // This prints "[object Object]"
console.log(this.carrier.setId) // This prints "undefined"
this.carrier.setId(123); // crashes
};
});
Code of carrier:
package com.app;
import android.os.Parcel;
import android.os.Parcelable;
public class Carrier implements Parcelable {
private int id;
private String name;
private String officeTerminalAddress;
public Carrier() {
}
protected Carrier(Parcel parcel) {
this.id = parcel.readInt();
this.name = parcel.readString();
this.officeTerminalAddress = parcel.readString();
}
public int getId() {
return this.id;
}
public void setId(int i) {
this.id = i;
}
}
Looks like the common problem in Frida that the way to access fields is different in Frida.
Frida uses JavaScript code so it can't handle non-JavaScript objects directly.
Therefore it wraps "native" objects (Android Java objects in this case) in JavaScript objects.
If you now call in Frida this.carrier you are getting the Frida JavaScript wrapper, not the Java Carrier instance you are aiming.
Of course the Frida JavaScript wrapper does not has the methods you try to call, therefore this.carrier.setId(123); will always fail.
Accessing a Java field with Frida
To access a field you always have to call .value on it to get the actual value:
So if you want this.carrier you have to use this.carrier.value.
Furthermore it is recommended to access a field by it's name with an additional underscore in front. Otherwise in obfuscated apps it may occur that there is a field and a method of the same name. In such a case Frida doesn't know if you want to access the field carrier or the method carrier.
Conclusion if you want to access a field of an Java class instance in an Android app using Frida the recommended way is
this._carrier.value
So for writing a field value you should call
this._carrier.value = ...
And the same way for reading.
Reference to Frida help pages
This is also described on the Frida pages, e.g. here:
Note we use this.m.value = 0 instead of this.m = 0 to set the field’s value. If there is also a method in this class called m, we need to use this._m.value = 0 to set the value of field m. In general, when looking at the properties of objects it will be necessary to use .value to access the values those fields refer to.
Complete simplified code
But in your case you can simplify everything by just using a local variable:
Java.perform(function () {
var SignUpActivity = Java.use('com.app.features.authentication.SignUpActivity');
SignUpActivity.validatePhoneNumber.implementation = function() {
const Carrier = Java.use("com.app.Carrier");
const c = Carrier.$new();
c.setId(123);
this._carrier.value = c;
};
});

How to expose JS patch function of Incremental DOM library in GWT app using #JsInterop

I would like to use Incremental DOM library in my GWT app.
https://google.github.io/incremental-dom/#about
As I am coming from the Java world, I struggle with concepts of JavaScript namespaces and modules. I was able to use Closure Compiler with closure version of Incremental DOM (has to be build from sources).
It starts with the following line:
goog.module('incrementaldom');
So if I was to use it in regular JS I would type:
var patch = goog.require('incrementaldom').patch;
And then the patch function would be available in the scope of my code. But how to make it accessible from #JsInterop annotated classes?
I tried something like:
public class IncrementalDom {
#JsMethod(namespace = "incrementaldom", name = "patch")
public static native void patch(Element element, Patcher patcher);
#JsFunction
#FunctionalInterface
public interface Patcher {
void apply();
}
}
But it doesn't work. I get this error in the runtime:
(TypeError) : Cannot read property 'patch' of undefined
So I guess I have to somehow expose the incrementaldom module or at least only the patch method. But I don't know how.
After fighting for the whole day I found the solution. In the goog.module: an ES6 module like alternative to goog.provide document I found the missing information about the role of goog.scope function - required modules are visible only within the scoped call.
I created another Closure JS file named incrementaldom.js:
goog.provide('app.incrementaldom'); // assures creation of namespace
goog.require("incrementaldom");
goog.scope(function() {
var module = goog.module.get("incrementaldom");
var ns = app.incrementaldom;
app.incrementaldom.patch = module.patch;
});
goog.exportSymbol("app.incrementaldom", app.incrementaldom);
And now I can call it from Java code like this:
public class IncrementalDom {
#JsMethod(namespace = "app.incrementaldom", name = "patch")
public static native void patch(Element element, Patcher patcher);
#JsFunction
#FunctionalInterface
public interface Patcher {
void apply();
}
}
Still I have to define every object exported in original module separately in the Closure JS file. Fortunately I only need patch method. I hope one day I will find less cumbersome way for #JsInterop with goog.module :(

Javascript bridge / upcall to JavaFX (via JSObject.setMember() method) breaks when distributing

The Problem
I spent several hours trying to determine why my distributed code fails and yet my source code when debugging with the IDE (NetBeans) works without issue. I have found a solution and am posting to help others that might have similar issues.
BTW: I'm a self-taught programmer and might be missing a few fundamental concepts -- feel free to educate me.
Background Information
Using a WebView control within JavaFX application I load a webpage from an html file. I want to use JavaScript to handle the HTML side of things but I also need to freely pass information between Java and JavaScript (both directions). Works great to use the WebEngine.executeScript() method for Java initiated transfers and to use JSObject.setMember() in Java to set up a way for JavaScript to initiate information transfer to Java.
Setting up the link (this way breaks later):
/*Simple example class that gives method for
JavaScript to send text to Java debugger console*/
public static class JavaLink {
public void showMsg(String msg) {
System.out.println(msg);
}
}
...
/*This can be added in the initialize() method of
the FXML controller with a reference to the WebEngine*/
public void initialize(URL url, ResourceBundle rb) {
webE = webView.getEngine();
//Retrieve a reference to the JavaScript window object
JSObject jsObj = (JSObject)webE.executeScript("window");
jsObj.setMember("javaLink", new JavaLink());
/*Now in our JavaScript code we can type javaLink.showMsg("Hello!");
which will send 'Hello!' to the debugger console*/
}
The code above will work great until distributing it and attempting to run the JAR file. After hours of searching and testing different tweaks I finally narrowed the problem down to the JavaLink object itself (I eventually learned that you can use try-catch blocks in JavaScript which enabled me to catch the error: "TypeError: showMsg is not a function...").
The Solution
I found that declaring a global variable to hold an instance of the JavaLink class and passing that as a parameter into the setMember() method fixes it so that the app now runs both in the IDE as well as a standalone JAR:
JavaLink jl;
...
jl = new JavaLink();
//replace anonymous instantiation of JavaLink with global variable
jsObj.setMember("javaLink", jl);
Why!?
I'm guessing this has to do with garbage collection and that the JVM does not keep a reference to JavaLink unless you force it to by declaring a global variable. Any other ideas?
Just to demo that the hypothesis postulated in #DatuPuti's answer appears to be correct. Here's a quick test. Pressing the HTML button increments the counter in the HTML page, and also outputs to the system console. After forcing garbage collection by pressing the "GC" button, the updates to the system console stop:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import netscape.javascript.JSObject;
public class WebViewCallbackGCTest extends Application {
private final String HTML =
"<html>"
+ " <head>"
+ " <script>"
+ " var count = 0 ;"
+ " function doCallback() {"
+ " count++ ;"
+ " javaLink.showMsg('Hello world '+count);"
+ " document.getElementById('test').innerHTML='test '+count;"
+ " }"
+ " </script>"
+ " </head>"
+ " <body>"
+ " <div>"
+ " <button onclick='doCallback()'>Call Java</button>"
+ " </div>"
+ " <div id='test'></div>"
+ " </body>"
+ "</html>" ;
#Override
public void start(Stage primaryStage) {
WebView webView = new WebView();
webView.getEngine().loadContent(HTML);
JSObject js = (JSObject) webView.getEngine().executeScript("window");
js.setMember("javaLink", new JavaLink());
Button gc = new Button("GC");
gc.setOnAction(e -> System.gc());
BorderPane root = new BorderPane(webView);
BorderPane.setAlignment(gc, Pos.CENTER);
BorderPane.setMargin(gc, new Insets(5));
root.setBottom(gc);
primaryStage.setScene(new Scene(root, 400, 400));
primaryStage.show();
}
public static class JavaLink {
public void showMsg(String msg) {
System.out.println(msg);
}
}
public static void main(String[] args) {
launch(args);
}
}
Of course, as stated in the other answer, if you declare an instance variable
private JavaLink jl = new JavaLink();
and replace
js.setMember("javaLink", new JavaLink());
with
js.setMember("javaLink", jl);
the problem is fixed, and the updates continue to appear in the console after calling System.gc();.
The solution from #DatuPuti works fine but partially. In my case, I've got multiple nested classes in the JavaLink class. Before doing what #DatuPuti exposes all the nested classes link broke and after applying the exposed solution, only the class being called is the one that keeps the link, all the rest are still breaking. This is the model of my JavaLink class:
public class JavaLink{
public NestedClass1 ClassName1;
public NestedClass2 ClassName2;
//Constructor
public JavaLink(){
ClassName1 = new NestedClass1();
ClassName2 = new NestedClass2();
}
//Nested classes being called from Javascript
public class NestedClass1(){}
public class NestedClass2(){}
}
The link would be created like this:
JavaLink javaLink;
...
javaLink = new JavaLink();
jsObject.setMember("JavaLink", javaLink);
Then, from Javascript I call classes methods like this:
JavaLink.ClassName1.method();
JavaLink.ClassName2.method();
And here comes the probem: When the crash occurs calling ClassName1 methods, ClassName2 unlinks and it's not available anymore using Javascript. The same happens if I the crash occurs while calling ClassName2 methods.
The solution that works for me (in addition to the exposed solution):
Besides declaring JavaLink in the higher scope possible, declaring all the nested classes will make them keep the link. For example (keeping the same reference from the example
class model):
JavaLink javaLink;
JavaLink.NestedClass1 nestedClass1;
JavaLink.NestedClass2 nestedClass2;
javaLink = new JavaLink();
nestedClass1 = javaLink.new NestedClass1();
nestedClass2 = javaLink.new NestedClass2();
//Creating an Object array to store classes instances
Object[] javaLinkClasses = new Object[2];
javaLinkClasses[0] = nestedClass1;
javaLinkClasses[1] = nestedClass2;
jsObject.setMember("JavaLink", javaLinkClasses); //Setting member as an array
And then finally, in order to call methods from nested classes in Javascript, object reallocation is needed, just like this (Javascript):
JavaLink.NestedClass1 = JavaLink[0];
JavaLink.NestedClass2 = JavaLink[1];
//Now we are able to call again methods from nested classes without them unlinking
JavaLink.NestedClass1.method();
I hope this helps people facing the same issue. I'm using Java JDK 1.8 with IntelliJIDEA.

Creating an array of JSON objects in JavaScript on a Razor page from custom viewmodel in MVC?

I'm trying to populate the array in my script (it's going to be used for charting with D3.JS later on). According to this post, I'm supposed to use the syntax below. However, it doesn't work, bacause I get the error on the pushy line saying Uncaught ReferenceError: WebSite is not defined, where WebSite is the name of the namespace of the data (I'm guessing that, as it's the name of my project).
<script>
var data = new Array();
#foreach (var piece in #Model.DataPieces)
{
#:data.push(#piece);
}
</script>
I'm pretty sure it has to do with the data type of piece, because the following change makes it work (at least not producing a bunch of errors). I'm picking out the individual fields from piece object and push those into the array, as a new object.
<script>
var data = new Array();
#foreach (var piece in #Model.DataPieces)
{
#:data.push({'cat': '#piece.Category', 'key': '#piece.Key', 'val': '#piece.Value'});
}
</script>
It's inconvenient, prone to mistakes and requires a refactoring of the assignment each time the model changes. How can I avoid this approach and be able to automagically create JSON objects upon assignment, as shown in the first sample?
The viewmodel for the Razor page is declared as folows.
namespace WebSite.Models
{
public class DrillDataViewModel
{
public List<DataPiece> DataPieces { get; set; }
public class DataPiece
{
public string Category { get; set; }
public string Key { get; set; }
public int Value { get; set; }
}
}
}
The line #:data.push(#piece); will be written to the output HTML as
data.push(<the result of calling ToString() on #piece>);
You need to replace #piece with something that will be evaluated to the JSON you want. This should work:
#:data.push(#Html.Raw(Json.Encode(piece)));
Or you can just output the whole array at once:
var data = #Html.Raw(Json.Encode(Model.DataPieces));
Try to pass this from Razor page to JavaScript.
#Html.Raw(#JsonConvert.SerializeObject(Model.DataPieces)
.Replace("{\"", "{")
.Replace(",\"", ",")
.Replace("\":", ":"))
The replaces serve to get rid of the invalid characters produced by default in the converter without you needing to play with streams or applying other libraries. Ugly but working.
Sometimes, I also add one more replace: .Replace("\"","'") to get a more JS like look. The outer method is needed so you don't get problems with & in the &quote;.
This is a hand-on solution so if anybody knows a better way, I'd love to get some feedback.
Try
var yourJavascriptArray=#Html.Raw(Json.Encode(YouModel));

Call JavaScript function from Silverlight 4.0 application

I am trying to call a function from a Silverlight application. It should be a very simple task to do but so far I am not getting the result that I am looking for.
This is my Silverlight code:
private void button2_Click(object sender, RoutedEventArgs e)
{
HtmlPage.Window.Invoke("SayHello", new string[] { "Salut!" });
}
And this is the JavaScript code :
function SayHello(theid) {
alert(eval(theid));
var divStatusDiv = document.getElementById("divStatus");
divStatusDiv.style.backgroundColor = "Red";
}
The alert message always show "undefined" but when I press "OK" the colour of that DIV gets changed to Red as it should be.
Why am I getting "Undefined" all the time ?
You need to create the json that can be passed properly instead of just passing along an array like that. You can simply return "Salut!" instead of new string[] { "Salut!" } or you can create the json array for the string array you have.
I'm not familiar with Silverlight, but if theid has value "Salut!" inside of SayHello, then you cannot eval it, since it is a string of text, not code. You should change the line alert(eval(theid)); to just alert(theid);.
Use
alert(eval(theid.value));

Categories