I am using Java Access Bridge API with interop in C#. When trying to create a new AccessibleWindow with the hwnd obtained from the user32.dll method GetForegroundWindow(), it does not recognize the window as a java window, returning null. IsJavaWindow() returns false, but calling it a second time returns true. I tested this out with the example "SwingSet2" application.
public void Initialize()
{
if(!Initialized)
{
accessBridge = new AccessBridge();
var hwnd = WindowsNativeMethods.GetForegroundWindow();
var window = accessBridge.CreateAccessibleWindow(hwnd);
window.AccessBridge.Initialize();
window.AccessBridge.Functions.GetAccessibleContextFromHWND(hwnd, out vmId, out mainContext);
Initialized = true;
}
}
I am also using code from this repo: Google Access Bridge
Initialize() or the initialization code in general needs to called in a UI thread or a message pumping thread.
Using the IntPtr from GetForegroundWindow() or GetActiveWindow() always returns false in IsJavaWindow(), but using FindWindow() works from user32.dll's methods.
Related
I'm trying to return value to Qt service from java service code. Java always returns initial value for variable. However when I run same code in Qt main activity, it is working.
Code in BackService java class:
public static int SAYK = 0;
public static void callNorm(int valSy) {
BackService.SAYK = valSy;
System.out.println("Result= "+BackService.SAYK);
}
public static int retStt() {
return BackService.SAYK;
}
C++ code in Qt Service:
int jobj = QAndroidJniObject::callStaticMethod<jint>("org/qtproject/example_v2/BackService",
"retStt",
"()I");
When I call "callNorm" function from java, system prints new value of SAYK. But when I call "retStt" from Qt service, it returns only initial value 0. I tried also with callStaticObjectMethod, getStaticField, java native functions(gives unsatisfied link error) etc. Unfortunately, none of these worked. Also I can call java functions that has not return value from Qt Service, without problems.
I thought that android service was placed in same android sandbox with qt cpp app, and they are sharing memory. But actually i had in android manifest:
<service android:process=":qt" android:name=".QtJavaService">
What actually happens if you call static methods of Your Android Java Service you will get them from empty instance of the qt process, and the proper instance is in :qt process.
So you just need to remove "android:process=":qt" "
The Java Access Bridge API documentation states:
The Java Access Bridge API calls are contained in AccessBridgeCalls.h
and to use them, you must also compile the file AccessBridgeCalls.c,
which acts as the interface between your application and
WindowsAccessBridge.dll.
But when I tried to create a dll out of AccessBridgeCalls.h & AccessBridgeCalls.c, it says missing AccessBridgeDebug.h file.
How do I call the initiateAccessBridge() method? I am looking to perform tasks similar to JavaMonkey.exe, such as identifying components in a Java Swing application.
When I call isJavaWindow(int) from the Access Bridge, it always returns false for all handlers.
JAB relies on the Windows Messaging mechanism to perform inter-process communications. You have to set up a thread to run message pump loop and call initiateAccessBridge() in that thread, otherwise some methods like isJavaWindow() will always return false.
Here is a C# reference:
var accessBridge = new AccessBridge();
// Use WPF UI thread if there is one
var messageLoopDispatcher = Application.Current?.Dispatcher;
if (messageLoopDispatcher == null)
{
var readyEvent = new ManualResetEvent(false);
var messageLoopThread = new Thread(() =>
{
messageLoopDispatcher = Dispatcher.CurrentDispatcher;
readyEvent.Set();
Dispatcher.Run();
});
messageLoopThread.SetApartmentState(ApartmentState.STA);
messageLoopThread.Start();
readyEvent.WaitOne();
}
messageLoopDispatcher.Invoke(() =>
{
accessBridge.Initialize();
});
First off, I am not a Java developer. I had to create a Java applet to invoke some code in a native DLL I wrote from the browser.
I use JNA to load a native DLL and invoke its methods.
I have signed the applet using a self-signed certificate.
The browser asks me whether or not to allow the execution of the applet.
The applet code which loads my DLL is enclosed within AccessController.doPrivileged block.
Like this:
public String Test()
{
pHelper = AccessController.doPrivileged(new PrivilegedAction<IHelper>()
{
#Override
public IHelper run()
{
return (IHelper)Native.loadLibrary("Helper", IHelper.class);
}
});
return "test";
}
The code works fine when debugged inside Eclipse.
It does not work when invoked from JavaScript. Causes PrivilegedActionException.
If I remove the entire AccessController.doPrivileged block and leave return "test" only, the code runs when invoked from JavaScript. Any code that doesn't require privileges runs fine when invoked from JavaScript.
Tested from Chrome version 40.something and Firefox 36 on Windows 8.1 64-bit.
The native DLL is 32-bit as well as JRE used to run the applet.
Any tips?
I have never resolved this particular mystery. However, I was able to find a workaround, thanks to my applet design specification which doesn't require exposing any applet methods which need to be invoked to perform privileged operations.
I have found that executing privileged operations inside the applet init() function will work. Only privileged operations executed by invoking from JavaScript seem to cause problems. Consider the following code.
public class MyApplet extends JApplet {
private IHelper pHelper = null;
private MyReturnedInfo pInfo = null;
public void init() {
pHelper = (IHelper)Native.loadLibrary("Helper", IHelper.class);
if (pHelper != null) {
pInfo = pHelper.GetInfo();
}
}
public String GetInfoString() {
if (pInfo != null) {
// need to call toString to convert from native wide char to something JavaScript will be able to interpret
return pInfo.MyInfoString.toString();
}
return null;
}
}
Upon loading this applet, calling document.myApplet.GetInfoString() from JavaScript (providing the applet has an ID "myApplet") will return the required information.
Interestingly though, after signing the applet with a certificate issued by a trusted authority such as VeriSign, even this would not work in IE, while it would work properly in FF and Chrome. I have seen signed Java applets which work fine when called from JavaScript in IE, but I guess my applet is special because it requires all-permissions attribute in the manifest and IE probably doesn't like that. It's a guess. However, I have never found the real reason for that either, because I was able to resort to another workaround. :) If you are reading this answer then I bet you are interested in it as well.
Java applets allow us to provide additional parameters which we are able to obtain by calling this.getParameter() from inside the init() function. Also, if we allow the applet to call JavaScript functions from our HTML document by using mayscript attribute, we can easily combine these two facts to provide the JavaScript function for applet to call after the information from our native DLL has been obtained.
Let's say that in our HTML, we define the JavaScript like this.
<script type="text/javascript" src="https://www.java.com/js/deployJava.js"></script>
<script type="text/javascript">
var attributes = {
id: "myApplet",
name: "myApplet",
code: "MyApplet.class",
mayscript: "true",
scriptable: "true",
archive: "/path(s)/to/jar(s)",
width: 0,
height: 0
};
var params = {
"AppletReady": "appletInitialized",
};
// For convenience, it's easier to deploy the applet using deployJava,
// so it writes the applet HTML tag for us after checking if Java is installed.
// We have included it above.
deployJava.runApplet(attributes, params, "1.8.0");
function appletInitialized(myString, someOtherArgument) {
// do something with your parameters
// NOTE: do NOT call alert() from this function!
// Because it will most likely cause your browser to freeze,
// I've found that's also one of the things Java doesn't like.
};
</script>
Then, we modify the Java applet code to look like this.
public class MyApplet extends JApplet {
private IHelper pHelper = null;
private MyReturnedInfo pInfo = null;
public void init() {
// Read the AppletReady parameter as passed from JavaScript
String paramKey = "AppletReady";
String jsLoadedCallback = this.getParameter(paramKey);
// Load the library and get the information
pHelper = (IHelper)Native.loadLibrary("Helper", IHelper.class);
if (pHelper != null) {
pInfo = pHelper.GetInfo();
if (pInfo != null && jsLoadedCallback != null) {
// Get the window which contains "this" applet
JSObject jsObject = JSObject.getWindow(this);
// Call the provided JavaScript function.
// You can use as many parameters as you need.
jsObject.call(jsLoadedCallback, new Object[] {
pInfo.MyInfoString.toString(),
pInfo.SomeOtherStringMaybe.toString()
});
}
}
}
}
However, if you need the applet to call your native DLL methods dynamically during runtime (I.E. you require the applet to expose functions which need to be called to perform privileged operations dynamically) this solution will not work for you and you are out of luck, at least if using JNA.
Suppose I have a Javascript file
function js_main(args){
/* some code */
var x = api_method1(some_argument);
/* some code */
}
And I try to run it with javax.scripting the usual way
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("javascript");
engine.eval(...);
Now the I'd like to handle the call to api_method1 in Javascript with my Java class. I'd like to have some kind of mapping/binding of calls i.e. each time the script calls api_method1(arg) a method
public Object api_method1(Object arg){ ... }
(placed in the same class as the engine) would be called.
Can I achieve this?
use engine.createBindings() to make a Bindings object;
put an object exposing your method into the bindings with some name:
Bindings b = engine.createBindings();
b.put("api", yourApiObject);
engine.setBindings(b, ScriptContext.ENGINE_SCOPE);
Then in JavaScript there'll be a global "api" object you can call:
api.method1( "foo", 14, "whatever" );
The facility is easy to use, but be careful with what you pass back and forth; it doesn't do that much to convert JavaScript types to Java types.
I have the following code in an applet to call some Javascript (it's a bit convoluted because the fn that's called gets an object from the DOM identified by divId and calls a function on it).
#Override
public final void start() {
System.err.println("start() method called");
this.javascript = JSObject.getWindow(this);
this.jsObjectDivId = getParameter("parent_div_id");
this.initCallbackFnName = getParameter("init_callback");
Object args[] = {this.jsObjectDivId, this.initCallbackFnName};
System.out.print("Calling init_callback\n");
this.javascript.call("callJS", args);
}
The callJS function is:
window.callJS = function(divId, functionName, jsonArgString) {
var args, obj;
obj = $(divId).data('neatObject');
args = eval(jsonArgString);
return obj[functionName](args);
};
In Firefox/Chrome the divId and functionName arguments contain valid strings and everything works fine; the desired function is called on the object hanging off the specified DIV data.
In Safari, the divId and functionName arguments are both reported as a JavaRuntimeObject with values of true.
> divId
JavaRuntimeObject
true
What gives?
LiveConnect is not fully supported in all browsers. Especially, Safari doesn't convert Java Strings to the prober JS equivalent when using call. In your case you can just use eval at the Applet side instead of call and put in a JSON string object with the arguments. Something like:
javascript.eval(callback + "({\"id\":\"" + id + "\",\" ... })")
Basically, you need to know the cross-browser compatible subset of LiveConnect that works.
I've written a blog post that describes the subset: http://blog.aarhusworks.com/applets-missing-information-about-liveconnect-and-deployment/
It comes with a LiveConnect test suite which runs in the browser: http://www.jdams.org/live-connect-test
I had a similar issue with calling a method on an applet in Safari. It was returning a JavaRuntimeObject that I caused an exception when it was user later on.
As pointed out by #edoloughlin I had to use (applet.getMethod() + "") after which point the proper string was evaluated.
The comment saved me a bunch of time so I thought it useful to add here as I can't comment above.