I am writing a wrapper class for C++ ".so". I want to use the library in Java application and Android app using JNI. So I have to create header file and cpp file which will do JNI calls.
I could use that on Linux in Java application.
The steps I followed:
Created java class and called native functions in that class
public class TestWrapper {
static {
System.load("/home/native.so");
}
public static void main(String[] args) {
new TestWrapper().TestWrapper();
}
private native void sayHello();
}
Created header file and cpp file. CCP contains following code
JNIEXPORT void JNICALL Java_TestWrapper_sayHello(JNIEnv *, jobject){
uint16_t data = 0;
void (*func_print_name)(const uint16_t*);
void* handle = dlopen("libCppTobeUsed.so.0", RTLD_LAZY);
if (handle){
*(void**)(&func_print_name) = dlsym(handle, function_name);
func_print_name(&data);
dlclose(handle);
std::cout << "data received .." << data << std::endl;
}
}
}
Compiled this cpp class and generated "native.so"
This is working fine. The "native.so" could call the fuction form "ibCppTobeUsed.so.0" when called from TestWrapper.java.
I want to use same library for android as well. So, I have to write wrapper class all over again in Android NDK? Or I can compile my "native.so" for Android platform?
If I try to use it directly, I get error
"install_failed_no_matching_abis".
No, you cannot use the same shared library. Android is not GNU. You need to compile your libraries for Android.
So, I have to write wrapper class all over again in Android NDK?
No, you can write it in a way that works for both. You need to factor our your JNI wrapper class from your main class, since Android uses Activity instead of main.
I would also strongly recommend against ever relying on dlclose on any platform. The API is not sound, and will lead to surprising behavior with modern C++. A single global thread_local with a non-trivial destructor renders the library un-unloadable, so the next dlopen will not reset library state as you might expect. If you need to implement initialization/finalization logic for your library, make explicit Initialize and Finalize functions a part of the libary and call them directly.
Without knowing your architecture's full architecture I can't be sure, but from the sample you've given here I'd recommend dropping the dlopen/dlsym from your JNI entirely and just link against libCppTobeUsed directly.
Related
I have code in Unity that calls a function programmed in Java for Android using AndroidJavaClass and AndroidJavaObject and I would like to pass this code to a dll in C# .Net but I can't find an easy way to call Java functions using C# .Net since the AndroidJavaObject and AndroidJavaClass classes are exclusive to the Unity dll, here is an example of my code:
Java (Android):
public class DeviceManager {
public interface DeviceListener {
void DoSomething();
}
private DeviceListener deviceListener;
public DeviceManager(DeviceListener deviceListener){
this.deviceListener = deviceListener;
}
public String[] getSomeData(){
return new String[]{};
}
}
C# (Unity):
public class DeviceListener : AndroidJavaProxy
{
DeviceManager deviceManager;
public DeviceListener(DeviceManager manager) : base("com.myapp.DeviceManager$DeviceListener")
{
this.deviceManager = manager;
}
public void DoSomething();
}
public class DeviceManager
{
private AndroidJavaObject jo;
public DeviceManager(){
jo = new AndroidJavaObject("com.myapp.DeviceManager", new DeviceListener(this));
}
public string[] GetData(){
AndroidJavaObject returnedData = jo.Call<AndroidJavaObject>("getSomeData");
return AndroidJNIHelper.ConvertFromJNIArray<string[]>(returnedData.GetRawObject());
}
}
What is the best way to do that example in C# .Net instead of Unity?
EDIT: my main question is related to know how to get objects and call functions from an .aar file in .NET Framework inside a dll C# file for Unity
EDIT 2: I found some information about Xamarin, maybe that is what I am looking for?: https://github.com/xamarin/monodroid-samples/tree/master/JavaIntegration/AarBinding But even though I could link an .aar file to a dll in C# that doesn't quite answer my question in the best way to create a code that works the same as the example described above
EDIT 3: Currently, to solve this problem, I am using the UnityEngine .dll in my Visual Studio project but I want to generate a .dll for use in other graphics engines so I would like to be able to find a different solution than using Unity's own .dll for solve this problem, hence I mentioned Xamarin since it is a good way to communicate .Net Framework with Android (Java) but I have not found any example that uses it outside of a mobile application created in Visual Studio since in my case I would need to obtain objects and call functions from the .aar file
It seems like you are looking to use an AAR plugin, I haven't done it myself, but Unity has a doc on how to integrate it in your project.
Then, once that library is compiled in Android studio and integrated in Unity, you can look at this example on how to call it from C# code.
I haven't done it myself, but the example should put you on the right track.
Regarding the scripting backend, I don't believe it will have any impact on this.
Edit
Added Extract from the Example
Assets / Plugins / Android
Place your native android .AAR file inside this folder.
Now let’s write some code to call the plugin.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NativeCodeRunner : MonoBehaviour
{
void Start(){
CallNativePlugin();
}
//method that calls our native plugin.
public void CallNativePlugin()
{
// Retrieve the UnityPlayer class.
AndroidJavaClass unityPlayerClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
// Retrieve the UnityPlayerActivity object ( a.k.a. the current context )
AndroidJavaObject unityActivity = unityPlayerClass.GetStatic<AndroidJavaObject>("currentActivity");
// Retrieve the "Bridge" from our native plugin.
// ! Notice we define the complete package name.
AndroidJavaObject alert = new AndroidJavaObject("plugins.vsoft.com.library.Alert");
// Setup the parameters we want to send to our native plugin.
object[] parameters = new object[2];
parameters[0] = unityActivity;
parameters[1] = "Hello World!";
// Call PrintString in bridge, with our parameters.
alert.Call("PrintString", parameters);
}
}
my main question is related to know how to get objects and call
functions from an .aar file in .NET Framework inside a dll C# file for
Unity
If you want to use AndroidJavaClass or AndroidJavaObject methods in your custom DLL, you should add references to the Unity DLLs. In Visual Studio, open the contextual menu for References in the Solution Explorer and select Add Reference. Then, select Browse > Select File.
At this stage, select the required .dll file, located in the UnityEngine folder.
using System;
/* Unity */
using UnityEngine;
namespace DLLTest {
public class MyDLLTest {
}
}
Android developer documentation describes the java.net package to be use for networking by apps. Now Android has a Linux kernel, so eventually any HTTP request made by the package has to translate into some system call.
What I want to ask is whether this library also ships with some JNI layer code, and does it rely on libc library to interact with the kernel ?
In Oracle JVM net.dll is loaded inside AbstractPlainSocketImpl which is present in jre/bin.
/**
* Load net library into runtime.
*/
static {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
System.loadLibrary("net");
return null;
}
});
}
Android's Dalvik also works the same way. Take a look at the following links:
https://github.com/android/platform_system_core/tree/master/libcutils
https://github.com/android/platform_system_core/blob/master/libcutils/socket_network_client.c
I am trying to use a public interface in a .dll file in JAVA. This is what a public interface in the .dll shows:
// Interface declaration.
public interface ISslTcpClient
{
string Encrypt(string requestContent);
string Decrypt(string requestContent);
};
Here is how I load my dll and use it in my Java application in eclipse:
public class NJInew {
static {
System.loadLibrary("ICVTnsClient");
}
native String Encrypt(String requestContent);
public static void main(String[] args) {
NJInew p= new NJInew ();
p.Encrypt("pejman");
}
}
However I get the following error:
Exception in thread "main" java.lang.UnsatisfiedLinkError: NJInew.Encrypt(Ljava/lang/String;)Ljava/lang/String;
at NJInew.Encrypt(Native Method)
at NJInew.main(NJInew.java:13)
Please let me know how to fix this issue and I would greatly appreciate it.
I do not understand how the native DLL can possibly look like a Java interface. Are you sure it's a JNI DLL? You can't call any old DLL from Java. If it isn't a JNI DLL you'll either need to add the required JNI entry points to it, starting with the javah tool, or write a wrapper DLL that contains those and calls this one.
This interface looks definitely like written in .NET C#. Therefore you do not need to call native DLL but managed .NET DLL and that is completely different topic.
You can achieve it yourself by wrapping .NET DLL with native C++ code and calling it from JAVA but number of obstacles and challenges is huge... You need to take care of type conversion, memory management, threads, C++ runtime depndencies and more... so that approach is not recommended.
What you should look for is native bridge like Javonet which takes care of all those things for you and makes sure you can run your code on any machine.
Using your interface would like like this:
Javonet.addReference("ICVTnsClient.dll");
NObject client = Javonet.New("SslTcpClient");
//some concreto type which implements this interface
String encryptedStr = client.invoke("Encrypt", "some string");
You can get free license for academic and non-commercial usage or try the free trial: http://www.javonet.com
Is there a nice Java API I can use on top of JVMTI?
JVMTI was not built for having Java API on top. The JVM TI definition itself says:
The JVM tool interface (JVM TI) is a standard native API that allows for native libraries to capture events and control a Java Virtual Machine (JVM) for the Java platform.
Since it was built for native API to capture events & controls, I don't think there are API on top of it. May you can explain what you are trying to achieve?
I am not aware of any Java API on top of JVM TI.
ok... just tried it... seems to work as expected.... in real life the VMInit callback would return an instance of a class that implemented an interface that mirrored the C JVMTI interface.... the C agent would store this instance and call it when required on events .... additionally before the VMInit Java returned it would set up capabilities and callbacks and register events etc.... you would probably be able to get about 90% JVMTI API coverage..... it's just a case of typing it in .... I could do it in a weekend if you have a strong case :-)
the following code produces this:
C: VMInit, preparing to callback Java method
Java: JVMTI callback class, VMInit().
C: VMInit, callback Java method returned successfully
Java: And Finally... Hello, I'm the Java main
package com.stackoverflow;
public class JVMTICallback {
public static void VMInit() {
System.out.println("Java:\tJVMTI callback class, VMInit().");
}
public static void main(String[] args) {
// This main is only here to give us something to run for the test
System.out.println("Java:\tAnd Finally... Hello, I'm the Java main");
}
}
and the C
#include <stdlib.h>
#include "jvmti.h"
jvmtiEnv *globalJVMTIInterface;
void JNICALL
vmInit(jvmtiEnv * jvmti_env, JNIEnv * jni_env, jthread thread)
{
printf("C:\tVMInit, preparing to callback Java method\n");
char *className = "com/stackoverflow/JVMTICallback";
char *methodName = "VMInit";
char *descriptor = "()V";
jclass callbackClass = (*jni_env)->FindClass(jni_env, className);
if (!callbackClass) {
fprintf(stderr,"C:\tUnable to locate callback class.\n");
return;
}
jmethodID callbackMethodID = (*jni_env)->GetStaticMethodID(jni_env, callbackClass, methodName, descriptor);
if (!callbackMethodID)
{
fprintf(stderr, "C:\tUnable to locate callback VMInit method\n");
return;
}
(*jni_env)->CallStaticVoidMethodV(jni_env, callbackClass, callbackMethodID, NULL);
printf("C:\tVMInit, callback Java method returned successfully\n");
}
JNIEXPORT jint JNICALL
Agent_OnLoad(JavaVM * jvm, char *options, void *reserved)
{
jint returnCode = (*jvm)->GetEnv(jvm, (void **) &globalJVMTIInterface,
JVMTI_VERSION_1_0);
if (returnCode != JNI_OK)
{
fprintf(stderr,
"The version of JVMTI requested (1.0) is not supported by this JVM.\n");
return JVMTI_ERROR_UNSUPPORTED_VERSION;
}
jvmtiEventCallbacks *eventCallbacks;
eventCallbacks = calloc(1, sizeof(jvmtiEventCallbacks));
if (!eventCallbacks)
{
fprintf(stderr, "Unable to allocate memory\n");
return JVMTI_ERROR_OUT_OF_MEMORY;
}
eventCallbacks->VMInit = &vmInit;
returnCode = (*globalJVMTIInterface)->SetEventCallbacks(globalJVMTIInterface,
eventCallbacks, (jint) sizeof(*eventCallbacks));
if (returnCode != JNI_OK)
{
fprintf(stderr, "C:\tJVM does not have the required capabilities (%d)\n",
returnCode);
exit(-1);
}
returnCode = (*globalJVMTIInterface)->SetEventNotificationMode(
globalJVMTIInterface, JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, (jthread) NULL);
if (returnCode != JNI_OK)
{
fprintf(
stderr,
"C:\tJVM does not have the required capabilities, JVMTI_ENABLE, JVMTI_EVENT_VM_INIT (%d)\n",
returnCode);
exit(-1);
}
return JVMTI_ERROR_NONE;
}
I searched around and unfortunately can't find any Java API library on top of JVMTI. Seems like you're out of luck.
What you can do though is to call a native lib from your Java code. I'm not very good at C/C++ but from JVMTI docs I see that it's possible to build a small shared library from provided headers. Then you can call it using JNA**. It will give you a nice API wrapper around native library.
Take a look at examples at JNA Getting Started page
This page also links to JNAerator which can generate all necessary Java bindings for you.
The downside of this approach is a necessity of maintaining this thin native layer for your target platforms.
** JNA deals a runtime overhead compared to usual JNI but ease of development overweights performance benefits IMO. Switch to JNI only if you have to.
It will not work. JVMTI has callbacks that the Java code has no direct control over (like ClassPrepare). If these callbacks are implemented in Java, the execution can lead other callbacks causing deadlock.
it wouldn't be difficult to write.... just thunk the JVMTI calls to callback a Java class over JNI.. you would probably face a couple of issues... firstly the Agent_onLoad.. this initial "registering" function happens too early on in the lifecycle of the JVM for it to callback your java.... secondly there are potential circularity issues and the probability that the JVM was written expecting you to do anything like this this at...
Iĺl try to write an example.... back in a few mins...
JDI is a TOP level interface written in Java, which uses JVMTI as the backend api.
this link give you detailed info.
I use Launch4j as a wrapper for my Java application under Windows 7, which, to my understanding, in essence forks an instance of javaw.exe that in turn interprets the Java code. As a result, when attempting to pin my application to the task bar, Windows instead pins javaw.exe. Without the required command line, my application will then not run.
As you can see, Windows also does not realize that Java is the host application: the application itself is described as "Java(TM) Platform SE binary".
I have tried altering the registry key HKEY_CLASSES_ROOT\Applications\javaw.exe to add the value IsHostApp. This alters the behavior by disabling pinning of my application altogether; clearly not what I want.
After reading about how Windows interprets instances of a single application (and a phenomenon discussed in this question), I became interested in embedding a Application User Model ID (AppUserModelID) into my Java application.
I believe that I can resolve this by passing a unique AppUserModelID to Windows. There is a shell32 method for this, SetCurrentProcessExplicitAppUserModelID. Following Gregory Pakosz suggestion, I implemented it in an attempt to have my application recognized as a separate instance of javaw.exe:
NativeLibrary lib;
try {
lib = NativeLibrary.getInstance("shell32");
} catch (Error e) {
Logger.out.error("Could not load Shell32 library.");
return;
}
Object[] args = { "Vendor.MyJavaApplication" };
String functionName = "SetCurrentProcessExplicitAppUserModelID";
try {
Function function = lib.getFunction(functionName);
int ret = function.invokeInt(args);
if (ret != 0) {
Logger.out.error(function.getName() + " returned error code "
+ ret + ".");
}
} catch (UnsatisfiedLinkError e) {
Logger.out.error(functionName + " was not found in "
+ lib.getFile().getName() + ".");
// Function not supported
}
This appears to have no effect, but the function returns without error. Diagnosing why is something of a mystery to me. Any suggestions?
Working implementation
The final implementation that worked is the answer to my follow-up question concerning how to pass the AppID using JNA.
I had awarded the bounty to Gregory Pakosz' brilliant answer for JNI that set me on the right track.
For reference, I believe using this technique opens the possibility of using any of the APIs discussed in this article in a Java application.
I don't have Windows 7 but here is something that might get you started:
On the Java side:
package com.stackoverflow.homework;
public class MyApplication
{
static native boolean setAppUserModelID();
static
{
System.loadLibrary("MyApplicationJNI");
setAppUserModelID();
}
}
And on the native side, in the source code of the `MyApplicationJNI.dll library:
JNIEXPORT jboolean JNICALL Java_com_stackoverflow_homework_MyApplication_setAppUserModelID(JNIEnv* env)
{
LPCWSTR id = L"com.stackoverflow.homework.MyApplication";
HRESULT hr = SetCurrentProcessExplicitAppUserModelID(id);
return hr == S_OK;
}
Your question explicitly asked for a JNI solution. However, since your application doesn't need any other native method, jna is another solution which will save you from writing native code just for the sake of forwarding to the windows api. If you decide to go jna, pay attention to the fact that SetCurrentProcessExplicitAppUserModelID() is expecting a UTF-16 string.
When it works in your sandbox, the next step is to add operating system detection in your application as SetCurrentProcessExplicitAppUserModelID() is obviously only available in Windows 7:
you may do that from the Java side by checking that System.getProperty("os.name"); returns "Windows 7".
if you build from the little JNI snippet I gave, you can enhance it by dynamically loading the shell32.dll library using LoadLibrary then getting back the SetCurrentProcessExplicitAppUserModelID function pointer using GetProcAddress. If GetProcAddress returns NULL, it means the symbol is not present in shell32 hence it's not Windows 7.
EDIT: JNA Solution.
References:
The JNI book for more JNI examples
Java Native Access (JNA)
There is a Java library providing the new Windows 7 features for Java. It's called J7Goodies by Strix Code. Applications using it can be properly pinned to the Windows 7 taskbar. You can also create your own jump lists, etc.
I have implemented access to the SetCurrentProcessExplicitAppUserModelID method using JNA and it works quite well when used as the MSDN documentation suggests. I've never used the JNA api in the way you have in your code snippet. My implementation follows the typical JNA usage instead.
First the Shell32 interface definition:
interface Shell32 extends StdCallLibrary {
int SetCurrentProcessExplicitAppUserModelID( WString appID );
}
Then using JNA to load Shell32 and call the function:
final Map<String, Object> WIN32API_OPTIONS = new HashMap<String, Object>() {
{
put(Library.OPTION_FUNCTION_MAPPER, W32APIFunctionMapper.UNICODE);
put(Library.OPTION_TYPE_MAPPER, W32APITypeMapper.UNICODE);
}
};
Shell32 shell32 = (Shell32) Native.loadLibrary("shell32", Shell32.class,
WIN32API_OPTIONS);
WString wAppId = new WString( "Vendor.MyJavaApplication" );
shell32.SetCurrentProcessExplicitAppUserModelID( wAppId );
Many of the API's in the last article you mentioned make use of Windows COM which is quite difficult to use directly with JNA. I have had some success creating a custom DLL to call these API's (eg. using the SHGetPropertyStoreForWindow to set a different app ID for a submodule window) which I then use JNA to access at runtime.
Try to use JSmooth. I use always this one. In JSmooth is there an option under Skeleton by Windowed Wrapper called
Lauch java app in exe process
See on this image.
(source: andrels.com)
Also command line arguments can be passed.
I think this can be a solution for you.
Martijn
SetCurrentProcessExplicitAppUserModelID (or SetAppID()) would in fact do what you're trying to do. However, it might be easier to modify your installer to set the AppUserModel.ID property on your shortcut - quoting from the Application User Model ID document mentioned above:
In the System.AppUserModel.ID property of the application's shortcut file. A shortcut (as an IShellLink, CLSID_ShellLink, or a .lnk file) supports properties through IPropertyStore and other property-setting mechanisms used throughout the Shell. This allows the taskbar to identify the proper shortcut to pin and ensures that windows belonging to the process are appropriately associated with that taskbar button.
Note: The System.AppUserModel.ID property should be applied to a shortcut when that shortcut is created. When using the Microsoft Windows Installer (MSI) to install the application, the MsiShortcutProperty table allows the AppUserModelID to be applied to the shortcut when it is created during installation.
The latest jna-platform library now includes JNA bindings for SetCurrentProcessExplicitAppUserModelID:
https://github.com/java-native-access/jna/pull/680
I fixed mine without any ID settings.
There is an option in Launch4J if you are using it and you say you do then...
You can change the header to JNI Gui and then wrap it around the jar with the JRE.
The good thing is that it runs .exe in the process now instead on running javaw.exe with your jar. It probably does it under the hood (not sure).
Also I have noticed also that it takes around 40-50% less CPU resource which is even better!
And the pinning works fine and all that window features are enabled.
I hope it helps to someone as I spent nearly 2 days trying to solve that issue with my undecorated javafx app.