JNI - Async task crashing - java

I'm trying to do the following :
Run Java app on Android
Call native C++ code from that Java app
Create and run a native thread from that natice code
return immediatly while the thread keeps running
Call a java method from native code once the thread is finished
Here is my code :
#include <jni.h>
#include <pthread.h>
#include <unistd.h> /* sleep() */
#include <stdio.h>
#ifdef __cplusplus
extern "C"
{
#endif
JNIEXPORT jstring JNICALL Java_xxx_asynctasktest_MainActivity_runAsyncTask( JNIEnv * env, jobject that, jstring oDummyStr );
#ifdef __cplusplus
}
#endif
JavaVM * g_jvm;
jobject g_obj;
jmethodID g_mid;
void * threadProc( void * pArg )
{
JNIEnv * env = 0;
int res = g_jvm->AttachCurrentThread( & env, NULL );
sleep( 3 );
jobject oStr = (jobject)pArg;
env->CallObjectMethod( g_obj, g_mid, oStr );
env->DeleteGlobalRef( oStr );
env->DeleteGlobalRef( g_obj );
g_jvm->DetachCurrentThread();
return 0;
}
JNIEXPORT jstring JNICALL Java_xxx_asynctasktest_MainActivity_runAsyncTask( JNIEnv * env, jobject that, jstring oDummyStr )
{
env->GetJavaVM( & g_jvm );
g_obj = env->NewGlobalRef( that );
g_mid = env->GetMethodID( env->GetObjectClass( that ), "onTaskFinished", "(Ljava/lang/String;)Ljava/lang/String;" );
if( g_mid == NULL )
{
fprintf( stderr, "No such method" );
}
else
{
pthread_t thread = 0;
pthread_create( & thread, NULL, threadProc, (void *)env->NewGlobalRef( oDummyStr ) );
}
return env->NewStringUTF( "lolol" );
}
My problem is that when i call env->CallObjectMethod( g_obj, g_mid, oStr ); eclipse tries to open Handler.class in the class file editor and says not found and the thread doesn't call the Java method.
What am I doing wrong ? Thank you.
EDIT : Forgot to mention that my Java callback creates a dialog as follows :
AlertDialog.Builder dlgAlert = new AlertDialog.Builder( this );
dlgAlert.setMessage( msgBoxText );
dlgAlert.setTitle( "" );
dlgAlert.setPositiveButton( "OK", null );
dlgAlert.setCancelable( true );
dlgAlert.create().show();
And i'm having a runtime exception somewhere in "Handler" at line 121, source file not available.
EDIT : I'm actually reaching the Java callback, but it crashes when i start creating my dialog.

Related

When I create a stack strcuct variable in jni part and send it by value to native, junk is sent

When I create an in stack variable from a struct in jni cpp and send it to a function in cpp, junk data is sent in the struct.
The strange thing is that if I print some data from the struct before sending it, the program runs correctly
JNIEXPORT void JNICALL Java_dev_eng_gdx_database_DbSquirrel_jniSetBodyLayer(JNIEnv* env, jobject object, jlong address, jshort id, jfloat zDisplacement, jboolean useVecFriction, jfloat friction, jfloat frictionX, jfloat frictionY, jboolean useSlopePhysics, jboolean useSlopeRotation, jboolean rollOnMove, jboolean useVecRoll, jfloat roll, jfloat rollX, jfloat rollY) {
BodyLayer bodyLayer = {.zDisplacement = zDisplacement, .useVecFriction = useVecFriction, .friction = friction, .vecFriction = {frictionX, frictionY},
.useSlopePhysics = useSlopePhysics, .useSlopeRotation = useSlopeRotation,
.rollOnMove = rollOnMove, .useVecRoll = useVecRoll, .roll = roll, .vecRoll = {rollX, rollY} };
//std::cout << "useVecFriction " << bodyLayer.useVecFriction << "\n";
//std::cout << "friction " << friction << "\n";
((SquirrelDb*)address)->setBodyLayer(toC2(id), bodyLayer);
}
If I run the code printing the struct data whith std::cout, the program runs correctly.
If I run the code without printing the struct data it throws this error:
AL lib: (EE) alc_cleanup: 1 device not closed
Thanks ind advance

How to call C# function from java [duplicate]

This question already has answers here:
Calling C# method within a Java program
(2 answers)
Closed 7 years ago.
I need to call C# function from java and to this I have created the following.
I have a create a java header file Authenticator.h , here is the code:
#include <jni.h>
/* Header for class Authenticator */
#ifndef _Included_Authenticator
#define _Included_Authenticator
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: Authenticator
* Method: authenticate
* Signature: (Ljava/lang/String;Ljava/lang/String;)Z
*/
JNIEXPORT jboolean JNICALL Java_Authenticator_authenticate
(JNIEnv *, jobject, jstring, jstring);
#ifdef __cplusplus
}
#endif
#endif
I have then create a C# function that Authenticate
namespace SharpAuthenticator
{
public class Authenticator
{
public bool Authenticate(String username,String password)
{
return username == "user" && password == "login";
}
}
}
Then I am trying to call the C# function from C++(project to create a dll) using the code below;
String^ toString(const char *str)
{
int len = (int)strlen(str);
array<unsigned char>^ a = gcnew array<unsigned char>(len);
int i = 0;
while (i < len)
{
a[i] = str[i];
i++;
}
return Encoding::UTF8->GetString(a);
}
bool authenticate(const char *username, const char *password)
{
SharpAuthenticator::Authenticator::Authenticate(toString(username), toString(password));
}
JNIEXPORT jboolean JNICALL Java_Authenticator_authenticate
(JNIEnv *env, jobject c, jstring name, jstring pass)
{
jboolean result;
jboolean isCopyUsername;
const char * username = env->GetStringUTFChars(name, &isCopyUsername);
jboolean isCopypassword;
const char * password = env->GetStringUTFChars(pass, &isCopypassword);
result = authenticate(username, password);
env->ReleaseStringUTFChars(name, username);
env->ReleaseStringUTFChars(pass, password);
return result;
}
And finnally create a dll that i need to call from java. The dll is created and I load it well in java but I get this error log in java. What could I be Missing.
#
# A fatal error has been detected by the Java Runtime Environment:
#
# Internal Error (0xe0434352), pid=9708, tid=7756
#
# JRE version: 7.0-b147
# Java VM: Java HotSpot(TM) Client VM (21.0-b17 mixed mode, sharing windows-x86 )
# Problematic frame:
# C [KERNELBASE.dll+0x812f]
#
# Failed to write core dump. Minidumps are not enabled by default on client versions of Windows
First of all lets create a C# file like this:
using System;
public class Test{
public Test(){}
public String ping(){
return "C# is here.";
}
}
Then compile this with command below:
csc.exe /target:module Test.cs
You can find csc.exe in install path of .NET framework. After that create java file:
public class Test{
public native String ping();
public static void main(String[] args){
System.load("/path/to/dll");
System.out.println("Java is running.");
Test t = new Test();
System.out.println("Trying to catch C# " + r.ping());
}
}
javac Test.java This generates a Test.class.
javah -jni Test This generates a Test.h file which will be included in
C++ code.
After that we need to create our C++ file:
#include "stdafx.h"
#include "JAVA/Test.h"
#include "MCPP/Test.h"
#pragma once
#using <mscorlib.dll>
#using "Test.netmodule"
JNIEXPORT jstring JNICALL Java_Test_ping(JNIEnv *env, jobject obj){
Test^ t = gcnew Test();
String^ ping = t->ping();
char* str = static_cast<char*>((System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(ping)).ToPointer());
char cap[128];
strcpy_s(cap, str);
return env->NewStringUTF(cap);
}
Finally:
c:\>java Test
I hope this helps you. A basic example to use function C# in Java.
Sources:
https://www.quora.com/How-common-is-the-problem-of-calling-C-methods-from-Java-Do-many-developers-come-across-such-necessity

JNI JVM Invocation Classpath

I am writing a small C program using Cygwin that launches a Java Virtual Machine (libraries I am using require POSIX environment). So far, I have been able to get it to work as long as I place all of my classes in the same folder as the executable. However, I want to specify an actual JAR file that contains the application I want to run. This does not seem to work though, FindClass simply returns a null. I've narrowed it down to a problem with the classpath setting, like I said, because I can extract my jar file in the same directory as the executable and it will work. Here is a subset of my code:
I've loosely been following this guide: http://www.inonit.com/cygwin/jni/invocationApi/
int main( int argc, char *argv[] )
{
void* jvmDllHandle;
JNIEnv* jenv;
JavaVM* jvm;
JavaVMInitArgs args;
JavaVMOption options[1];
jclass cls;
jmethodID mainMethod;
jobjectArray appArgs;
jstring arg0;
assert( cygwin_internal( CW_SYNC_WINENV ) != 1UL );
jvmDllHandle = LoadLibrary( "c:\\Path\\To\\Application\\jre\\bin\\server\\jvm.dll" );
createJavaVM = dlsym( jvmDllHandle, "JNI_CreateJavaVM" );
args.version = JNI_VERSION_1_6;
args.nOptions = 1;
options[0].optionString = "-Djava.class.path=c:\\Path\\To\\Application\\TheJarFile.jar";
args.options = options;
args.ignoreUnrecognized = JNI_FALSE;
createJavaVM( &jvm, (void **) &jenv, &args );
cls = (*jenv)->FindClass( jenv, "some/package/MainClass" );
assert( cls != NULL ); // This fails.
/// Omitted...
return 0;
}
Tried using -classpath and -cp
int main( int argc, char *argv[] )
{
void* jvmDllHandle;
JNIEnv* jenv;
JavaVM* jvm;
JavaVMInitArgs args;
JavaVMOption options[1];
jclass cls;
jmethodID mainMethod;
jobjectArray appArgs;
jstring arg0;
assert( cygwin_internal( CW_SYNC_WINENV ) != 1UL );
jvmDllHandle = LoadLibrary( "c:\\Path\\To\\Application\\jre\\bin\\server\\jvm.dll" );
createJavaVM = dlsym( jvmDllHandle, "JNI_CreateJavaVM" );
args.version = JNI_VERSION_1_6;
args.nOptions = 1;
options[0].optionString = "-classpath c:\\Path\\To\\Application\\TheJarFile.jar";
args.options = options;
args.ignoreUnrecognized = JNI_FALSE;
createJavaVM( &jvm, (void **) &jenv, &args );
cls = (*jenv)->FindClass( jenv, "some/package/MainClass" );
assert( cls != NULL ); // This fails.
/// Omitted...
return 0;
}
How am I specifying the classpath incorrectly?
On x86-64, the Oracle Windows JDK headers define jint as long. This is 32 bits with Microsoft compilers (which the Oracle JDK is written for) but 64 bits with Cygwin gcc. Since JavaVMInitArgs contains some fields of this type, its binary layout is changed by this discrepancy.
I worked around this by providing a local jni.h header:
#include "stdint.h"
#define __int64 int64_t
#define long int32_t
#include "jni_md.h"
#undef long
#include_next "jni.h"
I'm including only jni_md.h within the scope of the long redefinition because it doesn't include any other headers, whereas jni.h includes a couple of standard headers which we would not want to be affected as well.
To ensure this is always included ahead of the Oracle header, use the -I compiler option to add its directory to the #include path.

Loading dynamic libraries using JNI and C++ with linux

Recently I'm learning JNI to execute C code. Of course I did basic examples that were on the web. Now Im trying to load a C library that makes dynamic library loading (dlopen). But Im fighthing with an error. Im posting my Java code, C ++ code and the error.
My Java Class
/**
*
* #author glassfish
*/
public class MediationJniWeb {
public String library ;
static {
System.loadLibrary("-core-web");
}
/**
*
* #param library name of mediation core library [32]
* #param method name of method to be executed [128]
* #param parameters parameters of method [10240]
* [partype1,value1,...,valuen]...[partypen,value1,..,valuen]
* #return
*/
private native String execute();
public static void main(String args[]) {
//new MediationJniWeb().callToFunction(null, null, null) ;
MediationJniWeb jniWeb = new MediationJniWeb();
jniWeb.library = "libtest.so" ;
System.out.println(jniWeb.execute());
}
}
I generate .class file with
javac MediationJniWeb
and I generate .h File with
javah -jni MediationJniWeb
my MediationJniWeb.h file is
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class MediationJniWeb */
#ifndef _Included_MediationJniWeb
#define _Included_MediationJniWeb
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: MediationJniWeb
* Method: execute
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_MediationJniWeb_execute
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
and my MediationJniWEb.cpp file
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#include <dlfcn.h>
#include <iostream>
#include "MediationJniWeb.h"
using namespace std ;
typedef void (*test)(string);
/*
* Class: MediationJniWeb
* Method: execute
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_MediationJniWeb_execute
(JNIEnv * env, jobject obj){
const char * str_library ;
jfieldID fid_library ;
jstring jstr_library ;
jboolean isCopy ;
jclass cls = env->GetObjectClass( obj) ;
fid_library = env->GetFieldID( cls,"library", "Ljava/lang/String;");
jstr_library = ( jstring )env->GetObjectField( obj,fid_library);
str_library = env->GetStringUTFChars( jstr_library, &isCopy) ;
void* handle = dlopen(str_library, RTLD_NOW); // open libtest.so
if ( 0 == handle ) {
cout << "failed to load 'libtest.so'. " << dlerror () <<endl;
exit(1);
}
test t = (test)dlsym(handle, "_Z8testfuncSs"); // run default method
if ( 0 == t ) {
cout << "failed to load 'testfunc()'." << endl;
exit(1);
}
t("Hello, World!");
dlclose(handle);
/*
*/
return env->NewStringUTF( str_library); // I just return library name just for fun
}
}
I compile with
g++ -shared -fpic -I//include/ -I//include/linux/ MediationJniWeb.cpp -o lib-core-web.so MediationJniWeb.cpp -ldl
this generate lib-core-web.so file. Then I copy this to $HOME/lib and I configure
LD_LIBRARY_PATH=$HOME/lib
Now I create my library libtest.so that is going to be executed by lib-core-web.so
I create file for shared libray mylib.cpp
#include <iostream>
#include <string>
using namespace std;
void testfunc(string s)
{
cout << s << endl;
}
I compile this that is going to work as a shared library with
g++ -shared -fpic -o libtest.so mylib.cpp
This command generates libtest.so file.. and also, I copy it to $HOME/lib
That's all I do to call from JNI to a C++ library to load a dynamic library.
when I execute MediationJniWeb java class I'm having this error
failed to load. libtest.so: cannot open shared object file: No such
file or directory
What do I have to do with libtest.so ?? where do I have to put it ?
I have on mind that configuring only LD_LIBRARY_PATH variable with right path, JVM should know where to find all needed libraries to be loaded.
Please help with your comments and let me know where my mistakes are.
Thanks in advance !
A simple thing I have done
instead of
jniWeb.library = "libtest.so"
library parameter to be loaded I declared
jniWeb.library = "/home/myuser/lib/libtest.so"
And it worked !

How to convert FormatMessage output to an Java Exception message in JNI under windows?

I am trying to convert Windows error to an java IOException with the fuciont below:
void ThrowIOException(JNIEnv * env, LPCTSTR lpszFunction, DWORD dw)
{
LPVOID lpMsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
// MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
LPVOID lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf)+lstrlen((LPCTSTR)lpszFunction)+40)*sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf),
TEXT("%s failed with error %d: %s"),
lpszFunction, dw, lpMsgBuf);
jclass Exception = env->FindClass("java/io/IOException");
if(env->ThrowNew(Exception, (const char *)lpDisplayBuf)){
printf("Can't throw IOException: %s\n", lpDisplayBuf);
}
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
}
I am using Unicode in my VC project. The IOException throws successfully, but the message in 'lpDisplayBuf' is not displayed properly.
I am aware of that casting lpDisplayBuf to "const char *" might be wrong, but I don't know how to correct it.
If your VC project is setup as UNICODE then you need to convert the string returned from FormatMessage to ANSI as in the following example (please, note that I put no error checking at all)
// This is the main DLL file.
#include "stdafx.h"
#include "jni.h"
#include
extern "C"
{
JNIEXPORT jstring JNICALL Java_jnihellowworld_sayIt(JNIEnv *env, jobject obj)
{
jclass excClass;
LPVOID lpMsgBuf;
char buffer[1000];
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
2,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
excClass = env->FindClass("java/io/IOException");
::WideCharToMultiByte(CP_ACP,
0,
(LPCWSTR) lpMsgBuf,
-1,
buffer,
1000,
NULL,
NULL);
LocalFree(lpMsgBuf);
env->ThrowNew(excClass, buffer);
return env->NewStringUTF("never reached");
}
}
Hope this helps.

Categories